DO NOT MERGE: Merge Oreo MR1 into master
Exempt-From-Owner-Approval: Changes already landed internally
Change-Id: Ie577b7dd4c610be47d2c78cf65e9f85a712226f8
diff --git a/Android.bp b/Android.bp
index b578f16..70f8382 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,7 +18,6 @@
name: "chre_client",
vendor: true,
export_include_dirs: [
- "external/flatbuffers/include",
"host/common/include",
"platform/shared/include",
"util/include",
@@ -28,6 +27,8 @@
"host/common/host_protocol_host.cc",
"platform/shared/host_protocol_common.cc",
],
+ header_libs: ["chre_flatbuffers"],
+ export_header_lib_headers: ["chre_flatbuffers"],
shared_libs: [
"libcutils",
"liblog",
@@ -76,3 +77,23 @@
static_libs: ["chre_client"],
tags: ["optional"],
}
+
+cc_library_headers {
+ name: "chre_api",
+ vendor: true,
+ export_include_dirs: [
+ "chre_api/include/chre_api",
+ ]
+}
+
+cc_library_headers {
+ name: "chre_flatbuffers",
+ vendor: true,
+ export_include_dirs: [
+ "external/flatbuffers/include",
+ ],
+}
+
+subdirs = [
+ "apps/wifi_offload",
+]
diff --git a/Makefile b/Makefile
index 386094f..1a9bdd5 100644
--- a/Makefile
+++ b/Makefile
@@ -54,8 +54,11 @@
HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/platform/rtld/inc
HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/api
HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/common/idl/inc
+HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/common/inc
+HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/common/smr/inc
HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/common/util/mathtools/inc
HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/goog/api
+HEXAGON_CFLAGS += -I$(SLPI_PREFIX)/Sensors/pm/inc
# Makefile Includes ############################################################
@@ -80,5 +83,6 @@
# google_cm4_nanohub as Nanohub itself is a CHRE implementation.
include $(CHRE_PREFIX)/build/variant/google_hexagonv60_slpi.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv62_slpi.mk
+include $(CHRE_PREFIX)/build/variant/google_hexagonv62_slpi-uimg.mk
include $(CHRE_PREFIX)/build/variant/google_x86_linux.mk
include $(CHRE_PREFIX)/build/variant/google_x86_googletest.mk
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..6b4fac5
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,219 @@
+
+ Copyright (c) 2016-2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
+Support for running dynamic modules in uImage for SLPI:
+
+Copyright (c) 2017, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index 8229adc..c0ba2f0 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,12 @@
#### Linux Build/Run
+The simulator uses TCLAP for command-line argument parsing. It must be available
+on the system path of the simulator. Here is an example of how to install it for
+Ubuntu:
+
+ sudo apt-get install libtclap-dev
+
The build target for x86 linux is ``google_x86_linux``. You can build/run the
simulator with the following command:
diff --git a/apps/chqts/README.md b/apps/chqts/README.md
new file mode 100644
index 0000000..9266f7d
--- /dev/null
+++ b/apps/chqts/README.md
@@ -0,0 +1,4 @@
+The subdirectories here contain the source code for nanoapps used for Context
+Hub Qualification Test Suite (CHQTS). CHQTS is a test suite used to validate
+the proper functionality of a CHRE implementation as specified in the API
+methods.
diff --git a/apps/chqts/build/busy_startup/Makefile b/apps/chqts/build/busy_startup/Makefile
new file mode 100644
index 0000000..64e8ff9
--- /dev/null
+++ b/apps/chqts/build/busy_startup/Makefile
@@ -0,0 +1,8 @@
+NANOAPP_NAME := busy_startup
+NANOAPP_SRC_FILES := busy_startup.cc
+
+NANOAPP_ID := 0x476f6f6754fffffc
+NANOAPP_VERSION := 0
+NANOAPP_NAME_STRING := \"CHQTS\ Busy\ Startup\"
+
+include ../shared_make.mk
diff --git a/apps/chqts/build/busy_startup/header b/apps/chqts/build/busy_startup/header
new file mode 100644
index 0000000..dd238c9
--- /dev/null
+++ b/apps/chqts/build/busy_startup/header
Binary files differ
diff --git a/apps/chqts/build/do_nothing/Makefile b/apps/chqts/build/do_nothing/Makefile
new file mode 100644
index 0000000..39f0417
--- /dev/null
+++ b/apps/chqts/build/do_nothing/Makefile
@@ -0,0 +1,8 @@
+NANOAPP_NAME := do_nothing
+NANOAPP_SRC_FILES := do_nothing.cc
+
+NANOAPP_ID := 0x476f6f6754fffffd
+NANOAPP_VERSION := 0
+NANOAPP_NAME_STRING := \"CHQTS\ Do\ Nothing\"
+
+include ../shared_make.mk
diff --git a/apps/chqts/build/do_nothing/header b/apps/chqts/build/do_nothing/header
new file mode 100644
index 0000000..f44a12d
--- /dev/null
+++ b/apps/chqts/build/do_nothing/header
Binary files differ
diff --git a/apps/chqts/build/fail_startup/Makefile b/apps/chqts/build/fail_startup/Makefile
new file mode 100644
index 0000000..8abfb0e
--- /dev/null
+++ b/apps/chqts/build/fail_startup/Makefile
@@ -0,0 +1,8 @@
+NANOAPP_NAME := fail_startup
+NANOAPP_SRC_FILES := fail_startup.cc
+
+NANOAPP_ID := 0x476f6f6754fffffe
+NANOAPP_VERSION := 0
+NANOAPP_NAME_STRING := \"CHQTS\ Fail\ Startup\"
+
+include ../shared_make.mk
diff --git a/apps/chqts/build/fail_startup/header b/apps/chqts/build/fail_startup/header
new file mode 100644
index 0000000..91fcc95
--- /dev/null
+++ b/apps/chqts/build/fail_startup/header
Binary files differ
diff --git a/apps/chqts/build/general_test/Makefile b/apps/chqts/build/general_test/Makefile
new file mode 100644
index 0000000..cf0f9d7
--- /dev/null
+++ b/apps/chqts/build/general_test/Makefile
@@ -0,0 +1,10 @@
+include ../general_test_sources.mk
+
+NANOAPP_NAME := general_test
+NANOAPP_SRC_FILES := $(GENERAL_TEST_SRC_FILES)
+
+NANOAPP_ID := 0x476f6f6754000000
+NANOAPP_VERSION := 0
+NANOAPP_NAME_STRING := \"CHQTS\ General\ Test\"
+
+include ../shared_make.mk
diff --git a/apps/chqts/build/general_test/header b/apps/chqts/build/general_test/header
new file mode 100644
index 0000000..32d6d61
--- /dev/null
+++ b/apps/chqts/build/general_test/header
Binary files differ
diff --git a/apps/chqts/build/general_test2/Makefile b/apps/chqts/build/general_test2/Makefile
new file mode 100644
index 0000000..498c47d
--- /dev/null
+++ b/apps/chqts/build/general_test2/Makefile
@@ -0,0 +1,11 @@
+include ../general_test_sources.mk
+
+NANOAPP_NAME := general_test2
+NANOAPP_DIR_NAME := general_test
+NANOAPP_SRC_FILES := $(GENERAL_TEST_SRC_FILES)
+
+NANOAPP_ID := 0x476f6f6754000001
+NANOAPP_VERSION := 0
+NANOAPP_NAME_STRING := \"CHQTS\ General\ Test\"
+
+include ../shared_make.mk
diff --git a/apps/chqts/build/general_test2/header b/apps/chqts/build/general_test2/header
new file mode 100644
index 0000000..aed6d1c
--- /dev/null
+++ b/apps/chqts/build/general_test2/header
Binary files differ
diff --git a/apps/chqts/build/general_test_sources.mk b/apps/chqts/build/general_test_sources.mk
new file mode 100644
index 0000000..91a30df
--- /dev/null
+++ b/apps/chqts/build/general_test_sources.mk
@@ -0,0 +1,40 @@
+# Since we add source files to this app somewhat regularly, we prefer
+# a central definition shared across all target platforms.
+
+GENERAL_TEST_SRC_FILES = \
+ app.cc \
+ basic_sensor_test_base.cc \
+ basic_sensor_tests.cc \
+ cell_info_base.cc \
+ cell_info_cdma.cc \
+ cell_info_gsm.cc \
+ cell_info_lte.cc \
+ cell_info_tdscdma.cc \
+ cell_info_wcdma.cc \
+ estimated_host_time_test.cc \
+ event_between_apps_test.cc \
+ get_time_test.cc \
+ gnss_capabilities_test.cc \
+ heap_alloc_stress_test.cc \
+ heap_exhaustion_stability_test.cc \
+ hello_world_test.cc \
+ logging_sanity_test.cc \
+ nanoapp_info.cc \
+ nanoapp_info_by_app_id_test.cc \
+ nanoapp_info_by_instance_id_test.cc \
+ nanoapp_info_events_test_observer.cc \
+ nanoapp_info_events_test_performer.cc \
+ running_info.cc \
+ send_event_test.cc \
+ send_event_stress_test.cc \
+ send_message_to_host_test.cc \
+ sensor_info_test.cc \
+ simple_heap_alloc_test.cc \
+ test.cc \
+ timer_cancel_test.cc \
+ timer_set_test.cc \
+ timer_stress_test.cc \
+ version_sanity_test.cc \
+ wifi_capabilities_test.cc \
+ wwan_capabilities_test.cc \
+ wwan_cell_info_test.cc
diff --git a/apps/chqts/build/shared_make.mk b/apps/chqts/build/shared_make.mk
new file mode 100644
index 0000000..37a8388
--- /dev/null
+++ b/apps/chqts/build/shared_make.mk
@@ -0,0 +1,38 @@
+ifndef NANOAPP_NAME
+$(error NANOAPP_NAME unset)
+endif
+
+ifndef NANOAPP_SRC_FILES
+$(error NANOAPP_SRC_FILES unset)
+endif
+
+ifndef ANDROID_BUILD_TOP
+$(error Must set Android build environment first)
+endif
+
+NANOAPP_DIR_NAME ?= $(NANOAPP_NAME)
+
+# This path is actually relative to one level deeper as this file
+# gets included from Makefile of each test subdirectory
+NANOAPP_SRC_PATH = ../../src
+
+SHARED_LIB_FILES = abort.cc \
+ dumb_allocator.cc \
+ nano_endian.cc \
+ nano_string.cc \
+ send_message.cc
+
+COMMON_SRCS += \
+ $(addprefix $(NANOAPP_SRC_PATH)/$(NANOAPP_DIR_NAME)/, $(NANOAPP_SRC_FILES)) \
+ $(addprefix $(NANOAPP_SRC_PATH)/shared/, $(SHARED_LIB_FILES))
+
+COMMON_CFLAGS += -DCHRE_NO_ENDIAN_H \
+ -D__LITTLE_ENDIAN=1 \
+ -D__BYTE_ORDER=1 \
+ -D__BIG_ENDIAN=2
+
+COMMON_CFLAGS += -I$(NANOAPP_SRC_PATH)
+
+OPT_LEVEL=2
+
+include ${ANDROID_BUILD_TOP}/system/chre/build/nanoapp/app.mk
diff --git a/apps/chqts/src/busy_startup/busy_startup.cc b/apps/chqts/src/busy_startup/busy_startup.cc
new file mode 100644
index 0000000..cf2c05e
--- /dev/null
+++ b/apps/chqts/src/busy_startup/busy_startup.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+/**
+ * Nanoapp which performs a number of operations within nanoappStart().
+ *
+ * This nanoapp is to confirm a number of CHRE methods can be invoked from
+ * within nanoappStart(). There are other tests which test each of these
+ * CHRE methods more in depth. We're just doing a sanity check that calling
+ * from nanoappStart() works at all.
+ *
+ * Specifically, we're testing:
+ * o chreHeapAlloc() and chreHeapFree()
+ * o chreGetInstanceId()
+ * o chreSendEvent() [*]
+ * o chreTimerSet() [*]
+ * o chreSensorFindDefault() and chreSensorConfigure() [*]
+ * o chreSendMessageToHost() [**]
+ *
+ * [*] These require nanoappHandleEvent() to be called successfully in order
+ * to confirm.
+ * [**] This is confirmed by the host receiving this message.
+ *
+ * This isn't a "general" test, so it doesn't have a standard communication
+ * protocol. Notably, the Host doesn't send any messages to this nanoapp.
+ *
+ * Protocol:
+ * Nanoapp to Host: kContinue
+ * Nanoapp to Host: kSuccess
+ */
+
+#include <cinttypes>
+
+#include <chre.h>
+
+#include <shared/send_message.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendMessageToHost;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+
+static bool gInMethod = false;
+static uint32_t gInstanceId;
+static uint32_t gTimerId;
+static uint32_t gSensorHandle;
+
+constexpr size_t kSelfEventStage = 0;
+constexpr size_t kTimerStage = 1;
+constexpr size_t kSensorStage = 2;
+constexpr size_t kStageCount = 3;
+
+constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
+static uint32_t gFinishedBitmask = 0;
+
+constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
+
+static void markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage", &stage);
+ }
+ gFinishedBitmask |= finishedBit;
+ if (gFinishedBitmask == kAllFinished) {
+ sendSuccessToHost();
+ }
+}
+
+static void checkSelfEvent(uint16_t eventType, const uint32_t *eventData) {
+ if (eventType != kEventType) {
+ uint32_t e = eventType;
+ sendFatalFailureToHost("Event from self, bad event type:", &e);
+ }
+ if (eventData == nullptr) {
+ sendFatalFailureToHost("Event from self, null data");
+ }
+ if (*eventData != gInstanceId) {
+ sendFatalFailureToHost("Event from self, bad data:", eventData);
+ }
+ markSuccess(kSelfEventStage);
+}
+
+static void checkTimerEvent(const uint32_t *eventData) {
+ if (eventData == nullptr) {
+ sendFatalFailureToHost("TimerEvent, null data");
+ }
+ if (*eventData != gInstanceId) {
+ sendFatalFailureToHost("TimerEvent, bad data:", eventData);
+ }
+ markSuccess(kTimerStage);
+}
+
+static void checkSensorEvent(const void *eventData) {
+ const chreSensorDataHeader* header =
+ static_cast<const chreSensorDataHeader *>(eventData);
+ if (header == nullptr) {
+ sendFatalFailureToHost("sensorEvent, null data");
+ }
+ if (header->sensorHandle != gSensorHandle) {
+ sendFatalFailureToHost("sensorEvent for wrong handle",
+ &header->sensorHandle);
+ }
+ if (header->readingCount == 0) {
+ sendFatalFailureToHost("sensorEvent has readingCount of 0");
+ }
+ if ((header->reserved[0] != 0) || (header->reserved[1] != 0)) {
+ sendFatalFailureToHost("sensorEvent has non-zero reserved bytes");
+ }
+ markSuccess(kSensorStage);
+}
+
+extern "C" void nanoappHandleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ if (gInMethod) {
+ sendFatalFailureToHost("CHRE reentered nanoapp");
+ }
+ gInMethod = true;
+ const uint32_t *intData = static_cast<const uint32_t *>(eventData);
+ if (senderInstanceId == gInstanceId) {
+ checkSelfEvent(eventType, intData);
+
+ } else if (senderInstanceId == CHRE_INSTANCE_ID) {
+ if (eventType == CHRE_EVENT_TIMER) {
+ checkTimerEvent(intData);
+ } else if (eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_DATA) {
+ checkSensorEvent(eventData);
+ } else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE) {
+ // This could have been generated when we configured the
+ // sensor. We just ignore it.
+ } else {
+ uint32_t e = eventType;
+ sendFatalFailureToHost("Unexpected event from CHRE:", &e);
+ }
+ } else {
+ sendFatalFailureToHost("Unexpected senderInstanceId",
+ &senderInstanceId);
+ }
+ gInMethod = false;
+}
+
+extern "C" bool nanoappStart(void) {
+ gInMethod = true;
+ void *ptr = chreHeapAlloc(15);
+ if (ptr == nullptr) {
+ // TODO(b/32326854): We're not able to send messages from
+ // nanoappStart(), so we just use chreLog() here, and make
+ // the user look through the logs to determine why this failed.
+ chreLog(CHRE_LOG_ERROR, "Unable to malloc in start");
+ return false;
+ }
+ gInstanceId = chreGetInstanceId();
+ if (gInstanceId == CHRE_INSTANCE_ID) {
+ chreLog(CHRE_LOG_ERROR, "Got bad instance ID in start");
+ return false;
+ }
+
+ // Send an event to ourself.
+ if (!chreSendEvent(kEventType, &gInstanceId, nullptr, gInstanceId)) {
+ chreLog(CHRE_LOG_ERROR, "Failed chreSendEvent in start");
+ return false;
+ }
+
+ // One shot timer that should trigger very quickly.
+ gTimerId = chreTimerSet(1, &gInstanceId, true);
+ if (gTimerId == CHRE_TIMER_INVALID) {
+ chreLog(CHRE_LOG_ERROR, "Failed chreTimerSet in start");
+ return false;
+ }
+
+ // We don't have a way to confirm the 'free' worked, we'll just look
+ // to see that we didn't crash. We intentionally move this 'free' to
+ // be not immediately after the 'alloc', and still before we're done
+ // calling other methods.
+ chreHeapFree(ptr);
+
+ // Confirm we can find and configure a sensor.
+ if (!chreSensorFindDefault(CHRE_SENSOR_TYPE_ACCELEROMETER,
+ &gSensorHandle)) {
+ chreLog(CHRE_LOG_ERROR, "Failed sensorFindDefault in start");
+ return false;
+ }
+ if (!chreSensorConfigure(gSensorHandle,
+ CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS,
+ CHRE_SENSOR_INTERVAL_DEFAULT,
+ CHRE_SENSOR_LATENCY_ASAP)) {
+ chreLog(CHRE_LOG_ERROR, "Failed sensorConfigure in start");
+ return false;
+ }
+
+ // TODO(b/32326854): Confirm we can send a message to the host.
+
+ gInMethod = false;
+ return true;
+}
+
+extern "C" void nanoappEnd(void) {
+ if (!chreSensorConfigureModeOnly(gSensorHandle,
+ CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
+ sendFatalFailureToHost("Unable to configure sensor mode to DONE");
+ }
+
+ if (gInMethod) {
+ // This message won't be noticed by the host; but hopefully the
+ // fatal failure prevents a clean unload of the app and fails the test.
+ sendFatalFailureToHost("nanoappEnd called in reentrant manner");
+ }
+}
diff --git a/apps/chqts/src/do_nothing/do_nothing.cc b/apps/chqts/src/do_nothing/do_nothing.cc
new file mode 100644
index 0000000..cbe68c5
--- /dev/null
+++ b/apps/chqts/src/do_nothing/do_nothing.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The most trivial possible nanoapp.
+ *
+ * This successfully loads, and does nothing else. If a CHRE implementation
+ * can't load and unload this, there's no point in testing anything else yet.
+ */
+
+#include <cstdint>
+
+#include <chre.h>
+
+extern "C" void nanoappHandleEvent(uint32_t /* senderInstanceId */,
+ uint16_t /* eventType */,
+ const void* /* eventData */) {
+ // Intentionally do nothing.
+}
+
+extern "C" bool nanoappStart(void) {
+ return true;
+}
+
+extern "C" void nanoappEnd(void) {
+ // Intentionally do nothing.
+}
diff --git a/apps/chqts/src/fail_startup/fail_startup.cc b/apps/chqts/src/fail_startup/fail_startup.cc
new file mode 100644
index 0000000..168f90b
--- /dev/null
+++ b/apps/chqts/src/fail_startup/fail_startup.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Trivial nanoapp which fails on startup.
+ *
+ * Non-trivial nanoapp tests may indicate early failure by failing the
+ * nanoappStart() method. This test assures that those failures are
+ * properly reported by the CHRE, so we know our other test failures will
+ * be caught.
+ */
+
+#include <cstdint>
+
+#include <chre.h>
+
+#include <shared/abort.h>
+
+extern "C" void nanoappHandleEvent(uint32_t /* senderInstanceId */,
+ uint16_t /* eventType */,
+ const void* /* eventData */) {
+ // Intentionally do nothing.
+}
+
+extern "C" bool nanoappStart(void) {
+ // Return failure
+ return false;
+}
+
+extern "C" void nanoappEnd(void) {
+ // It is an error for the CHRE to call this method.
+ nanoapp_testing::abort(nanoapp_testing::AbortBlame::kChreInNanoappEnd);
+}
diff --git a/apps/chqts/src/general_test/app.cc b/apps/chqts/src/general_test/app.cc
new file mode 100644
index 0000000..743bf95
--- /dev/null
+++ b/apps/chqts/src/general_test/app.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstddef> // max_align_t
+#include <cstdint>
+#include <new> // placement new
+
+#include <chre.h>
+
+#include <general_test/basic_sensor_tests.h>
+#include <general_test/estimated_host_time_test.h>
+#include <general_test/event_between_apps_test.h>
+#include <general_test/get_time_test.h>
+#include <general_test/gnss_capabilities_test.h>
+#include <general_test/heap_alloc_stress_test.h>
+#include <general_test/heap_exhaustion_stability_test.h>
+#include <general_test/hello_world_test.h>
+#include <general_test/logging_sanity_test.h>
+#include <general_test/nanoapp_info_by_app_id_test.h>
+#include <general_test/nanoapp_info_by_instance_id_test.h>
+#include <general_test/nanoapp_info_events_test_observer.h>
+#include <general_test/nanoapp_info_events_test_performer.h>
+#include <general_test/send_event_test.h>
+#include <general_test/send_event_stress_test.h>
+#include <general_test/send_message_to_host_test.h>
+#include <general_test/sensor_info_test.h>
+#include <general_test/simple_heap_alloc_test.h>
+#include <general_test/test.h>
+#include <general_test/test_names.h>
+#include <general_test/timer_cancel_test.h>
+#include <general_test/timer_set_test.h>
+#include <general_test/timer_stress_test.h>
+#include <general_test/version_sanity_test.h>
+#include <general_test/wifi_capabilities_test.h>
+#include <general_test/wwan_capabilities_test.h>
+#include <general_test/wwan_cell_info_test.h>
+#include <shared/abort.h>
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+#include <shared/send_message.h>
+
+using nanoapp_testing::AbortBlame;
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+
+
+namespace general_test {
+
+// The size of this array is checked at compile time by the static_assert
+// in getNew().
+alignas(alignof(max_align_t)) static uint8_t gGetNewBackingMemory[128];
+
+template<typename N>
+static N *getNew() {
+ // We intentionally avoid using chreHeapAlloc() to reduce dependencies
+ // for our tests, especially things like HelloWorld. This obviously
+ // cannot be called more than once, but our usage doesn't require it.
+ static_assert(sizeof(gGetNewBackingMemory) >= sizeof(N),
+ "getNew() backing memory is undersized");
+
+ return new(gGetNewBackingMemory) N();
+}
+
+// TODO(b/32114261): Remove this variable.
+bool gUseNycMessageHack = true;
+
+class App {
+ public:
+ App() : mConstructionCookie(kConstructed),
+ mCurrentTest(nullptr) {}
+
+ ~App() {
+ // Yes, it's very odd to actively set a value in our destructor.
+ // However, since we're making a static instance of this class,
+ // the space for this class will stick around (unlike heap memory
+ // which might get reused), so we can still use this to perform
+ // some testing.
+ mConstructionCookie = kDestructed;
+ }
+
+ bool wasConstructed() const { return mConstructionCookie == kConstructed; }
+ bool wasDestructed() const { return mConstructionCookie == kDestructed; }
+
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData);
+
+ void createTest(const void *eventData);
+ void freeTest();
+
+ private:
+ uint32_t mConstructionCookie;
+ Test *mCurrentTest;
+
+ static constexpr uint32_t kConstructed = UINT32_C(0x51501984);
+ static constexpr uint32_t kDestructed = UINT32_C(0x19845150);
+
+ // TODO(b/32114261): Remove this method.
+ chreMessageFromHostData
+ adjustHostMessageForNYC(const chreMessageFromHostData *data);
+};
+
+
+// In the NYC version of the CHRE, the "reservedMessageType" isn't
+// assured to be sent correctly from the host. But we want our
+// tests to be written using this field (it's cleaner). So in NYC
+// the host prefixes this value in the first four bytes of 'message',
+// and here we reconstruct the message to be correct.
+// TODO(b/32114261): Remove this method.
+chreMessageFromHostData
+App::adjustHostMessageForNYC(const chreMessageFromHostData *data) {
+ if (!gUseNycMessageHack) {
+ return *data;
+ }
+ chreMessageFromHostData ret;
+
+ if (data->messageSize < sizeof(uint32_t)) {
+ sendFatalFailureToHost("Undersized message in adjustHostMessageForNYC");
+ }
+ const uint8_t *messageBytes = static_cast<const uint8_t*>(data->message);
+ nanoapp_testing::memcpy(&(ret.reservedMessageType), messageBytes,
+ sizeof(ret.reservedMessageType));
+ nanoapp_testing::littleEndianToHost(&(ret.reservedMessageType));
+ ret.messageSize = data->messageSize - sizeof(ret.reservedMessageType);
+ ret.message = messageBytes + sizeof(ret.reservedMessageType);
+ return ret;
+}
+
+
+void App::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) {
+ // TODO: When we get an API that fixes the reservedMessageType,
+ // then we should only use our adjustedData hack on APIs
+ // prior to it being fixed. Eventually, we should remove
+ // this altogether.
+ chreMessageFromHostData adjustedData;
+ if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
+ auto data = static_cast<const chreMessageFromHostData *>(eventData);
+ adjustedData = adjustHostMessageForNYC(data);
+ eventData = &adjustedData;
+ }
+
+ if (mCurrentTest != nullptr) {
+ // Our test is in progress, so let it take control.
+ mCurrentTest->testHandleEvent(senderInstanceId, eventType, eventData);
+ return;
+ }
+
+ // No test in progress, so we expect this message to be the host telling
+ // us which test to run. We fail if it's anything else.
+ if (eventType != CHRE_EVENT_MESSAGE_FROM_HOST) {
+ uint32_t localEventType = eventType;
+ sendFatalFailureToHost(
+ "Unexpected event type with no established test:",
+ &localEventType);
+ }
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost(
+ "Got MESSAGE_FROM_HOST not from CHRE_INSTANCE_ID:",
+ &senderInstanceId);
+ }
+ createTest(eventData);
+}
+
+void App::createTest(const void *eventData) {
+ if (mCurrentTest != nullptr) {
+ sendInternalFailureToHost(
+ "Got to createTest() with non-null mCurrentTest");
+ }
+
+ auto data = static_cast<const chreMessageFromHostData *>(eventData);
+ switch (static_cast<TestNames>(data->reservedMessageType)) {
+ using namespace general_test;
+
+#define CASE(testName, className) \
+ case TestNames::testName: \
+ mCurrentTest = getNew<className>(); \
+ break;
+
+ CASE(kHelloWorld, HelloWorldTest);
+ CASE(kSimpleHeapAlloc, SimpleHeapAllocTest);
+ CASE(kHeapAllocStress, HeapAllocStressTest);
+ CASE(kGetTime, GetTimeTest);
+ CASE(kEventBetweenApps0, EventBetweenApps0);
+ CASE(kEventBetweenApps1, EventBetweenApps1);
+ CASE(kSendEvent, SendEventTest);
+ CASE(kBasicAccelerometer, BasicAccelerometerTest);
+ CASE(kBasicInstantMotionDetect, BasicInstantMotionDetectTest);
+ CASE(kBasicStationaryDetect, BasicStationaryDetectTest);
+ CASE(kBasicGyroscope, BasicGyroscopeTest);
+ CASE(kBasicMagnetometer, BasicMagnetometerTest);
+ CASE(kBasicBarometer, BasicBarometerTest);
+ CASE(kBasicLightSensor, BasicLightSensorTest);
+ CASE(kBasicProximity, BasicProximityTest);
+ CASE(kVersionSanity, VersionSanityTest);
+ CASE(kLoggingSanity, LoggingSanityTest);
+ CASE(kSendMessageToHost, SendMessageToHostTest);
+ CASE(kTimerSet, TimerSetTest);
+ CASE(kTimerCancel, TimerCancelTest);
+ CASE(kTimerStress, TimerStressTest);
+ CASE(kSendEventStress, SendEventStressTest);
+ CASE(kHeapExhaustionStability, HeapExhaustionStabilityTest);
+ CASE(kGnssCapabilities, GnssCapabilitiesTest);
+ CASE(kWifiCapabilities, WifiCapabilitiesTest);
+ CASE(kWwanCapabilities, WwanCapabilitiesTest);
+ CASE(kSensorInfo, SensorInfoTest);
+ CASE(kWwanCellInfoTest, WwanCellInfoTest);
+ CASE(kEstimatedHostTime, EstimatedHostTimeTest);
+ CASE(kNanoappInfoByAppId, NanoappInfoByAppIdTest);
+ CASE(kNanoappInfoByInstanceId, NanoappInfoByInstanceIdTest);
+ CASE(kNanoAppInfoEventsPerformer, NanoAppInfoEventsTestPerformer);
+ CASE(kNanoAppInfoEventsObserver, NanoAppInfoEventsTestObserver);
+
+#undef CASE
+
+ default:
+ sendFatalFailureToHost("Unexpected message type:",
+ &(data->reservedMessageType));
+ }
+
+ if (mCurrentTest != nullptr) {
+ mCurrentTest->testSetUp(data->messageSize, data->message);
+ } else {
+ sendInternalFailureToHost("createTest() ended with null mCurrentTest");
+ }
+}
+
+void App::freeTest() {
+ if (mCurrentTest == nullptr) {
+ sendInternalFailureToHost("Nanoapp unloading without running any test");
+ }
+ mCurrentTest->~Test();
+}
+
+} // namespace general_test
+
+static general_test::App gApp;
+
+
+extern "C" void nanoappHandleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ gApp.handleEvent(senderInstanceId, eventType, eventData);
+}
+
+static uint32_t zeroedBytes[13];
+
+extern "C" bool nanoappStart(void) {
+ // zeroedBytes is in the BSS and needs to be zero'd out.
+ for (size_t i = 0; i < sizeof(zeroedBytes)/sizeof(zeroedBytes[0]); i++) {
+ if (zeroedBytes[i] != 0) {
+ return false;
+ }
+ }
+
+ // A CHRE is required to call the constructor of our class prior to
+ // reaching this point.
+ return gApp.wasConstructed();
+}
+
+extern "C" void nanoappEnd(void) {
+ if (gApp.wasDestructed()) {
+ // It's not legal for us to send a message from here. The best
+ // we can do is abort, although there's no means for the end user
+ // to see such a failure.
+ // TODO: Figure out how to have this failure noticed.
+ nanoapp_testing::abort(AbortBlame::kChreInNanoappEnd);
+ }
+ gApp.freeTest();
+
+ // TODO: Unclear how we can test the global destructor being called,
+ // but that would be good to test. Since it's supposed to happen
+ // after this call completes, it's difficult to test.
+}
diff --git a/apps/chqts/src/general_test/basic_sensor_test_base.cc b/apps/chqts/src/general_test/basic_sensor_test_base.cc
new file mode 100644
index 0000000..3024eda
--- /dev/null
+++ b/apps/chqts/src/general_test/basic_sensor_test_base.cc
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/basic_sensor_test_base.h>
+
+#include <cinttypes>
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+using nanoapp_testing::sendStringToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * Our general test flow is as follows:
+ *
+ * Constructor: Send startEvent to self to start.
+ * StartEvent: Get default sensor and perform various sanity checks. Configure
+ * the sensor.
+ *
+ * At this point, it depends what kind of sensor we have for how we proceed
+ * with the test.
+ *
+ * One-shot: finishTest()
+ * On-change: Wait for one data event from sensor. Then finishTest().
+ * Continuous: Wait for two data events from sensor. Then finishTest().
+ *
+ * We also look for and perform basic sanity checking on sampling status
+ * change events, as well as bias data reports.
+ */
+
+
+namespace general_test {
+
+namespace {
+constexpr uint16_t kStartEvent = CHRE_EVENT_FIRST_USER_VALUE;
+constexpr uint16_t kPassiveCompleteEvent = CHRE_EVENT_FIRST_USER_VALUE + 1;
+constexpr uint64_t kNanosecondsPerSecond = 1000000000;
+constexpr uint64_t kEventLoopSlack = 100000000; // 100 msec
+
+uint64_t getEventDuration(const chreSensorThreeAxisData *event) {
+ uint64_t duration = 0;
+ for (size_t i = 0; i < event->header.readingCount; i++) {
+ duration += event->readings[i].timestampDelta;
+ }
+
+ return duration;
+}
+} // anonymous namespace
+
+BasicSensorTestBase::BasicSensorTestBase()
+ : Test(CHRE_API_VERSION_1_0),
+ mInMethod(true),
+ mExternalSamplingStatusChange(false),
+ mDoneWithPassiveConfigure(false),
+ mState(State::kPreStart),
+ mInstanceId(chreGetInstanceId())
+ /* All other members initialized later */ {
+}
+
+void BasicSensorTestBase::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "Beginning message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+ // Most tests start running in the constructor. However, since this
+ // is a base class, and we invoke abstract methods when running our
+ // test, we don't start until after the class has been fully
+ // constructed.
+ if (!chreSendEvent(kStartEvent, nullptr, nullptr, mInstanceId)) {
+ sendFatalFailureToHost("Failed chreSendEvent to begin test");
+ }
+ mInMethod = false;
+}
+
+void BasicSensorTestBase::checkPassiveConfigure() {
+ chreSensorConfigureMode mode = isOneShotSensor() ?
+ CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT :
+ CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS;
+
+ if (mApiVersion == CHRE_API_VERSION_1_0) {
+ // Any attempt to make a PASSIVE call with a non-default interval
+ // or latency should fail.
+ if (chreSensorConfigure(mSensorHandle, mode,
+ CHRE_SENSOR_INTERVAL_DEFAULT, 999)) {
+ sendFatalFailureToHost("chreSensorConfigure() allowed passive with "
+ "different latency");
+ }
+ if (chreSensorConfigure(mSensorHandle, mode,
+ 999, CHRE_SENSOR_LATENCY_DEFAULT)) {
+ sendFatalFailureToHost("chreSensorConfigure() allowed passive with "
+ "different interval");
+ }
+ // TODO: In a more in-depth test, we should test passive mode
+ // receiving data. This is somewhat complicated by the fact that
+ // pretty much by definition, we don't control whether a sensor
+ // we're passively listening to is enabled or not. We could try
+ // to control this with an additional test nanoapp toggling sensor
+ // usage, but there's still the complication of other nanoapps in
+ // the system.
+ } else {
+ if (!chreSensorConfigure(mSensorHandle, mode,
+ CHRE_SENSOR_INTERVAL_DEFAULT,
+ kNanosecondsPerSecond)) {
+ sendFatalFailureToHost("chreSensorConfigure() failed passive with "
+ "default interval and non-default latency");
+ }
+ if (!isOneShotSensor() && !chreSensorConfigure(
+ mSensorHandle, mode, kNanosecondsPerSecond,
+ CHRE_SENSOR_LATENCY_DEFAULT)) {
+ sendFatalFailureToHost("chreSensorConfigure() failed passive with "
+ "non-default interval and default latency");
+ }
+ if (!isOneShotSensor() && !chreSensorConfigure(
+ mSensorHandle, mode, kNanosecondsPerSecond,
+ kNanosecondsPerSecond)) {
+ sendFatalFailureToHost("chreSensorConfigure() failed passive with "
+ "non-default interval and latency");
+ }
+ }
+}
+
+void BasicSensorTestBase::startTest() {
+ mState = State::kPreConfigure;
+ if (!chreSensorFindDefault(getSensorType(), &mSensorHandle)) {
+ if (isRequiredSensor()) {
+ sendFatalFailureToHost("Sensor is required, but no default found.");
+ }
+ sendStringToHost(MessageType::kSkipped, "No default sensor found for "
+ "optional sensor.");
+ return;
+ }
+
+ chreSensorInfo info;
+ if (!chreGetSensorInfo(mSensorHandle, &info)) {
+ sendFatalFailureToHost("GetSensorInfo() call failed");
+ }
+ if (info.sensorName == nullptr) {
+ sendFatalFailureToHost("chreSensorInfo::sensorName is NULL");
+ }
+ if (info.sensorType != getSensorType()) {
+ uint32_t type = info.sensorType;
+ sendFatalFailureToHost("chreSensorInfo::sensorType is not expected "
+ "value, is:", &type);
+ }
+ if (info.isOnChange != isOnChangeSensor()) {
+ sendFatalFailureToHost("chreSensorInfo::isOnChange is opposite of "
+ "what we expected");
+ }
+ if (info.isOneShot != isOneShotSensor()) {
+ sendFatalFailureToHost("chreSensorInfo::isOneShot is opposite of "
+ "what we expected");
+ }
+
+ if (!chreGetSensorSamplingStatus(mSensorHandle, &mOriginalStatus)) {
+ sendFatalFailureToHost("chreGetSensorSamplingStatus() failed");
+ }
+
+ // Nanoapp may start getting events with a passive request. Set the base
+ // timestamp to compare against before configuring the sensor.
+ mPreTimestamp = chreGetTime();
+
+ checkPassiveConfigure();
+ if (!chreSendEvent(kPassiveCompleteEvent, nullptr, nullptr, mInstanceId)) {
+ sendFatalFailureToHost("Failed chreSendEvent to complete passive test");
+ }
+
+ // Default interval/latency must be accepted by all sensors.
+ mNewStatus = {
+ CHRE_SENSOR_INTERVAL_DEFAULT, /* interval */
+ CHRE_SENSOR_LATENCY_DEFAULT, /* latency */
+ true /* enabled */
+ };
+ chreSensorConfigureMode mode = isOneShotSensor() ?
+ CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT :
+ CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS;
+
+ if (!chreSensorConfigure(mSensorHandle, mode,
+ mNewStatus.interval, mNewStatus.latency)) {
+ sendFatalFailureToHost("chreSensorConfigure() call failed with default"
+ " interval and latency");
+ }
+ // handleEvent may start getting events, and our testing continues there.
+ // (Note: The CHRE is not allow to call handleEvent() while we're still
+ // in this method, so it's not a race to set this state here.)
+
+ // Set a new request so the test can receive events before test timeout.
+ mNewStatus = {
+ // This will be valid on all required sensors.
+ // TODO: A more in-depth test could try to change this interval
+ // from what it currently is for the sensor, and confirm it
+ // changes back when we're DONE. But that's beyond the current
+ // scope of this 'basic' test.
+ kNanosecondsPerSecond, /* interval */
+ // We want the test to run as quickly as possible.
+ // TODO: Similar to the interval, we could try to test changes in
+ // this value, but it's beyond our 'basic' scope for now.
+ CHRE_SENSOR_LATENCY_ASAP, /* latency */
+ true /* enabled */
+ };
+
+ // Skip one-shot sensors for non-default interval configurations.
+ if (!isOneShotSensor() && !chreSensorConfigure(
+ mSensorHandle, mode, mNewStatus.interval, mNewStatus.latency)) {
+ sendFatalFailureToHost("chreSensorConfigure() call failed");
+ }
+
+ if (isOnChangeSensor()) {
+ // We should receive the current state of this sensor after the
+ // configure call. However, we're not assured additional events,
+ // since we don't know if this is going to change. Thus, we jump
+ // our testing state to waiting for the last event.
+ mState = State::kExpectingLastDataEvent;
+ } else if (isOneShotSensor()) {
+ // There's no assurance we'll get any events from a one-shot
+ // sensor, so we'll just skip to the end of the test.
+ finishTest();
+ } else {
+ mState = State::kExpectingInitialDataEvent;
+ }
+}
+
+void BasicSensorTestBase::finishTest() {
+ if (!chreSensorConfigureModeOnly(mSensorHandle,
+ CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
+ sendFatalFailureToHost("Unable to configure sensor mode to DONE");
+ }
+ mDoneTimestamp = chreGetTime();
+ chreSensorSamplingStatus status;
+ if (!chreGetSensorSamplingStatus(mSensorHandle, &status)) {
+ sendFatalFailureToHost("Could not get final sensor info");
+ }
+ if (!mExternalSamplingStatusChange) {
+ // No one else changed this, so it should be what we had before.
+ if (status.enabled != mOriginalStatus.enabled) {
+ sendFatalFailureToHost("SensorInfo.enabled not back to original");
+ }
+ if (status.interval != mOriginalStatus.interval) {
+ sendFatalFailureToHost("SensorInfo.interval not back to original");
+ }
+ if (status.latency != mOriginalStatus.latency) {
+ sendFatalFailureToHost("SensorInfo.latency not back to original");
+ }
+ }
+ mState = State::kFinished;
+ sendSuccessToHost();
+}
+
+void BasicSensorTestBase::sanityCheckHeader(const chreSensorDataHeader* header,
+ bool modifyTimestamps,
+ uint64_t eventDuration) {
+ if (header->sensorHandle != mSensorHandle) {
+ sendFatalFailureToHost("SensorDataHeader for wrong handle",
+ &header->sensorHandle);
+ }
+
+ if (!isOnChangeSensor()) {
+ // An on-change sensor is supposed to send its current state, which
+ // could be timestamped in the past. Everything else should be
+ // getting recent data.
+ uint64_t *minTime = nullptr;
+ uint64_t *timeToUpdate = nullptr;
+
+ if (mState == State::kExpectingInitialDataEvent) {
+ minTime = &mPreTimestamp;
+ timeToUpdate = &mFirstEventTimestamp;
+ } else if (mState == State::kExpectingLastDataEvent) {
+ minTime = &mFirstEventTimestamp;
+ timeToUpdate = &mLastEventTimestamp;
+ } else { // State::kFinished
+ minTime = &mLastEventTimestamp;
+ // Go ahead and update this timestamp again.
+ timeToUpdate = &mLastEventTimestamp;
+ }
+
+ // If there's another CHRE client requesting batched sensor data,
+ // baseTimestamp can be before mPreTimestamp. Also allow
+ // kEventLoopSlack to handle this nanoapp before handling the sensor
+ // event.
+ uint64_t minTimeWithSlack =
+ (*minTime > eventDuration + kEventLoopSlack) ?
+ (*minTime - eventDuration - kEventLoopSlack) : 0;
+ if (header->baseTimestamp < minTimeWithSlack) {
+ chreLog(CHRE_LOG_ERROR,
+ "baseTimestamp %" PRIu64 " < minTimeWithSlack %" PRIu64
+ ": minTime %" PRIu64 " eventDuration %" PRIu64
+ " kEventLoopSlack %" PRIu64,
+ header->baseTimestamp, minTimeWithSlack,
+ *minTime, eventDuration, kEventLoopSlack);
+ sendFatalFailureToHost("SensorDataHeader is in the past");
+ }
+ if ((mState == State::kFinished) &&
+ (header->baseTimestamp > mDoneTimestamp)) {
+ sendFatalFailureToHost("SensorDataHeader is from after DONE");
+ }
+ if (modifyTimestamps) {
+ *timeToUpdate = header->baseTimestamp;
+ }
+ }
+ if (header->readingCount == 0) {
+ sendFatalFailureToHost("SensorDataHeader has readingCount of 0");
+ }
+ if ((header->reserved[0] != 0) || (header->reserved[1] != 0)) {
+ sendFatalFailureToHost("SensorDataHeader has non-zero reserved bytes");
+ }
+}
+
+
+void BasicSensorTestBase::handleBiasEvent(
+ uint16_t eventType, const chreSensorThreeAxisData *eventData) {
+ uint8_t expectedSensorType = 0;
+ uint32_t eType = eventType;
+ if (eventType == CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO) {
+ expectedSensorType = CHRE_SENSOR_TYPE_GYROSCOPE;
+ } else if (eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO) {
+ expectedSensorType = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
+ } else {
+ sendInternalFailureToHost("Illegal eventType in handleBiasEvent",
+ &eType);
+ }
+
+ if (expectedSensorType != getSensorType()) {
+ sendFatalFailureToHost("Unexpected bias event:", &eType);
+ }
+ sanityCheckHeader(&eventData->header, false, getEventDuration(eventData));
+
+ // TODO: Sanity check the eventData. This check is out-of-scope for
+ // Android N testing.
+}
+
+void BasicSensorTestBase::handleSamplingChangeEvent(
+ const chreSensorSamplingStatusEvent* eventData) {
+ if (eventData->sensorHandle != mSensorHandle) {
+ sendFatalFailureToHost("SamplingChangeEvent for wrong sensor handle:",
+ &eventData->sensorHandle);
+ }
+ if (mState == State::kFinished) {
+ // TODO: If we strictly define whether this event is or isn't
+ // generated upon being DONE with a sensor, then we can perform
+ // a strict check here. For now, we just let this go.
+ return;
+ }
+ // Passive sensor requests do not guarantee sensors will always be enabled.
+ // Bypass 'enabled' check for passive configurations.
+ if (mDoneWithPassiveConfigure && !eventData->status.enabled) {
+ sendFatalFailureToHost("SamplingChangeEvent disabled the sensor.");
+ }
+
+ if ((mNewStatus.interval != eventData->status.interval) ||
+ (mNewStatus.latency != eventData->status.latency)) {
+ // This is from someone other than us. Let's note that so we know
+ // our sanity checks are invalid.
+ mExternalSamplingStatusChange = true;
+ }
+}
+
+void BasicSensorTestBase::handleSensorDataEvent(const void* eventData) {
+ if ((mState == State::kPreStart) || (mState == State::kPreConfigure)) {
+ sendFatalFailureToHost("SensorDataEvent sent too early.");
+ }
+ // Note, if mState is kFinished, we could be getting batched data which
+ // hadn't been delivered yet at the time we were DONE. We'll sanity
+ // check it, even though in theory we're done testing.
+ uint64_t eventDuration = getEventDuration(
+ static_cast<const chreSensorThreeAxisData *>(eventData));
+ sanityCheckHeader(static_cast<const chreSensorDataHeader*>(eventData),
+ true, eventDuration);
+
+ // Send to the sensor itself for any additional checks of actual data.
+ confirmDataIsSane(eventData);
+ if (mState == State::kExpectingInitialDataEvent) {
+ mState = State::kExpectingLastDataEvent;
+ } else if (mState == State::kExpectingLastDataEvent) {
+ finishTest();
+ } else if (mState != State::kFinished) {
+ uint32_t value = static_cast<uint32_t>(mState);
+ sendInternalFailureToHost("Illegal mState in handleSensorDataEvent:",
+ &value);
+ }
+}
+
+void BasicSensorTestBase::handleEvent(
+ uint32_t senderInstanceId, uint16_t eventType, const void* eventData) {
+ if (mInMethod) {
+ sendFatalFailureToHost("handleEvent() invoked while already in "
+ "method.");
+ }
+ mInMethod = true;
+ const uint16_t dataEventType =
+ CHRE_EVENT_SENSOR_DATA_EVENT_BASE + getSensorType();
+
+ if (senderInstanceId == mInstanceId) {
+ if ((eventType == kStartEvent) && (mState == State::kPreStart)) {
+ startTest();
+ } else if (eventType == kPassiveCompleteEvent) {
+ mDoneWithPassiveConfigure = true;
+ }
+
+ } else if ((mState == State::kPreStart) ||
+ (mState == State::kPreConfigure)) {
+ unexpectedEvent(eventType);
+
+ } else if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("Unexpected senderInstanceId:",
+ &senderInstanceId);
+
+ } else if (eventData == nullptr) {
+ uint32_t eType = eventType;
+ sendFatalFailureToHost("Got NULL eventData for event:", &eType);
+
+ } else if (eventType == dataEventType) {
+ handleSensorDataEvent(eventData);
+
+ } else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE) {
+ handleSamplingChangeEvent(
+ static_cast<const chreSensorSamplingStatusEvent*>(eventData));
+
+ } else if ((eventType == CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO) ||
+ (eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO)) {
+ handleBiasEvent(eventType,
+ static_cast<const chreSensorThreeAxisData*>(eventData));
+
+ } else {
+ unexpectedEvent(eventType);
+ }
+
+ mInMethod = false;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/basic_sensor_test_base.h b/apps/chqts/src/general_test/basic_sensor_test_base.h
new file mode 100644
index 0000000..1ad5a61
--- /dev/null
+++ b/apps/chqts/src/general_test/basic_sensor_test_base.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TEST_BASE_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TEST_BASE_H_
+
+#include <general_test/test.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+/**
+ * Abstract base class for basic sensor tests.
+ *
+ * This repeats a similar test for several different sensor types. Children
+ * classes must implement the abstract methods to define details about the
+ * sensor.
+ *
+ * This uses a Simple Protocol between the Host and Nanoapp.
+ */
+class BasicSensorTestBase : public Test {
+ public:
+ BasicSensorTestBase();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ /**
+ * Abstract method indicating which sensor type this is.
+ *
+ * @returns One of the CHRE_SENSOR_TYPE_* constants.
+ */
+ virtual uint8_t getSensorType() const = 0;
+
+ /**
+ * Abstract method indicating if this sensor is required for a valid
+ * CHRE implementation.
+ *
+ * A CHRE isn't useful without certain sensors available.
+ *
+ * @returns true if this is sensor is required; false otherwise.
+ */
+ virtual bool isRequiredSensor() const = 0;
+
+ /**
+ * Abstract method indicating if this is an on-change sensor.
+ *
+ * @returns true if this sensor is on-change; false otherwise.
+ */
+ virtual bool isOnChangeSensor() const = 0;
+
+ /**
+ * Abstract method indicating if this is a one-shot sensor.
+ *
+ * @returns true if this sensor is one-shot; false otherwise.
+ */
+ virtual bool isOneShotSensor() const = 0;
+
+ /**
+ * Abstract method which makes sure the given data is sane.
+ *
+ * This is a very loose test, and some sensors may provide no checking
+ * at all here. But some sensor might be able to provide a basic check
+ * (for example, a barometer claiming 0 hPa is broken (assuming the tests
+ * aren't running in outer space)).
+ *
+ * @returns If the data is absurd, this function will not return (it
+ * will trigger a fatal error report). This function returning
+ * is evidence the data is sane.
+ */
+ virtual void confirmDataIsSane(const void* eventData) = 0;
+
+ private:
+ enum State {
+ kPreStart,
+ kPreConfigure,
+ kExpectingInitialDataEvent,
+ kExpectingLastDataEvent,
+ kFinished
+ };
+
+ // Catch if CHRE performs reentrant calls for handleEvent()
+ bool mInMethod;
+ // If some external user changes the sampling status of our sensor,
+ // we shouldn't perform some of the checking, because it will be flaky.
+ bool mExternalSamplingStatusChange;
+ bool mDoneWithPassiveConfigure;
+ State mState;
+ uint16_t mInstanceId;
+ uint32_t mSensorHandle;
+ uint64_t mPreTimestamp;
+ uint64_t mFirstEventTimestamp;
+ uint64_t mLastEventTimestamp;
+ uint64_t mDoneTimestamp;
+ chreSensorSamplingStatus mOriginalStatus;
+ chreSensorSamplingStatus mNewStatus;
+
+ void startTest();
+ void finishTest();
+ void checkPassiveConfigure();
+ void handleBiasEvent(uint16_t eventType,
+ const chreSensorThreeAxisData* eventData);
+ void handleSamplingChangeEvent(
+ const chreSensorSamplingStatusEvent* eventData);
+ void handleSensorDataEvent(const void* eventData);
+ void sanityCheckHeader(const chreSensorDataHeader* header,
+ bool modifyTimestamps,
+ uint64_t eventDuration);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TEST_BASE_H_
diff --git a/apps/chqts/src/general_test/basic_sensor_tests.cc b/apps/chqts/src/general_test/basic_sensor_tests.cc
new file mode 100644
index 0000000..fd1706f
--- /dev/null
+++ b/apps/chqts/src/general_test/basic_sensor_tests.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/basic_sensor_tests.h>
+
+#include <shared/send_message.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+
+namespace general_test {
+
+static void checkFloat(float value, float extremeLow, float extremeHigh) {
+ if ((value < extremeLow) || (value > extremeHigh)) {
+ uint32_t i = static_cast<uint32_t>(value);
+ sendFatalFailureToHost("Value beyond extreme. As int:", &i);
+ }
+}
+
+static void checkTimestampDelta(uint32_t delta, uint32_t index) {
+ if (index == 0) {
+ // This delta is allowed (and expected, but not required) to be 0.
+ return;
+ }
+ if (delta == 0) {
+ sendFatalFailureToHost("timestampDelta was 0 for reading index ",
+ &index);
+ }
+}
+
+static void sanityCheckThreeAxisData(const void *eventData, float extremeLow,
+ float extremeHigh) {
+ auto data = static_cast<const chreSensorThreeAxisData*>(eventData);
+ for (size_t i = 0; i < data->header.readingCount; i++) {
+ checkTimestampDelta(data->readings[i].timestampDelta, i);
+ for (size_t j = 0; j < 3; j++) {
+ checkFloat(data->readings[i].values[j], extremeLow, extremeHigh);
+ }
+ }
+}
+
+static void sanityCheckFloatData(const void *eventData, float extremeLow,
+ float extremeHigh) {
+ auto data = static_cast<const chreSensorFloatData*>(eventData);
+ for (size_t i = 0; i < data->header.readingCount; i++) {
+ checkTimestampDelta(data->readings[i].timestampDelta, i);
+ checkFloat(data->readings[i].value, extremeLow, extremeHigh);
+ }
+}
+
+void BasicAccelerometerTest::confirmDataIsSane(const void* eventData) {
+ constexpr float kExtreme = 70.5f; // Apollo 16 on reentry (7.19g)
+ sanityCheckThreeAxisData(eventData, -kExtreme, kExtreme);
+}
+
+void BasicInstantMotionDetectTest::confirmDataIsSane(const void* eventData) {
+ // Nothing to sanity check.
+}
+
+void BasicStationaryDetectTest::confirmDataIsSane(const void* eventData) {
+ // Nothing to sanity check.
+}
+
+void BasicGyroscopeTest::confirmDataIsSane(const void* eventData) {
+ constexpr float kExtreme = 9420.0f; // Zippe centrifuge
+ sanityCheckThreeAxisData(eventData, -kExtreme, kExtreme);
+}
+
+void BasicMagnetometerTest::confirmDataIsSane(const void* eventData) {
+ constexpr float kExtreme = 9400000.0f; // Strength of medical MRI
+ sanityCheckThreeAxisData(eventData, -kExtreme, kExtreme);
+}
+
+void BasicBarometerTest::confirmDataIsSane(const void* eventData) {
+ constexpr float kExtremeLow = 337.0f; // Mount Everest summit
+ constexpr float kExtremeHigh = 1067.0f; // Dead Sea
+ sanityCheckFloatData(eventData, kExtremeLow, kExtremeHigh);
+}
+
+void BasicLightSensorTest::confirmDataIsSane(const void* eventData) {
+ constexpr float kExtreme = 300000.0f; // 3x the Sun
+ sanityCheckFloatData(eventData, 0.0f, kExtreme);
+}
+
+void BasicProximityTest::confirmDataIsSane(const void* eventData) {
+ auto data = static_cast<const chreSensorByteData*>(eventData);
+ for (size_t i = 0; i < data->header.readingCount; i++) {
+ checkTimestampDelta(data->readings[i].timestampDelta, i);
+ // 'invalid' is a sane reading. But our padding should be zero'd.
+ if (data->readings[i].padding0 != 0) {
+ uint32_t padding = data->readings[i].padding0;
+ sendFatalFailureToHost("padding0 is data is non-zero:", &padding);
+ }
+ }
+}
+
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/basic_sensor_tests.h b/apps/chqts/src/general_test/basic_sensor_tests.h
new file mode 100644
index 0000000..cdff3cf
--- /dev/null
+++ b/apps/chqts/src/general_test/basic_sensor_tests.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TESTS_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TESTS_H_
+
+#include <general_test/basic_sensor_test_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class BasicAccelerometerTest : public BasicSensorTestBase {
+ public:
+ BasicAccelerometerTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_ACCELEROMETER;
+ }
+ bool isRequiredSensor() const override { return true; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicInstantMotionDetectTest : public BasicSensorTestBase {
+ public:
+ BasicInstantMotionDetectTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT;
+ }
+ bool isRequiredSensor() const override { return true; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return true; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicStationaryDetectTest : public BasicSensorTestBase {
+ public:
+ BasicStationaryDetectTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_STATIONARY_DETECT;
+ }
+ bool isRequiredSensor() const override { return true; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return true; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicGyroscopeTest : public BasicSensorTestBase {
+ public:
+ BasicGyroscopeTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_GYROSCOPE;
+ }
+ bool isRequiredSensor() const override { return true; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicMagnetometerTest : public BasicSensorTestBase {
+ public:
+ BasicMagnetometerTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
+ }
+ bool isRequiredSensor() const override { return false; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicBarometerTest : public BasicSensorTestBase {
+ public:
+ BasicBarometerTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_PRESSURE;
+ }
+ bool isRequiredSensor() const override { return false; }
+ bool isOnChangeSensor() const override { return false; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicLightSensorTest : public BasicSensorTestBase {
+ public:
+ BasicLightSensorTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_LIGHT;
+ }
+ bool isRequiredSensor() const override { return false; }
+ bool isOnChangeSensor() const override { return true; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+class BasicProximityTest : public BasicSensorTestBase {
+ public:
+ BasicProximityTest()
+ : BasicSensorTestBase() {}
+
+ protected:
+ uint8_t getSensorType() const override {
+ return CHRE_SENSOR_TYPE_PROXIMITY;
+ }
+ bool isRequiredSensor() const override { return false; }
+ bool isOnChangeSensor() const override { return true; }
+ bool isOneShotSensor() const override { return false; }
+ void confirmDataIsSane(const void* eventData) override;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_BASIC_SENSOR_TESTS_H_
diff --git a/apps/chqts/src/general_test/cell_info_base.cc b/apps/chqts/src/general_test/cell_info_base.cc
new file mode 100644
index 0000000..34b1254
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_base.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_base.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+CellInfoBase::CellInfoBase() {
+ // Empty
+}
+
+bool CellInfoBase::isBoundedInt32(int32_t value, int32_t lower,
+ int32_t upper, int32_t invalid) {
+ return (((value >= lower) && (value <= upper)) || (value == invalid));
+}
+
+void CellInfoBase::sendFatalFailureInt32(const char *message, int32_t value) {
+ uint32_t val = value;
+ nanoapp_testing::sendFatalFailureToHost(message, &val);
+}
+
+void CellInfoBase::sendFatalFailureUint8(const char *message, uint8_t value) {
+ uint32_t val = value;
+ nanoapp_testing::sendFatalFailureToHost(message, &val);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_base.h b/apps/chqts/src/general_test/cell_info_base.h
new file mode 100644
index 0000000..c7bc133
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_base.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_BASE_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_BASE_H_
+
+#include <cstdint>
+
+namespace general_test {
+
+class CellInfoBase {
+ public:
+ // TODO: A quick hack to convert to a uint32_t for sending fatal failure
+ static void sendFatalFailureInt32(const char *message, int32_t value);
+ static void sendFatalFailureUint8(const char *message, uint8_t value);
+
+ private:
+ CellInfoBase();
+
+ protected:
+ static bool isBoundedInt32(int32_t value, int32_t lower,
+ int32_t upper, int32_t invalid);
+
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_BASE_H_
diff --git a/apps/chqts/src/general_test/cell_info_cdma.cc b/apps/chqts/src/general_test/cell_info_cdma.cc
new file mode 100644
index 0000000..3bd0156
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_cdma.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_cdma.h>
+
+namespace general_test {
+
+bool CellInfoCdma::validateIdentity(
+ const struct chreWwanCellIdentityCdma identity) {
+ bool valid = false;
+ constexpr int32_t max = INT32_MAX;
+
+ if (!isBoundedInt32(identity.networkId, 0, 65535, max)) {
+ sendFatalFailureInt32(
+ "Invalid CDMA Network Id: %d", identity.networkId);
+ } else if (!isBoundedInt32(identity.systemId, 0, 32767, max)) {
+ sendFatalFailureInt32(
+ "Invalid CDMA System Id: %d", identity.systemId);
+ } else if (!isBoundedInt32(identity.basestationId, 0, 65535, max)) {
+ sendFatalFailureInt32("Invalid CDMA Base Station Id: %d",
+ identity.basestationId);
+ } else if (!isBoundedInt32(identity.longitude, -2592000, 2592000, max)) {
+ sendFatalFailureInt32("Invalid CDMA Longitude: %d", identity.longitude);
+ } else if (!isBoundedInt32(identity.latitude, -1296000, 1296000, max)) {
+ sendFatalFailureInt32("Invalid CDMA Latitude: %d", identity.latitude);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoCdma::validateSignalStrengthCdma(
+ const struct chreWwanSignalStrengthCdma strength) {
+ bool valid = false;
+
+ // TODO: Find exact limits on dbm and ecio
+ if ((strength.dbm < 0) || (strength.dbm > 160)) {
+ sendFatalFailureInt32("Invalid CDMA/CDMA dbm: %d", strength.dbm);
+ } else if ((strength.ecio < 0) || (strength.ecio > 1600)) {
+ sendFatalFailureInt32("Invalid CDMA/CDMA ecio: %d", strength.ecio);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoCdma::validateSignalStrengthEvdo(
+ const struct chreWwanSignalStrengthEvdo strength) {
+ bool valid = false;
+
+ // TODO: Find exact limits on dbm and ecio
+ if ((strength.dbm < 0) || (strength.dbm > 160)) {
+ sendFatalFailureInt32("Invalid CDMA/EVDO dbm: %d", strength.dbm);
+ } else if ((strength.ecio < 0) || (strength.ecio > 1600)) {
+ sendFatalFailureInt32("Invalid CDMA/EVDO ecio: %d", strength.ecio);
+ } else if ((strength.signalNoiseRatio < 0)
+ || (strength.signalNoiseRatio > 8)) {
+ sendFatalFailureInt32("Invalid evdo signal noise ratio: %d",
+ strength.signalNoiseRatio);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoCdma::validate(const struct chreWwanCellInfoCdma& cell) {
+ return (validateIdentity(cell.cellIdentityCdma)
+ && validateSignalStrengthCdma(cell.signalStrengthCdma)
+ && validateSignalStrengthEvdo(cell.signalStrengthEvdo));
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_cdma.h b/apps/chqts/src/general_test/cell_info_cdma.h
new file mode 100644
index 0000000..6293ccc
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_cdma.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_CDMA_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_CDMA_H_
+
+#include <general_test/cell_info_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class CellInfoCdma : private CellInfoBase {
+ public:
+ static bool validate(const struct chreWwanCellInfoCdma& cell);
+
+ private:
+ static bool validateIdentity(const struct chreWwanCellIdentityCdma identity);
+ static bool validateSignalStrengthCdma(
+ const struct chreWwanSignalStrengthCdma strength);
+ static bool validateSignalStrengthEvdo(
+ const struct chreWwanSignalStrengthEvdo strength);
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_CDMA_H_
diff --git a/apps/chqts/src/general_test/cell_info_gsm.cc b/apps/chqts/src/general_test/cell_info_gsm.cc
new file mode 100644
index 0000000..3fbd7e2
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_gsm.cc
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_gsm.h>
+
+namespace general_test {
+
+bool CellInfoGsm::validateIdentity(
+ const struct chreWwanCellIdentityGsm identity) {
+ bool valid = false;
+
+ if (!isBoundedInt32(identity.mcc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid GSM Mobile Country Code: %d", identity.mcc);
+ } else if (!isBoundedInt32(identity.mnc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid GSM Mobile Network Code: %d", identity.mnc);
+ } else if (!isBoundedInt32(identity.lac, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid GSM Location Area Code", identity.lac);
+ } else if (!isBoundedInt32(identity.cid, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid GSM Cell Identity: %d", identity.cid);
+ } else if (!isBoundedInt32(identity.arfcn, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid GSM Absolute RF Channel Number: %d",
+ identity.arfcn);
+ } else if ((identity.bsic > 63) && (identity.bsic != UINT8_MAX)) {
+ sendFatalFailureUint8("Invalid GSM Base Station Identity Code: %d",
+ identity.bsic);
+ } else {
+ valid = true;
+
+ for (uint8_t byte : identity.reserved) {
+ if (byte != 0) {
+ valid = false;
+ sendFatalFailureUint8("Invalid GSM reserved byte: %d",
+ byte);
+ }
+
+ if (!valid) {
+ break;
+ }
+ }
+ }
+
+ return valid;
+}
+
+bool CellInfoGsm::validateSignalStrength(
+ const struct chreWwanSignalStrengthGsm strength) {
+ bool valid = false;
+
+ if (!isBoundedInt32(strength.signalStrength, 0, 31, 99)) {
+ sendFatalFailureInt32("Invalid GSM signal strength: %d",
+ strength.signalStrength);
+ } else if (!isBoundedInt32(strength.bitErrorRate, 0, 7, 99)) {
+ sendFatalFailureInt32("Invalid GSM bit error rate: %d",
+ strength.bitErrorRate);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoGsm::validate(const struct chreWwanCellInfoGsm& cell) {
+ return (validateIdentity(cell.cellIdentityGsm)
+ && validateSignalStrength(cell.signalStrengthGsm));
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_gsm.h b/apps/chqts/src/general_test/cell_info_gsm.h
new file mode 100644
index 0000000..bd28e0a
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_gsm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_GSM_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_GSM_H_
+
+#include <general_test/cell_info_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class CellInfoGsm : private CellInfoBase {
+ public:
+ static bool validate(const struct chreWwanCellInfoGsm& cell);
+
+ private:
+ static bool validateIdentity(const struct chreWwanCellIdentityGsm identity);
+ static bool validateSignalStrength(
+ const struct chreWwanSignalStrengthGsm strength);
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_GSM_H_
diff --git a/apps/chqts/src/general_test/cell_info_lte.cc b/apps/chqts/src/general_test/cell_info_lte.cc
new file mode 100644
index 0000000..9a1b8b9
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_lte.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_lte.h>
+
+namespace general_test {
+
+bool CellInfoLte::validateIdentity(
+ const struct chreWwanCellIdentityLte identity) {
+ bool valid = false;
+
+ if (!isBoundedInt32(identity.mcc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid LTE Mobile Country Code: %d", identity.mcc);
+ } else if (!isBoundedInt32(identity.mnc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid LTE Mobile Network Code: %d", identity.mnc);
+ } else if (!isBoundedInt32(identity.ci, 0, 268435455, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid LTE Cell Identity: %d", identity.ci);
+ } else if (!isBoundedInt32(identity.pci, 0, 503, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid LTE Physical Cell Id: %d", identity.pci);
+ } else if (!isBoundedInt32(identity.tac, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid LTE Tracking Area Code: %d", identity.tac);
+ } else if (!isBoundedInt32(identity.earfcn, 0, 262144, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid LTE Absolute RF Channel Number: %d",
+ identity.earfcn);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoLte::validateSignalStrength(
+ const struct chreWwanSignalStrengthLte strength) {
+ bool valid = false;
+ constexpr int32_t max = INT32_MAX;
+
+ if (!isBoundedInt32(strength.signalStrength, 0, 31, 99)) {
+ sendFatalFailureInt32("Invalid LTE Signal Strength: %d",
+ strength.signalStrength);
+ } else if (!isBoundedInt32(strength.rsrp, 44, 140, max)) {
+ sendFatalFailureInt32("Invalid LTE Reference Signal Receive Power: %d",
+ strength.rsrp);
+ } else if (!isBoundedInt32(strength.rsrq, 3, 20, max)) {
+ sendFatalFailureInt32(
+ "Invalid LTE Reference Signal Receive Quality: %d",
+ strength.rsrq);
+ } else if (!isBoundedInt32(strength.rssnr, -200, 300, max)) {
+ sendFatalFailureInt32(
+ "Invalid LTE Reference Signal Signal-to-noise Ratio: %d",
+ strength.rssnr);
+ } else if (!isBoundedInt32(strength.cqi, 0, 15, max)) {
+ sendFatalFailureInt32("Invalid LTE Channel Quality Indicator: %d",
+ strength.cqi);
+ } else if (!isBoundedInt32(strength.timingAdvance, 0, max, max)) {
+ sendFatalFailureInt32("Invalid LTE Timing Advance (ms): %d",
+ strength.timingAdvance);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoLte::validate(const struct chreWwanCellInfoLte& cell) {
+ return (validateIdentity(cell.cellIdentityLte)
+ && validateSignalStrength(cell.signalStrengthLte));
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_lte.h b/apps/chqts/src/general_test/cell_info_lte.h
new file mode 100644
index 0000000..d9e377d
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_lte.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_LTE_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_LTE_H_
+
+#include <general_test/cell_info_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class CellInfoLte : private CellInfoBase {
+ public:
+ static bool validate(const struct chreWwanCellInfoLte& cell);
+
+ private:
+ static bool validateIdentity(const struct chreWwanCellIdentityLte identity);
+ static bool validateSignalStrength(
+ const struct chreWwanSignalStrengthLte strength);
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_LTE_H_
diff --git a/apps/chqts/src/general_test/cell_info_tdscdma.cc b/apps/chqts/src/general_test/cell_info_tdscdma.cc
new file mode 100644
index 0000000..02df3f2
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_tdscdma.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_tdscdma.h>
+
+namespace general_test {
+
+bool CellInfoTdscdma::validateIdentity(
+ const struct chreWwanCellIdentityTdscdma identity) {
+ bool valid = false;
+
+ if (!isBoundedInt32(identity.mcc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid TDSCDMA Mobile Country Code: %d", identity.mcc);
+ } else if (!isBoundedInt32(identity.mnc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid TDSCDMA Mobile Network Code: %d", identity.mnc);
+ } else if (!isBoundedInt32(identity.lac, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid TDSCDMA Location Area Code: %d", identity.lac);
+ } else if (!isBoundedInt32(identity.cid, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid TDSCDMA Cell Identity: %d", identity.cid);
+ } else if (!isBoundedInt32(identity.cpid, 0, 127, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid TDSCDMA Cell Parameters ID: %d", identity.cpid);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoTdscdma::validateSignalStrength(
+ const struct chreWwanSignalStrengthTdscdma strength) {
+ bool valid = false;
+
+ if (!isBoundedInt32(strength.rscp, 25, 120, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid TDSCDMA Received Signal Code Power: %d",
+ strength.rscp);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoTdscdma::validate(const struct chreWwanCellInfoTdscdma& cell) {
+ return (validateIdentity(cell.cellIdentityTdscdma)
+ && validateSignalStrength(cell.signalStrengthTdscdma));
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_tdscdma.h b/apps/chqts/src/general_test/cell_info_tdscdma.h
new file mode 100644
index 0000000..186c3a7
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_tdscdma.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_TDSCDMA_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_TDSCDMA_H_
+
+#include <general_test/cell_info_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class CellInfoTdscdma : private CellInfoBase {
+ public:
+ static bool validate(const struct chreWwanCellInfoTdscdma& cell);
+
+ private:
+ static bool validateIdentity(
+ const struct chreWwanCellIdentityTdscdma identity);
+ static bool validateSignalStrength(
+ const struct chreWwanSignalStrengthTdscdma strength);
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_TDSCDMA_H_
diff --git a/apps/chqts/src/general_test/cell_info_wcdma.cc b/apps/chqts/src/general_test/cell_info_wcdma.cc
new file mode 100644
index 0000000..fbf2647
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_wcdma.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/cell_info_wcdma.h>
+
+namespace general_test {
+
+bool CellInfoWcdma::validateIdentity(
+ const struct chreWwanCellIdentityWcdma identity) {
+ bool valid = false;
+
+ if (!isBoundedInt32(identity.mcc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid WCDMA Mobile Country Code: %d", identity.mcc);
+ } else if (!isBoundedInt32(identity.mnc, 0, 999, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid WCDMA Mobile Network Code: %d", identity.mnc);
+ } else if (!isBoundedInt32(identity.lac, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32(
+ "Invalid WCDMA Location Area Code: %d", identity.lac);
+ } else if (!isBoundedInt32(identity.cid, 0, 268435455, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid WCDMA Cell Identity: %d", identity.cid);
+ } else if (!isBoundedInt32(identity.psc, 0, 511, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid WCDMA Primary Scrambling Code: %d",
+ identity.psc);
+ } else if (!isBoundedInt32(identity.uarfcn, 0, 65535, INT32_MAX)) {
+ sendFatalFailureInt32("Invalid WCDMA Absolute RF Channel Number: %d",
+ identity.uarfcn);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoWcdma::validateSignalStrength(
+ const struct chreWwanSignalStrengthWcdma strength) {
+ bool valid = false;
+
+ if (!isBoundedInt32(strength.signalStrength, 0, 31, 99)) {
+ sendFatalFailureInt32("Invalid WCDMA Signal Strength: %d",
+ strength.signalStrength);
+ } else if (!isBoundedInt32(strength.bitErrorRate, 0, 7, 99)) {
+ sendFatalFailureInt32("Invalid WCDMA Bit Error Rate: %d",
+ strength.bitErrorRate);
+ } else {
+ valid = true;
+ }
+
+ return valid;
+}
+
+bool CellInfoWcdma::validate(const struct chreWwanCellInfoWcdma& cell) {
+ return (validateIdentity(cell.cellIdentityWcdma)
+ && validateSignalStrength(cell.signalStrengthWcdma));
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/cell_info_wcdma.h b/apps/chqts/src/general_test/cell_info_wcdma.h
new file mode 100644
index 0000000..41db5fb
--- /dev/null
+++ b/apps/chqts/src/general_test/cell_info_wcdma.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_WCDMA_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_WCDMA_H_
+
+#include <general_test/cell_info_base.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+class CellInfoWcdma : private CellInfoBase {
+ public:
+ static bool validate(const struct chreWwanCellInfoWcdma& cell);
+
+ private:
+ static bool validateIdentity(
+ const struct chreWwanCellIdentityWcdma identity);
+ static bool validateSignalStrength(
+ const struct chreWwanSignalStrengthWcdma strength);
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_CELL_INFO_WCDMA_H_
diff --git a/apps/chqts/src/general_test/estimated_host_time_test.cc b/apps/chqts/src/general_test/estimated_host_time_test.cc
new file mode 100644
index 0000000..885eb26
--- /dev/null
+++ b/apps/chqts/src/general_test/estimated_host_time_test.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/estimated_host_time_test.h>
+
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+EstimatedHostTimeTest::EstimatedHostTimeTest()
+ : Test(CHRE_API_VERSION_1_1),
+ mTimerHandle(CHRE_TIMER_INVALID),
+ mRemainingIterations(25) {
+}
+
+void EstimatedHostTimeTest::setUp(uint32_t /* messageSize */,
+ const void * /* message */) {
+ mPriorHostTime = chreGetEstimatedHostTime();
+
+ constexpr uint64_t timerInterval = 100000000; // 100 ms
+
+ mTimerHandle = chreTimerSet(timerInterval, &mTimerHandle,
+ false /* oneShot */);
+
+ if (mTimerHandle == CHRE_TIMER_INVALID) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to set timer for time verification");
+ }
+}
+
+void EstimatedHostTimeTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ if (eventType == CHRE_EVENT_TIMER) {
+ verifyIncreasingTime();
+ } else {
+ // Verify application processor time is within reason
+ uint64_t currentHostTime = chreGetEstimatedHostTime();
+
+ // TODO: Estimate message RTT to allow stricter accuracy check
+ constexpr uint64_t timeDelta = 50000000; // 50 ms
+
+ uint64_t givenHostTime;
+ const void *message =
+ getMessageDataFromHostEvent(senderInstanceId, eventType,
+ eventData,
+ nanoapp_testing::MessageType::kContinue,
+ sizeof(givenHostTime));
+
+ nanoapp_testing::memcpy(&givenHostTime, message, sizeof(givenHostTime));
+ nanoapp_testing::littleEndianToHost(&givenHostTime);
+
+ if (currentHostTime >= givenHostTime) {
+ if ((currentHostTime - givenHostTime) <= timeDelta) {
+ nanoapp_testing::sendSuccessToHost();
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Current time is too far behind of host time");
+ }
+ } else if ((givenHostTime - currentHostTime) <= timeDelta) {
+ nanoapp_testing::sendSuccessToHost();
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Current time is too far ahead of host time");
+ }
+ }
+}
+
+void EstimatedHostTimeTest::verifyIncreasingTime() {
+ if (mRemainingIterations > 0) {
+ uint64_t currentHostTime = chreGetEstimatedHostTime();
+
+ if (currentHostTime > mPriorHostTime) {
+ chreTimerCancel(mTimerHandle);
+ nanoapp_testing::sendMessageToHost(
+ nanoapp_testing::MessageType::kContinue);
+ } else {
+ mPriorHostTime = currentHostTime;
+ }
+
+ --mRemainingIterations;
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to verify increasing time");
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/estimated_host_time_test.h b/apps/chqts/src/general_test/estimated_host_time_test.h
new file mode 100644
index 0000000..58659ea
--- /dev/null
+++ b/apps/chqts/src/general_test/estimated_host_time_test.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or aggreed 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 _GTS_NANOAPPS_GENERAL_TEST_ESTIMATED_HOST_TIME_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_ESTIMATED_HOST_TIME_TEST_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+namespace general_test {
+
+/*
+ * Verify estimated host time from nanoapp.
+ *
+ * Fundamentally, there are two phases to this test:
+ * 1) Verify that time does increase at some point
+ * 2) Verify that AP time is "close" to what nanoapp can get
+ *
+ * Protocol:
+ * host to app: ESITMATED_HOST_TIME, no data
+ * app to host: CONTINUE
+ * host to app: CONTINUE, 64-bit time
+ * app to host: SUCCESS
+ */
+class EstimatedHostTimeTest : public Test {
+ public:
+ EstimatedHostTimeTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ void verifyIncreasingTime();
+
+ uint32_t mTimerHandle;
+ uint32_t mRemainingIterations;
+ uint64_t mPriorHostTime;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_ESTIMATED_HOST_TIME_TEST_H_
diff --git a/apps/chqts/src/general_test/event_between_apps_test.cc b/apps/chqts/src/general_test/event_between_apps_test.cc
new file mode 100644
index 0000000..b79fe47
--- /dev/null
+++ b/apps/chqts/src/general_test/event_between_apps_test.cc
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/event_between_apps_test.h>
+
+#include <general_test/nanoapp_info.h>
+
+#include <cstddef>
+
+#include <shared/abort.h>
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+// Arbitrary, just to confirm our data is properly sent.
+const uint32_t EventBetweenApps0::kMagic = UINT32_C(0x51501984);
+
+EventBetweenApps0::EventBetweenApps0()
+ : Test(CHRE_API_VERSION_1_0), mContinueCount(0) {
+}
+
+void EventBetweenApps0::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "Initial message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ NanoappInfo info;
+ info.sendToHost();
+}
+
+void EventBetweenApps0::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ uint32_t app1InstanceId;
+ const void *message =
+ getMessageDataFromHostEvent(senderInstanceId, eventType, eventData,
+ MessageType::kContinue,
+ sizeof(app1InstanceId));
+ if (mContinueCount > 0) {
+ sendFatalFailureToHost("Multiple kContinue messages sent");
+ }
+
+ mContinueCount++;
+ nanoapp_testing::memcpy(&app1InstanceId, message, sizeof(app1InstanceId));
+ nanoapp_testing::littleEndianToHost(&app1InstanceId);
+ // It's safe to strip the 'const' because we're using nullptr for our
+ // free callback.
+ uint32_t *sendData = const_cast<uint32_t*>(&kMagic);
+ // Send an event to app1. Note since app1 is on the same system, there are
+ // no endian concerns for our sendData.
+ chreSendEvent(kEventType, sendData, nullptr, app1InstanceId);
+}
+
+EventBetweenApps1::EventBetweenApps1()
+ : Test(CHRE_API_VERSION_1_0)
+ , mApp0InstanceId(CHRE_INSTANCE_ID)
+ , mReceivedInstanceId(CHRE_INSTANCE_ID) {
+}
+
+void EventBetweenApps1::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "Initial message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ NanoappInfo appInfo;
+ appInfo.sendToHost();
+}
+
+void EventBetweenApps1::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ if (eventType == CHRE_EVENT_MESSAGE_FROM_HOST) {
+ const void *message =
+ getMessageDataFromHostEvent(senderInstanceId, eventType, eventData,
+ MessageType::kContinue,
+ sizeof(mApp0InstanceId));
+ // We expect kContinue once, with the app0's instance ID as data.
+ if (mApp0InstanceId != CHRE_INSTANCE_ID) {
+ // We know app0's instance ID can't be CHRE_INSTANCE_ID, otherwise
+ // we would have aborted this test in commonInit().
+ sendFatalFailureToHost("Multiple kContinue messages from host.");
+ }
+ nanoapp_testing::memcpy(&mApp0InstanceId, message,
+ sizeof(mApp0InstanceId));
+ nanoapp_testing::littleEndianToHost(&mApp0InstanceId);
+
+ } else if (eventType == EventBetweenApps0::kEventType) {
+ if (mReceivedInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("Multiple messages from other nanoapp.");
+ }
+ if (senderInstanceId == CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("Received event from other nanoapp with "
+ "CHRE_INSTANCE_ID for sender");
+ }
+ mReceivedInstanceId = senderInstanceId;
+ uint32_t magic;
+ nanoapp_testing::memcpy(&magic, eventData, sizeof(magic));
+ if (magic != EventBetweenApps0::kMagic) {
+ sendFatalFailureToHost("Got incorrect magic data: ", &magic);
+ }
+
+ } else {
+ unexpectedEvent(eventType);
+ }
+
+ if ((mApp0InstanceId != CHRE_INSTANCE_ID)
+ && (mReceivedInstanceId != CHRE_INSTANCE_ID)) {
+ if (mApp0InstanceId == mReceivedInstanceId) {
+ sendSuccessToHost();
+ } else {
+ sendFatalFailureToHost("Got bad sender instance ID for nanoapp "
+ "event: ", &mReceivedInstanceId);
+ }
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/event_between_apps_test.h b/apps/chqts/src/general_test/event_between_apps_test.h
new file mode 100644
index 0000000..a736ff6
--- /dev/null
+++ b/apps/chqts/src/general_test/event_between_apps_test.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_EVENT_BETWEEN_APPS_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_EVENT_BETWEEN_APPS_TEST_H_
+
+#include <general_test/test.h>
+#include <shared/send_message.h>
+
+namespace general_test {
+
+/**
+ * Send CHRE event to another nanoapp.
+ *
+ * Protocol:
+ * This is nanoapp app0. This test also involves nanoapp app1.
+ * All data to/from Host is in little endian.
+ *
+ * Host to app0: kEventBetweenApps0, no data
+ * app0 to Host: kContinue, 64-bit app ID, 32-bit instance ID
+ * Host to app0: kContinue, app1's 32-bit instance ID
+ * app0 to app1: kEventType, kMagic
+ */
+class EventBetweenApps0 : public Test {
+ public:
+ EventBetweenApps0();
+
+ // Arbitrary as long as it's different from
+ // CHRE_EVENT_MESSAGE_FROM_HOST (which this value assures us).
+ static constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
+
+ // NOTE: This is not constexpr, so we have storage for it.
+ static const uint32_t kMagic;
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ int mContinueCount;
+};
+
+/**
+ * Receive CHRE event from another nanopp.
+ *
+ * Protocol:
+ * This is nanoapp app1. This test also involves nanoapp app0.
+ * All data to/from Host is in little endian.
+ *
+ * Host to app1: kEventBetweenApps1, no data
+ * app1 to Host: kContinue, 64-bit app ID, 32-bit instance ID
+ * [NOTE: Next two event can happen in any order]
+ * Host to app1: kContinue, app0's 32-bit instance ID
+ * app0 to app1: kEventType, EventBetweenApps1::kMagic
+ * app1 to Host: kSuccess, no data
+ */
+class EventBetweenApps1 : public Test {
+ public:
+ EventBetweenApps1();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ uint32_t mApp0InstanceId;
+ uint32_t mReceivedInstanceId;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_EVENT_BETWEEN_APPS_TEST_H_
diff --git a/apps/chqts/src/general_test/get_time_test.cc b/apps/chqts/src/general_test/get_time_test.cc
new file mode 100644
index 0000000..943c5af
--- /dev/null
+++ b/apps/chqts/src/general_test/get_time_test.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/get_time_test.h>
+
+#include <cstddef>
+
+#include <shared/abort.h>
+#include <shared/nano_endian.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+
+namespace general_test {
+
+GetTimeTest::GetTimeTest()
+ : Test(CHRE_API_VERSION_1_0), mContinueCount(0) {
+}
+
+void GetTimeTest::setUp(uint32_t messageSize, const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "GetTime message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ uint64_t firstTime = chreGetTime();
+ if (firstTime == UINT64_C(0)) {
+ sendFatalFailureToHost("chreGetTime() gave 0 well after system boot.");
+ }
+
+ uint64_t baseTime = firstTime;
+ uint64_t nextTime;
+ for (size_t i = 0; i < 10; i++) {
+ nextTime = chreGetTime();
+ // We don't require this to have increased, because maybe we're
+ // on a relatively fast processor, or have a low resolution clock.
+ if (nextTime < baseTime) {
+ sendFatalFailureToHost(
+ "chreGetTime() is not monotonically increasing");
+ }
+ baseTime = nextTime;
+ }
+ // But if after ten iterations of the loop we never incremented the
+ // time, that seems highly suspicious.
+ if (nextTime == firstTime) {
+ sendFatalFailureToHost("chreGetTime() is not increasing.");
+ }
+
+ nanoapp_testing::hostToLittleEndian(&nextTime);
+ sendMessageToHost(MessageType::kContinue, &nextTime, sizeof(nextTime));
+
+ // Now we'll wait to get a 'continue' from the host.
+}
+
+void GetTimeTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ // We ignore the return value, since we expect no data.
+ getMessageDataFromHostEvent(senderInstanceId, eventType, eventData,
+ MessageType::kContinue, 0);
+ if (mContinueCount > 0) {
+ sendFatalFailureToHost("Multiple kContinue messages sent");
+ }
+
+ mContinueCount++;
+ uint64_t time = chreGetTime();
+ nanoapp_testing::hostToLittleEndian(&time);
+ sendMessageToHost(MessageType::kContinue, &time, sizeof(time));
+ // We do nothing else in the CHRE. It's up to the Host to declare
+ // if we've passed.
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/get_time_test.h b/apps/chqts/src/general_test/get_time_test.h
new file mode 100644
index 0000000..3af6ece
--- /dev/null
+++ b/apps/chqts/src/general_test/get_time_test.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_GET_TIME_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_GET_TIME_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Checks that chreGetTime() is reasonable.
+ *
+ * We check that it's monotonically increasing, and mostly in line with
+ * the application processor's notion of time passing.
+ *
+ * Protocol:
+ * Host: kGetTimeTest, no data
+ * Nanoapp: kContinue, 64-bit timestamp (little endian)
+ * [2.5 second pause]
+ * Host: kContinue, no data
+ * Nanoapp: kContinue, 64-bit timestamp (little endian)
+ * [Host declares pass]
+ */
+class GetTimeTest : public Test {
+ public:
+ GetTimeTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ int mContinueCount;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_GET_TIME_TEST_H_
diff --git a/apps/chqts/src/general_test/gnss_capabilities_test.cc b/apps/chqts/src/general_test/gnss_capabilities_test.cc
new file mode 100644
index 0000000..93e21c5
--- /dev/null
+++ b/apps/chqts/src/general_test/gnss_capabilities_test.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/gnss_capabilities_test.h>
+
+#include <chre.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+GnssCapabilitiesTest::GnssCapabilitiesTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void GnssCapabilitiesTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Expected 0 byte message, got more bytes:", &messageSize);
+ } else {
+ uint32_t allCapabilities = CHRE_GNSS_CAPABILITIES_NONE;
+
+ if (mApiVersion >= CHRE_API_VERSION_1_1) {
+ allCapabilities |= CHRE_GNSS_CAPABILITIES_LOCATION
+ | CHRE_GNSS_CAPABILITIES_MEASUREMENTS;
+ }
+
+ // Call the new API
+ uint32_t capabilities = chreGnssGetCapabilities();
+
+ // Clear out known capabilities, any remaining are unknown
+ if ((capabilities & ~allCapabilities) != 0) {
+ if (mApiVersion > CHRE_API_VERSION_1_1) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "New version with unknown capabilities encountered:",
+ &capabilities);
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received unexpected capabilities:", &capabilities);
+ }
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+void GnssCapabilitiesTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void * /* eventData */) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/gnss_capabilities_test.h b/apps/chqts/src/general_test/gnss_capabilities_test.h
new file mode 100644
index 0000000..21c89d7
--- /dev/null
+++ b/apps/chqts/src/general_test/gnss_capabilities_test.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_GNSS_CAPABILITIES_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_GNSS_CAPABILITIES_TEST_H_
+
+#include <cstdint>
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Confirms that GNSS capabilities exist
+ *
+ * Simple protocol
+ * Host : kGnssCapabilities, no data
+ * Nanoapp: kSuccess, no data
+ */
+class GnssCapabilitiesTest : public Test {
+ public:
+ GnssCapabilitiesTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_GNSS_CAPABILITIES_TEST_H_
diff --git a/apps/chqts/src/general_test/heap_alloc_stress_test.cc b/apps/chqts/src/general_test/heap_alloc_stress_test.cc
new file mode 100644
index 0000000..4240e4f
--- /dev/null
+++ b/apps/chqts/src/general_test/heap_alloc_stress_test.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/heap_alloc_stress_test.h>
+
+#include <cstddef>
+
+#include <general_test/test_names.h>
+#include <shared/abort.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFailureToHost;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+static void tryAbsurdMalloc(uint32_t hugeSize) {
+ void *ptr = chreHeapAlloc(hugeSize);
+ if (ptr != NULL) {
+ sendFailureToHost("chreHeapAlloc claimed allocation of huge size ",
+ &hugeSize);
+ chreHeapFree(ptr);
+ nanoapp_testing::abort();
+ }
+}
+
+HeapAllocStressTest::HeapAllocStressTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void HeapAllocStressTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "HeapAllocStress message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ // 1GB should be absurd on any CHRE implementation we anticipate for a
+ // while.
+ tryAbsurdMalloc(UINT32_C(0x40000000));
+
+ // Let's also make sure there's nothing treating this as signed behind
+ // the scenes and breaking things.
+ tryAbsurdMalloc(UINT32_C(-16));
+
+ // Since NULL is a valid response to chreHeapAlloc(), chreHeapFree()
+ // must accept it as an argument.
+ chreHeapFree(NULL);
+
+ // We do not test chreHeapFree() with invalid pointers, because that's
+ // an error by the caller, and there's no requirement for the CHRE
+ // implementation to handle it nicely.
+
+
+ // Now let's exhaust the heap, and make sure it properly frees up to allow
+ // things to be allocated again.
+ constexpr size_t kNumPtrs = 256;
+ void **ptrs = reinterpret_cast<void**>(
+ chreHeapAlloc(kNumPtrs * sizeof(void*)));
+ if (ptrs == NULL) {
+ // Oh, the irony.
+ sendFatalFailureToHost(
+ "Insufficient free heap to test heap exhaustion.");
+ }
+
+ size_t index;
+ uint32_t last_alloc_size = 1024 * 1024 * 256;
+ for (index = 0; (index < kNumPtrs); index++) {
+ uint32_t curr_alloc_size = last_alloc_size;
+ void *ptr = chreHeapAlloc(curr_alloc_size);
+ while (ptr == NULL) {
+ curr_alloc_size /= 2;
+ if (curr_alloc_size < 16) {
+ break;
+ }
+ ptr = chreHeapAlloc(curr_alloc_size);
+ }
+ if (ptr == NULL) {
+ break;
+ }
+ last_alloc_size = curr_alloc_size;
+ ptrs[index] = ptr;
+ }
+ if (index == 0) {
+ sendFatalFailureToHost(
+ "Failed to allocate anything for heap exhaustion");
+ }
+
+ // We should be able to free this allocation, and then obtain it again.
+ index--;
+ chreHeapFree(ptrs[index]);
+ ptrs[index] = chreHeapAlloc(last_alloc_size);
+ if (ptrs[index] == NULL) {
+ sendFatalFailureToHost(
+ "After exhausting heap and then free'ing, unable to alloc "
+ "again for size ", &last_alloc_size);
+ }
+
+ // Everything's good, let's free up our memory.
+ for (size_t i = 0; i <= index; i++) {
+ chreHeapFree(ptrs[i]);
+ }
+ chreHeapFree(ptrs);
+
+ sendSuccessToHost();
+}
+
+void HeapAllocStressTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void* /* eventData */) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/heap_alloc_stress_test.h b/apps/chqts/src/general_test/heap_alloc_stress_test.h
new file mode 100644
index 0000000..1aa5c5e
--- /dev/null
+++ b/apps/chqts/src/general_test/heap_alloc_stress_test.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_HEAP_ALLOC_STRESS_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_HEAP_ALLOC_STRESS_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Stresses the heap alloc implementation.
+ *
+ * We request extreme allocation sizes, exhaust the heap, and make
+ * sure things continue to work.
+ *
+ * Simple Protocol.
+ */
+class HeapAllocStressTest : public Test {
+ public:
+ HeapAllocStressTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_HEAP_ALLOC_STRESS_TEST_H_
diff --git a/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc b/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc
new file mode 100644
index 0000000..9828a3a
--- /dev/null
+++ b/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/heap_exhaustion_stability_test.h>
+
+#include <cinttypes>
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFailureToHost;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * We set an "exhaustion timer" to go off when we're ready for the test to
+ * be over. Then we exhaust the heap.
+ *
+ * We try a series of chre*() calls with the heap exhausted. For many of
+ * these calls, we're less interested in them succeeding than in the system
+ * just not crashing. However, for things which claim success, we require
+ * they succeed.
+ *
+ * To track the things which claim success, we have two "stages", kTimerStage
+ * and kEventStage.
+ *
+ * When the "exhaustion timer" fires, we free our memory, and make sure our
+ * stages have all succeeded.
+ */
+
+namespace general_test {
+
+// Note: We use pointers to the 'duration' to serve as our timer event data.
+// Thus we make this "static const" instead of "constexpr", as we expect
+// them to have backing memory.
+
+// 5 seconds
+static const uint64_t kExhaustionDuration = UINT64_C(5000000000);
+// 10 milliseconds
+static const uint64_t kShortDuration = UINT64_C(10000000);
+
+constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
+
+constexpr uint32_t kTimerStage = 0;
+constexpr uint32_t kEventStage = 1;
+
+void HeapExhaustionStabilityTest::exhaustHeap() {
+ constexpr size_t kNumPtrs = 256;
+ mExhaustionPtrs = reinterpret_cast<void**>(
+ chreHeapAlloc(kNumPtrs * sizeof(*mExhaustionPtrs)));
+ if (mExhaustionPtrs == nullptr) {
+ // Oh, the irony.
+ sendFatalFailureToHost(
+ "Insufficient free heap to exhaust the heap.");
+ }
+
+ // We start by trying to allocate massive sizes (256MB to start).
+ // When we're not able to allocate massive sizes, we cut the size in
+ // half. We repeat until we've either done kNumPtrs allocations,
+ // or reduced our allocation size below 16 bytes.
+ uint32_t allocSize = 1024 * 1024 * 256;
+ for (mExhaustionPtrCount = 0;
+ mExhaustionPtrCount < kNumPtrs;
+ mExhaustionPtrCount++) {
+ void *ptr = chreHeapAlloc(allocSize);
+ while (ptr == nullptr) {
+ allocSize /= 2;
+ if (allocSize < 4) {
+ break;
+ }
+ ptr = chreHeapAlloc(allocSize);
+ }
+ if (ptr == nullptr) {
+ break;
+ }
+ mExhaustionPtrs[mExhaustionPtrCount] = ptr;
+ }
+ if (mExhaustionPtrCount == 0) {
+ sendFatalFailureToHost(
+ "Failed to allocate anything for heap exhaustion");
+ }
+}
+
+void HeapExhaustionStabilityTest::freeMemory() {
+ for (size_t i = 0; i < mExhaustionPtrCount; i++) {
+ chreHeapFree(mExhaustionPtrs[i]);
+ }
+ chreHeapFree(mExhaustionPtrs);
+}
+
+HeapExhaustionStabilityTest::HeapExhaustionStabilityTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void HeapExhaustionStabilityTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ mInMethod = true;
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "HeapExhaustionStability message expects 0 additional bytes, "
+ "got ", &messageSize);
+ }
+
+ if (chreTimerSet(kExhaustionDuration, &kExhaustionDuration, true) ==
+ CHRE_TIMER_INVALID) {
+ sendFatalFailureToHost("Unable to set initial timer");
+ }
+
+ exhaustHeap();
+
+ testLog(messageSize);
+ testSetTimer();
+ testSendEvent();
+ testSensor();
+ // TODO(b/32114261): This method currently doesn't test anything.
+ testMessageToHost();
+
+ // Some of the above 'test' methods might trigger events. Even if they
+ // don't, the kExhaustionDuration timer we set earlier should trigger
+ // eventually, and that's when we'll conclude the test.
+ mInMethod = false;
+}
+
+void HeapExhaustionStabilityTest::testLog(uint32_t zero) {
+ // This doesn't need to land in the log (and indeed we have no automated
+ // means of checking that right now anyway), but it shouldn't crash.
+ chreLog(CHRE_LOG_INFO, "Test log %s, zero: %" PRId32, "message", zero);
+}
+
+void HeapExhaustionStabilityTest::testSetTimer() {
+ if (chreTimerSet(kShortDuration, &kShortDuration, true) !=
+ CHRE_TIMER_INVALID) {
+ // CHRE claims we were able to set this timer. We'll
+ // mark this stage a success when the timer fires.
+ } else {
+ // CHRE was not able to set this timer. That's okay, since we're
+ // out of heap. We'll mark this stage as a success.
+ markSuccess(kTimerStage);
+ }
+}
+
+void HeapExhaustionStabilityTest::testSendEvent() {
+ if (chreSendEvent(kEventType, nullptr, nullptr, chreGetInstanceId())) {
+ // CHRE claims we were able to send this event. We'll make
+ // this stage a success when the event is received.
+ } else {
+ // CHRE was not able to send this event. That's okay, since we're
+ // out of heap. We'll mark this stage as a success.
+ markSuccess(kEventStage);
+ }
+}
+
+void HeapExhaustionStabilityTest::testSensor() {
+ static constexpr uint8_t kSensorType = CHRE_SENSOR_TYPE_ACCELEROMETER;
+ uint32_t handle;
+ if (!chreSensorFindDefault(kSensorType, &handle)) {
+ // We still expect this to succeed without any heap left.
+ sendFatalFailureToHost("chreSensorFindDefault failed");
+ }
+ chreSensorInfo info;
+ if (!chreGetSensorInfo(handle, &info)) {
+ // We still expect this to succeed, since we're supplying the memory.
+ sendFatalFailureToHost("chreGetSensorInfo failed");
+ }
+ if (info.sensorType != kSensorType) {
+ sendFatalFailureToHost("Invalid sensor info provided");
+ }
+
+ chreSensorSamplingStatus samplingStatus;
+ if (!chreGetSensorSamplingStatus(handle, &samplingStatus)) {
+ // We still expect this to succeed, since we're supplying the memory.
+ sendFatalFailureToHost("chreGetSensorSamplingStatus failed");
+ }
+
+ // TODO: We might want to consider calling chreSensorConfigure() for a
+ // more robust test of this. However, we don't expect sensor events to
+ // necessarily get delivered under heap exhaustion, so it's unclear
+ // how we'd make sure we eventually tell the system we're DONE with
+ // the sensor (setting a timer isn't assured to work at this point).
+}
+
+void HeapExhaustionStabilityTest::testMessageToHost() {
+ // TODO(b/32114261): We should invoke sendMessageToHost() here.
+ // Unfortunately, this is a real pain due to this bug, as we need to
+ // duplicate much of the contents of shared/send_message.cc to
+ // add the hack-around bytes (the method itself will internally
+ // fail if the send attempt fails, but we're in a state where
+ // we'll allow a failed send attempt). Or we need to take this
+ // off of the General test infrastructure to allow raw byte sending.
+ // That seems not worth the effort for NYC, and just easier to wait
+ // until OMC when this is much easier to implement.
+ // OMC Note: When we've fixed this bug, and added a send here, we'll
+ // need to make this no longer Simple protocol, since this nanoapp
+ // might send a message.
+}
+
+void HeapExhaustionStabilityTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ if (mInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ mInMethod = true;
+
+ if (eventType == CHRE_EVENT_TIMER) {
+ handleTimer(senderInstanceId, eventData);
+ } else if (eventType == kEventType) {
+ handleSelfEvent(senderInstanceId, eventData);
+ } else {
+ unexpectedEvent(eventType);
+ }
+ mInMethod = false;
+}
+
+void HeapExhaustionStabilityTest::handleTimer(uint32_t senderInstanceId,
+ const void *eventData) {
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("handleTimer with unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventData == &kShortDuration) {
+ // This was the timer we triggered while the heap was exhausted.
+ markSuccess(kTimerStage);
+
+ } else if (eventData == &kExhaustionDuration) {
+ // Our test is done.
+ freeMemory();
+ if (mFinishedBitmask != kAllFinished) {
+ sendFatalFailureToHost("Done with test, but not all stages "
+ "done.", &mFinishedBitmask);
+ }
+ sendSuccessToHost();
+
+ } else {
+ sendFatalFailureToHost("Unexpected timer eventData");
+ }
+}
+
+void HeapExhaustionStabilityTest::handleSelfEvent(uint32_t senderInstanceId,
+ const void *eventData) {
+ if (senderInstanceId != chreGetInstanceId()) {
+ sendFatalFailureToHost("handleSelfEvent with unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventData != nullptr) {
+ sendFatalFailureToHost("Unexpected data for event to self");
+ }
+ markSuccess(kEventStage);
+}
+
+void HeapExhaustionStabilityTest::markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage", &stage);
+ }
+ if ((mFinishedBitmask & finishedBit) != 0) {
+ // This could be when a timer/event method returned 'false', but
+ // actually did end up triggering an event.
+ sendFatalFailureToHost("markSuccess stage triggered twice", &stage);
+ }
+ mFinishedBitmask |= finishedBit;
+ // Note that unlike many markSuccess() implementations, we do not
+ // check against kAllFinished here. That happens when the
+ // timer for kExhaustionDuration fires.
+}
+
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/heap_exhaustion_stability_test.h b/apps/chqts/src/general_test/heap_exhaustion_stability_test.h
new file mode 100644
index 0000000..130f5f4
--- /dev/null
+++ b/apps/chqts/src/general_test/heap_exhaustion_stability_test.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_HEAP_EXHAUSTION_STABILITY_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_HEAP_EXHAUSTION_STABILITY_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Exhaust the heap and confirm the platform remains stable when trying
+ * various things.
+ *
+ * We don't require everything to be available when the heap is exhausted,
+ * but we do require the system is honest about its capabilities, and doesn't
+ * crash.
+ *
+ * Simple Protocol.
+ */
+class HeapExhaustionStabilityTest : public Test {
+ public:
+ HeapExhaustionStabilityTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ bool mInMethod;
+
+ void **mExhaustionPtrs;
+ size_t mExhaustionPtrCount;
+
+ static constexpr size_t kStageCount = 2;
+ static constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
+ uint32_t mFinishedBitmask;
+
+ void exhaustHeap();
+ void freeMemory();
+
+ void testLog(uint32_t zero);
+ void testSetTimer();
+ void testSendEvent();
+ void testSensor();
+ // TODO(b/32114261): This method currently doesn't test anything.
+ void testMessageToHost();
+
+ void handleTimer(uint32_t senderInstanceId, const void *eventData);
+ void handleSelfEvent(uint32_t senderInstanceId, const void *eventData);
+
+ void markSuccess(uint32_t stage);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_HEAP_EXHAUSTION_STABILITY_TEST_H_
diff --git a/apps/chqts/src/general_test/hello_world_test.cc b/apps/chqts/src/general_test/hello_world_test.cc
new file mode 100644
index 0000000..b476efd
--- /dev/null
+++ b/apps/chqts/src/general_test/hello_world_test.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/hello_world_test.h>
+
+#include <shared/send_message.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+HelloWorldTest::HelloWorldTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void HelloWorldTest::setUp(uint32_t messageSize, const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost("Expected 0 byte message, got more bytes:",
+ &messageSize);
+ } else {
+ sendSuccessToHost();
+ }
+}
+
+void HelloWorldTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void* /* eventData */) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/hello_world_test.h b/apps/chqts/src/general_test/hello_world_test.h
new file mode 100644
index 0000000..20c1610
--- /dev/null
+++ b/apps/chqts/src/general_test/hello_world_test.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_HELLO_WORLD_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_HELLO_WORLD_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Confirms a simple message from the host makes it to the app.
+ *
+ * Simple Protocol. That is:
+ * Host: kHelloWorld, no data
+ * Nanoapp: kSuccess, no data
+ */
+class HelloWorldTest : public Test {
+ public:
+ HelloWorldTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_HELLO_WORLD_TEST_H_
diff --git a/apps/chqts/src/general_test/logging_sanity_test.cc b/apps/chqts/src/general_test/logging_sanity_test.cc
new file mode 100644
index 0000000..ddbd03d
--- /dev/null
+++ b/apps/chqts/src/general_test/logging_sanity_test.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/logging_sanity_test.h>
+
+#include <cstddef>
+#include <limits>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+LoggingSanityTest::LoggingSanityTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void LoggingSanityTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "LoggingSanity message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ // Test each warning level.
+ chreLog(CHRE_LOG_ERROR, "Level: Error");
+ chreLog(CHRE_LOG_WARN, "Level: Warn");
+ chreLog(CHRE_LOG_INFO, "Level: Info");
+ chreLog(CHRE_LOG_DEBUG, "Level: Debug");
+
+ // Now we'll just test everything with INFO.
+ constexpr chreLogLevel kInfo = CHRE_LOG_INFO;
+
+ // Empty string
+ chreLog(kInfo, "");
+
+ // Try up through 10 arguments
+ chreLog(kInfo, "%d", 1);
+ chreLog(kInfo, "%d %d", 1, 2);
+ chreLog(kInfo, "%d %d %d", 1, 2, 3);
+ chreLog(kInfo, "%d %d %d %d", 1, 2, 3, 4);
+ chreLog(kInfo, "%d %d %d %d %d", 1, 2, 3, 4, 5);
+ chreLog(kInfo, "%d %d %d %d %d %d", 1, 2, 3, 4, 5, 6);
+ chreLog(kInfo, "%d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7);
+ chreLog(kInfo, "%d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8);
+ chreLog(kInfo, "%d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ chreLog(kInfo, "%d %d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10);
+
+ // Various 'int' specifiers. The value of the "%u" output depends on the
+ // size of 'int' on this machine.
+ chreLog(kInfo, "%d %u 0%o 0x%x 0x%X", -1, -1, 01234, 0xF4E, 0xF4E);
+
+ // Generic testing of all specific types. The format string is the same
+ // as the chreLog() above us, just using the appropriate prefix for each.
+ // We also use the min() value for all these signed types, assuring that
+ // we'll get different %d vs %u output, and we'll get letters within our
+ // %x and %X output.
+#define INT_TYPES(kPrefix, type) \
+ { \
+ type value = std::numeric_limits<type>::min(); \
+ chreLog(kInfo, "%" kPrefix "d %" kPrefix "u 0%" kPrefix "o 0x%" \
+ kPrefix "x 0x%" kPrefix "X", value, value, value, value, \
+ value); \
+ }
+
+ INT_TYPES("hh", char);
+ INT_TYPES("h", short);
+ INT_TYPES("l", long);
+ INT_TYPES("ll", long long);
+ INT_TYPES("z", size_t);
+ INT_TYPES("t", ptrdiff_t);
+
+ float f = 12.34f;
+ // Other required formats, including escaping the '%'.
+ chreLog(kInfo, "%% %f %c %s %p", f, '?', "str", &f);
+
+
+ // OPTIONAL specifiers. See chreLog() API documentation for extensive
+ // discussion of what OPTIONAL means.
+ // <width> and '-'
+ chreLog(kInfo, "(%5s) (%-5s) (%5d) (%-5d)", "str", "str", 10, 10);
+ // '+'
+ chreLog(kInfo, "(%+d) (%+d) (%+f) (%+f)", -5, 5, -5.f, 5.f);
+ // ' '
+ chreLog(kInfo, "(% d) (% d) (% f) (% f)", -5, 5, -5.f, 5.f);
+ // '#'
+ chreLog(kInfo, "%#o %#x %#X %#f", 8, 15, 15, 1.f);
+ // '0' padding
+ chreLog(kInfo, "%08d 0x%04x", 123, 0xF);
+ // '.'<precision>
+ chreLog(kInfo, "%.3d %.3d %.3f %.3f %.3s", 12, 1234, 1.5, 1.0625, "abcdef");
+
+ // TODO: In some future Android release, when chreLog() is required to
+ // output to logcat, we'll just send a Continue to the Host and have
+ // the Host verify this output. But for Android N, we leave it to
+ // the test runner to manually verify.
+ sendSuccessToHost();
+}
+
+void LoggingSanityTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/logging_sanity_test.h b/apps/chqts/src/general_test/logging_sanity_test.h
new file mode 100644
index 0000000..9ce49bb
--- /dev/null
+++ b/apps/chqts/src/general_test/logging_sanity_test.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_LOGGING_SANITY_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_LOGGING_SANITY_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Invokes chreLog() in a variety of ways.
+ *
+ * Unfortunately, we're unable to automatically check that this works
+ * correctly. At the very least, we can confirm in an automated manner
+ * that this doesn't crash. A diligent tester will check where the
+ * chreLog() messages go for this platform to confirm the contents look
+ * correct.
+ *
+ * Simple protocol.
+ */
+class LoggingSanityTest : public Test {
+ public:
+ LoggingSanityTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_LOGGING_SANITY_TEST_H_
diff --git a/apps/chqts/src/general_test/nanoapp_info.cc b/apps/chqts/src/general_test/nanoapp_info.cc
new file mode 100644
index 0000000..6645d52
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/nanoapp_info.h>
+
+#include <shared/nano_endian.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+struct AppInfo {
+ uint64_t appId;
+ uint32_t instanceId;
+} __attribute__((packed));
+
+NanoappInfo::NanoappInfo()
+ : mAppId(chreGetAppId()), mInstanceId(chreGetInstanceId()) {
+ if (mInstanceId == CHRE_INSTANCE_ID) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Given CHRE_INSTANCE_ID for my instance ID");
+ }
+}
+
+void NanoappInfo::sendToHost() {
+ AppInfo info;
+ info.appId = mAppId;
+ info.instanceId = mInstanceId;
+
+ nanoapp_testing::hostToLittleEndian(&info.appId);
+ nanoapp_testing::hostToLittleEndian(&info.instanceId);
+
+ sendMessageToHost(nanoapp_testing::MessageType::kContinue,
+ &info, sizeof(info));
+}
+
+bool NanoappInfo::validate(uint64_t appId, uint32_t instanceId) {
+ bool result = true;
+ if (appId != mAppId) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "app IDs do not match");
+ result = false;
+ } else if (instanceId != mInstanceId) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "instance IDs do not match");
+ result = false;
+ }
+
+ return result;
+}
+
+bool NanoappInfo::queryByAppId(struct chreNanoappInfo *info) {
+ bool result = chreGetNanoappInfoByAppId(mAppId, info);
+
+ if (!result) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to get nanoapp info by app ID");
+ }
+
+ return result;
+}
+
+bool NanoappInfo::queryByInstanceId(struct chreNanoappInfo *info) {
+ bool result = chreGetNanoappInfoByInstanceId(mInstanceId, info);
+
+ if (!result) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to get nanoapp info by instance ID");
+ }
+
+ return result;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/nanoapp_info.h b/apps/chqts/src/general_test/nanoapp_info.h
new file mode 100644
index 0000000..cc8c643
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_H_
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace general_test {
+
+class NanoappInfo {
+ public:
+ NanoappInfo();
+
+ /**
+ * Send the nanoapp information to the host
+ */
+ void sendToHost();
+
+ /**
+ * Validate that provided values match gathered data.
+ *
+ * @param appId The app ID to validate against
+ * @param instanceId The instance ID to validate against
+ * @return true if provided values match data, false otherwise
+ */
+ bool validate(uint64_t appId, uint32_t instanceId);
+
+ /**
+ * Query system for running nanoapp information by gathered application Id
+ *
+ * @param info The chreNanoappInfo to populate with query results
+ * @return true If nanoapp with application id is running on system
+ */
+ bool queryByAppId(struct chreNanoappInfo *info);
+
+ /**
+ * Query system for running nanoapp information by gathered instance Id
+ *
+ * @param info The chreNanoappInfo to populate with query results
+ * @return true If nanoapp with instance id is running on system
+ */
+ bool queryByInstanceId(struct chreNanoappInfo *info);
+
+ private:
+ uint64_t mAppId;
+ uint32_t mInstanceId;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_H_
diff --git a/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.cc b/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.cc
new file mode 100644
index 0000000..06c4828
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/nanoapp_info_by_app_id_test.h>
+
+#include <general_test/running_info.h>
+
+#include <shared/send_message.h>
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+NanoappInfoByAppIdTest::NanoappInfoByAppIdTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void NanoappInfoByAppIdTest::setUp(uint32_t /* messageSize */,
+ const void * /* message */) {
+ nanoapp_testing::sendMessageToHost(nanoapp_testing::MessageType::kContinue);
+}
+
+void NanoappInfoByAppIdTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ if (senderInstanceId == CHRE_INSTANCE_ID) {
+ uint32_t appVersion;
+ const void *message =
+ getMessageDataFromHostEvent(senderInstanceId, eventType,
+ eventData,
+ nanoapp_testing::MessageType::kContinue,
+ sizeof(&appVersion));
+
+ nanoapp_testing::memcpy(&appVersion, message, sizeof(appVersion));
+ nanoapp_testing::littleEndianToHost(&appVersion);
+
+ RunningInfo runningInfo;
+
+ if (runningInfo.queryByAppId() && runningInfo.validate(appVersion)) {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.h b/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.h
new file mode 100644
index 0000000..181463a
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_by_app_id_test.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_APP_ID_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_APP_ID_TEST_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+/**
+ * Verify chreGetNanoappInfoByAppId
+ *
+ * Simple Protocol
+ */
+namespace general_test {
+
+class NanoappInfoByAppIdTest : public Test {
+ public:
+ NanoappInfoByAppIdTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_APP_ID_TEST_H_
diff --git a/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.cc b/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.cc
new file mode 100644
index 0000000..9743912
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/nanoapp_info_by_instance_id_test.h>
+
+#include <general_test/running_info.h>
+
+#include <shared/send_message.h>
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+NanoappInfoByInstanceIdTest::NanoappInfoByInstanceIdTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void NanoappInfoByInstanceIdTest::setUp(uint32_t /* messageSize */,
+ const void * /* message */) {
+ nanoapp_testing::sendMessageToHost(nanoapp_testing::MessageType::kContinue);
+}
+
+void NanoappInfoByInstanceIdTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ if (senderInstanceId == CHRE_INSTANCE_ID) {
+ uint32_t appVersion;
+ const void *message =
+ getMessageDataFromHostEvent(senderInstanceId, eventType,
+ eventData,
+ nanoapp_testing::MessageType::kContinue,
+ sizeof(&appVersion));
+
+ nanoapp_testing::memcpy(&appVersion, message, sizeof(appVersion));
+ nanoapp_testing::littleEndianToHost(&appVersion);
+
+ RunningInfo runningInfo;
+
+ if (runningInfo.queryByInstanceId() && runningInfo.validate(appVersion)) {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.h b/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.h
new file mode 100644
index 0000000..34ee5b2
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_by_instance_id_test.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_INSTANCE_ID_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_INSTANCE_ID_TEST_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+/**
+ * Verify chreGetNanoappInfoByInstanceId
+ *
+ * Simple Protocol
+ */
+namespace general_test {
+
+class NanoappInfoByInstanceIdTest : public Test {
+ public:
+ NanoappInfoByInstanceIdTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_BY_INSTANCE_ID_TEST_H_
diff --git a/apps/chqts/src/general_test/nanoapp_info_events_test_observer.cc b/apps/chqts/src/general_test/nanoapp_info_events_test_observer.cc
new file mode 100644
index 0000000..56733de
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_events_test_observer.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/nanoapp_info_events_test_observer.h>
+
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+
+namespace general_test {
+
+NanoAppInfoEventsTestObserver::NanoAppInfoEventsTestObserver()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void NanoAppInfoEventsTestObserver::setUp(uint32_t /* messageSize */,
+ const void * /* message */) {
+ chreConfigureNanoappInfoEvents(true /* enable */);
+ nanoapp_testing::sendMessageToHost(nanoapp_testing::MessageType::kContinue);
+}
+
+void NanoAppInfoEventsTestObserver::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ if ((senderInstanceId == CHRE_INSTANCE_ID)
+ && ((eventType == CHRE_EVENT_NANOAPP_STARTED)
+ || (eventType == CHRE_EVENT_NANOAPP_STOPPED))) {
+
+ const struct chreNanoappInfo *nanoAppInfo =
+ static_cast<const struct chreNanoappInfo *>(eventData);
+
+ mStartStopHistory[mHistoryIndex].instanceId =
+ nanoAppInfo->instanceId;
+
+ mStartStopHistory[mHistoryIndex].eventType = eventType;
+ mHistoryIndex = (mHistoryIndex + 1) % kHistorySize;
+ } else if ((senderInstanceId == CHRE_INSTANCE_ID)
+ && (eventType == CHRE_EVENT_MESSAGE_FROM_HOST)) {
+ uint32_t performerInstanceId;
+
+ const void *message = getMessageDataFromHostEvent(
+ senderInstanceId, eventType, eventData,
+ nanoapp_testing::MessageType::kContinue, sizeof(performerInstanceId));
+
+ nanoapp_testing::memcpy(&performerInstanceId, message,
+ sizeof(performerInstanceId));
+ nanoapp_testing::littleEndianToHost(&performerInstanceId);
+
+ processStartStopHistory(performerInstanceId);
+ } else {
+ unexpectedEvent(eventType);
+ }
+}
+
+void NanoAppInfoEventsTestObserver::processStartStopHistory(
+ uint32_t performerInstanceId) {
+ uint32_t startCount = 0;
+ uint32_t stopCount = 0;
+ bool seenFirstEvent = false;
+ bool eventsOrdered = false;
+
+ // The oldest data (if present) is at the insertion point in the
+ // circular array (i.e. mHistoryIndex)
+ for (uint32_t i = 0; i < kHistorySize; ++i) {
+ HostActionMetadata& data =
+ mStartStopHistory[(mHistoryIndex + i) % kHistorySize];
+
+ if (data.instanceId == performerInstanceId) {
+ if (data.eventType == CHRE_EVENT_NANOAPP_STARTED) {
+ ++startCount;
+ } else {
+ ++stopCount;
+ }
+
+ if (!seenFirstEvent) {
+ eventsOrdered = (data.eventType == CHRE_EVENT_NANOAPP_STARTED);
+ seenFirstEvent = true;
+ }
+ }
+ }
+
+ if (startCount > 1) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received too many Start events");
+ } else if (startCount == 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Did not receive Start event");
+ } else if (stopCount > 1) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received too many Stop events");
+ } else if (stopCount == 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Did not receive Stop event");
+ } else if (!eventsOrdered) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Start and Stop events were not in order");
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/nanoapp_info_events_test_observer.h b/apps/chqts/src/general_test/nanoapp_info_events_test_observer.h
new file mode 100644
index 0000000..7bbcdcf
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_events_test_observer.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtian a copy of the License at
+ *
+ * http://www.apache.org/licensed/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 _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_OBSERVER_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_OBSERVER_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace general_test {
+
+/**
+ * Monitor CHRE_EVENT_NANOAPP_STARTED/CHRE_EVENT_NANOAPP_STOPPED events.
+ *
+ * This is the OBSERVER nanoapp for ContextHubNanoAppInfoEventsNanoAppTest
+ *
+ * Protocol:
+ * Host to observer: NANOAPP_INFO_EVENT, no data
+ * observer to Host: CONTINUE
+ * [Host starts performer]
+ * ...
+ * [Host stops performer]
+ * Host to observer: CONTINUE, performer's 32-bit instance ID
+ * observer to host: SUCCESS
+ */
+struct HostActionMetadata {
+ uint32_t instanceId;
+ uint16_t eventType;
+};
+
+class NanoAppInfoEventsTestObserver : public Test {
+ public:
+ NanoAppInfoEventsTestObserver();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ /**
+ * Search the Start/Stop message history looking for a Start/Stop
+ * pair from a given instance id
+ *
+ * @param performerInstanceId The instance Id to look for
+ */
+ void processStartStopHistory(uint32_t performerInstanceId);
+
+ static constexpr uint32_t kHistorySize = 8;
+ HostActionMetadata mStartStopHistory[kHistorySize];
+ uint32_t mHistoryIndex = 0;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_OBSERVER_H_
diff --git a/apps/chqts/src/general_test/nanoapp_info_events_test_performer.cc b/apps/chqts/src/general_test/nanoapp_info_events_test_performer.cc
new file mode 100644
index 0000000..86c6b9f
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_events_test_performer.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/nanoapp_info_events_test_performer.h>
+
+#include <general_test/nanoapp_info.h>
+
+#include <shared/nano_endian.h>
+
+namespace general_test {
+
+NanoAppInfoEventsTestPerformer::NanoAppInfoEventsTestPerformer()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void NanoAppInfoEventsTestPerformer::setUp(uint32_t /* messageSize */,
+ const void * /* message */) {
+ NanoappInfo info;
+ info.sendToHost();
+}
+
+void NanoAppInfoEventsTestPerformer::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventdata) {
+ // Do nothing
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/nanoapp_info_events_test_performer.h b/apps/chqts/src/general_test/nanoapp_info_events_test_performer.h
new file mode 100644
index 0000000..1fc1110
--- /dev/null
+++ b/apps/chqts/src/general_test/nanoapp_info_events_test_performer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_PERFORMER_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_PERFORMER_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+namespace general_test {
+
+/**
+ * Monitor CHRE_EVENT_NANOAPP_STARTED/CHRE_EVENT_NANOAPP_STOPPED events.
+ *
+ * This is the PERFORMER nanoapp for ContextHubNanoAppInfoEventsNanoAppTest
+ *
+ * Protocol:
+ * Host to performer: NANOAPP_INFO_EVENTS_PERFORMER, no data
+ * performer to Host: CONTINUE, 64-bit app ID, 32-bit instance ID
+ */
+class NanoAppInfoEventsTestPerformer : public Test {
+ public:
+ NanoAppInfoEventsTestPerformer();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_NANOAPP_INFO_EVENTS_TEST_PERFORMER_H_
diff --git a/apps/chqts/src/general_test/running_info.cc b/apps/chqts/src/general_test/running_info.cc
new file mode 100644
index 0000000..9410d0f
--- /dev/null
+++ b/apps/chqts/src/general_test/running_info.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/running_info.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+bool RunningInfo::queryByAppId() {
+ return mPlatformInfo.queryByAppId(&mRunningInfo);
+}
+
+bool RunningInfo::queryByInstanceId() {
+ return mPlatformInfo.queryByInstanceId(&mRunningInfo);
+}
+
+bool RunningInfo::validate(uint32_t appVersion) {
+ bool result;
+ if (mRunningInfo.version != appVersion) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Running info version does not match app version");
+ result = false;
+ } else if (mRunningInfo.appId != NANOAPP_ID) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Running info appId does not match build constant");
+ result = false;
+ } else if (mRunningInfo.version != NANOAPP_VERSION) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Running info version does not match build constant");
+ result = false;
+ } else {
+ result = mPlatformInfo.validate(mRunningInfo.appId,
+ mRunningInfo.instanceId);
+ }
+
+ return result;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/running_info.h b/apps/chqts/src/general_test/running_info.h
new file mode 100644
index 0000000..818e997
--- /dev/null
+++ b/apps/chqts/src/general_test/running_info.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_RUNNING_INFO_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_RUNNING_INFO_H_
+
+#include <general_test/nanoapp_info.h>
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace general_test {
+
+class RunningInfo {
+ public:
+ bool queryByAppId();
+ bool queryByInstanceId();
+
+ /**
+ * Validate that provided values match gathered data.
+ *
+ * @param appVersion The app version to validate against
+ * @return true if provided value matches data, false otherwise
+ */
+ bool validate(uint32_t appVersion);
+
+ private:
+ NanoappInfo mPlatformInfo;
+ struct chreNanoappInfo mRunningInfo;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_RUNNING_INFO_H_
diff --git a/apps/chqts/src/general_test/send_event_stress_test.cc b/apps/chqts/src/general_test/send_event_stress_test.cc
new file mode 100644
index 0000000..2fe49a1
--- /dev/null
+++ b/apps/chqts/src/general_test/send_event_stress_test.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/send_event_stress_test.h>
+
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * We stress the system by sending more and more events until it runs out.
+ * Then we wait for all the events to be delivered, and all the completion
+ * callbacks to be invoked.
+ */
+
+constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
+void *const kEventData = reinterpret_cast<void *>(-1);
+
+// If the system keeps claiming it can send more events, we don't let it
+// continue forever. Instead, we'll cut it off at this limit. And then
+// we'll call its bluff, and make sure that all of these events get
+// delivered. While it won't be an actual exhaustion test (we never took the
+// system down to no more events available), it will still give us confidence
+// that this CHRE can properly handle any semi-reasonable event load properly.
+// 1030 is an arbitrary number, slightly over 2^10. The hope is this
+// balances between catching incorrect behavior and the test taking too long.
+constexpr int32_t kMaxEventsToSend = INT32_C(1030);
+
+namespace general_test {
+
+bool SendEventStressTest::sInMethod = false;
+bool SendEventStressTest::sInitTime = false;
+
+int32_t SendEventStressTest::sEventsLeft = 0;
+int32_t SendEventStressTest::sCompleteCallbacksLeft = 0;
+
+SendEventStressTest::SendEventStressTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void SendEventStressTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ sInMethod = true;
+
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "SendEventStress message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ mInstanceId = chreGetInstanceId();
+
+ // When our chreSendEvent() call fails, the CHRE is allowed to
+ // directly invoke our completeCallback. We special case this
+ // with sInitTime, so we can ignore sInMethod for that case only.
+ sCompleteCallbacksLeft = 1;
+ sInitTime = true;
+
+ // We anticipate most CHREs will not reach kMaxEventsToSend.
+ while ((sEventsLeft < kMaxEventsToSend) &&
+ chreSendEvent(kEventType, kEventData, completeCallback,
+ mInstanceId)) {
+ sEventsLeft++;
+ }
+ sInitTime = false;
+
+ // We want at least 2 events for this to pretend to be an exhaustion test.
+ if (sEventsLeft < 2) {
+ sendFatalFailureToHost("Insufficient events available");
+ }
+
+ // sCompleteCallbacksLeft may be 0 or 1 at this point. We don't care.
+ // We just know we also expect all the sEventsLeft to have callbacks.
+ sCompleteCallbacksLeft += sEventsLeft;
+
+ sInMethod = false;
+}
+
+void SendEventStressTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ if (sInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ sInMethod = true;
+ if (senderInstanceId != mInstanceId) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+ sanityCheck(eventType, eventData, 0);
+
+ --sEventsLeft;
+ if (sEventsLeft < 0) {
+ sendFatalFailureToHost("Too many events delivered");
+ }
+
+ sInMethod = false;
+}
+
+void SendEventStressTest::sanityCheck(uint16_t eventType, const void *data,
+ uint32_t num) {
+ if (eventType != kEventType) {
+ unexpectedEvent(eventType);
+ }
+ if (data != kEventData) {
+ // 0: handleEvent, 1: completeCallback
+ sendFatalFailureToHost("bad event data:", &num);
+ }
+}
+
+
+void SendEventStressTest::completeCallback(uint16_t eventType, void *data) {
+ if (sInitTime) {
+ // The CHRE must be directly calling this from within
+ // chreSendEvent(), after it failed. We only allow a
+ // single one of these calls.
+ sInitTime = false;
+ sanityCheck(eventType, data, 1);
+ sCompleteCallbacksLeft--;
+ return;
+ }
+
+ if (sInMethod) {
+ sendFatalFailureToHost("completeCallback invoked while another nanoapp "
+ "method is running");
+ }
+ sanityCheck(eventType, data, 1);
+
+ --sCompleteCallbacksLeft;
+ if (sCompleteCallbacksLeft == 0) {
+ if (sEventsLeft != 0) {
+ sendFatalFailureToHost("completeCallbacks delivered before events");
+ }
+ sendSuccessToHost();
+ } else if (sCompleteCallbacksLeft < 0) {
+ // It's too late for the Host to catch this failure, but perhaps
+ // the abort will screw up our unload, and trigger a failure that way.
+ sendFatalFailureToHost("completeCallback called too many times");
+ }
+}
+
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/send_event_stress_test.h b/apps/chqts/src/general_test/send_event_stress_test.h
new file mode 100644
index 0000000..8176cf4
--- /dev/null
+++ b/apps/chqts/src/general_test/send_event_stress_test.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_STRESS_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_STRESS_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Sends events until we can't anymore, and makes sure things still work.
+ *
+ * Simple Protocol.
+ */
+class SendEventStressTest : public Test {
+ public:
+ SendEventStressTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ uint32_t mInstanceId;
+ static bool sInMethod;
+ static bool sInitTime;
+
+ static int32_t sEventsLeft;
+ static int32_t sCompleteCallbacksLeft;
+
+ static void completeCallback(uint16_t eventType, void *data);
+
+ static void sanityCheck(uint16_t eventType, const void *data, uint32_t num);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_STRESS_TEST_H_
diff --git a/apps/chqts/src/general_test/send_event_test.cc b/apps/chqts/src/general_test/send_event_test.cc
new file mode 100644
index 0000000..b07f89d
--- /dev/null
+++ b/apps/chqts/src/general_test/send_event_test.cc
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/send_event_test.h>
+
+#include <cstddef>
+
+#include <shared/abort.h>
+#include <shared/array_length.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * In a properly running test, we'll invoke chreSendEvent() a total of 12 times.
+ * We initially send eight events upon startup. And then for each of our four
+ * events which has a non-nullptr completeCallback, we call chreSendEvent()
+ * from that callback.
+ *
+ * For our first eight events, they will either be kEventType0 or kEventType1.
+ * They will either use completeCallback0 or completeCallback1. They have
+ * various data. This table describes them all:
+ *
+ * num | eventType | data | Callback
+ * ----|-----------|------------|---------
+ * 0 | 0 | ptr to num | 0
+ * 1 | 0 | ptr to num | 1
+ * 2 | 1 | ptr to num | 0
+ * 3 | 1 | ptr to num | 1
+ * 4 | 0 | ptr to num | nullptr
+ * 5 | 1 | ptr to num | nullptr
+ * 6 | 0 | nullptr | nullptr
+ * 7 | 1 | kOddData | nullptr
+ *
+ * The other four events are all kEventTypeCallback with nullptr data and
+ * nullptr callback.
+ */
+
+constexpr uint16_t kEventType0 = CHRE_EVENT_FIRST_USER_VALUE + 0;
+constexpr uint16_t kEventType1 = CHRE_EVENT_FIRST_USER_VALUE + 1;
+constexpr uint16_t kEventTypeCallback = CHRE_EVENT_FIRST_USER_VALUE + 2;
+
+// NOTE: This is not allowed to be constexpr, even if some version of g++/clang
+// allow it.
+static void *kOddData = reinterpret_cast<void*>(-1);
+
+namespace general_test {
+
+bool SendEventTest::sInMethod = false;
+uint8_t SendEventTest::sCallbacksInvoked = 0;
+
+template<uint8_t kCallbackIndex>
+void SendEventTest::completeCallback(uint16_t eventType, void *data) {
+ if (sInMethod) {
+ sendFatalFailureToHost("completeCallback called while another nanoapp "
+ "method is running.");
+ }
+ sInMethod = true;
+ if ((data == nullptr) || (data == kOddData)) {
+ sendFatalFailureToHost(
+ "completeCallback called with nullptr or odd data.");
+ }
+ uint32_t num = *(reinterpret_cast<uint32_t*>(data));
+ uint16_t expectedEventType = 0xFFFF;
+ uint8_t expectedCallbackIndex = 0xFF;
+ switch (num) {
+ case 0:
+ expectedEventType = kEventType0;
+ expectedCallbackIndex = 0;
+ break;
+ case 1:
+ expectedEventType = kEventType0;
+ expectedCallbackIndex = 1;
+ break;
+ case 2:
+ expectedEventType = kEventType1;
+ expectedCallbackIndex = 0;
+ break;
+ case 3:
+ expectedEventType = kEventType1;
+ expectedCallbackIndex = 1;
+ break;
+ default:
+ sendFatalFailureToHost("completeCallback given bad data.", &num);
+ }
+ if (expectedEventType != eventType) {
+ sendFatalFailureToHost("completeCallback bad/eventType mismatch.");
+ }
+ if (expectedCallbackIndex != kCallbackIndex) {
+ sendFatalFailureToHost("Incorrect callback function called.");
+ }
+ uint8_t mask = 1 << num;
+ if ((sCallbacksInvoked & mask) != 0) {
+ sendFatalFailureToHost("Complete callback invoked multiple times for ",
+ &num);
+ }
+ sCallbacksInvoked |= mask;
+
+ if (!chreSendEvent(kEventTypeCallback, nullptr, nullptr,
+ chreGetInstanceId())) {
+ sendFatalFailureToHost("Failed chreSendEvent in callback.");
+ }
+ sInMethod = false;
+}
+
+void SendEventTest::completeCallback0(uint16_t eventType, void *data) {
+ completeCallback<0>(eventType, data);
+}
+
+void SendEventTest::completeCallback1(uint16_t eventType, void *data) {
+ completeCallback<1>(eventType, data);
+}
+
+SendEventTest::SendEventTest()
+ : Test(CHRE_API_VERSION_1_0) , mNextNum(0) {
+}
+
+void SendEventTest::setUp(uint32_t messageSize, const void * /* message */) {
+ sInMethod = true;
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "SendEvent message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ const uint32_t id = chreGetInstanceId();
+ for (uint32_t i = 0; i < arrayLength(mData); i++) {
+ mData[i] = i;
+ }
+
+ // num: 0
+ if (!chreSendEvent(kEventType0, &mData[0], completeCallback0, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 0");
+ }
+
+ // num: 1
+ if (!chreSendEvent(kEventType0, &mData[1], completeCallback1, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 1");
+ }
+
+ // num: 2
+ if (!chreSendEvent(kEventType1, &mData[2], completeCallback0, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 2");
+ }
+
+ // num: 3
+ if (!chreSendEvent(kEventType1, &mData[3], completeCallback1, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 3");
+ }
+
+ // num: 4
+ if (!chreSendEvent(kEventType0, &mData[4], nullptr, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 4");
+ }
+
+ // num: 5
+ if (!chreSendEvent(kEventType1, &mData[5], nullptr, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 5");
+ }
+
+ // num: 6
+ if (!chreSendEvent(kEventType0, nullptr, nullptr, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 6");
+ }
+
+ // num: 7
+ if (!chreSendEvent(kEventType1, kOddData, nullptr, id)) {
+ sendFatalFailureToHost("Failed chreSendEvent num 7");
+ }
+
+ sInMethod = false;
+}
+
+void SendEventTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ if (sInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ sInMethod = true;
+ if (senderInstanceId != chreGetInstanceId()) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+
+ if (mNextNum < 8) {
+ void *expectedData;
+ if (mNextNum < 6) {
+ expectedData = &mData[mNextNum];
+ } else if (mNextNum == 6) {
+ expectedData = nullptr;
+ } else {
+ expectedData = kOddData;
+ }
+
+ uint16_t expectedEventType = 0xFFFF;
+ switch (mNextNum) {
+ case 0:
+ case 1:
+ case 4:
+ case 6:
+ expectedEventType = kEventType0;
+ break;
+ case 2:
+ case 3:
+ case 5:
+ case 7:
+ expectedEventType = kEventType1;
+ break;
+ }
+
+ if (expectedEventType != eventType) {
+ sendFatalFailureToHost("Incorrect event type sent for num ",
+ &mNextNum);
+ }
+ if (expectedData != eventData) {
+ sendFatalFailureToHost("Incorrect data sent for num ", &mNextNum);
+ }
+
+ } else {
+ if (eventType != kEventTypeCallback) {
+ sendFatalFailureToHost("Unexpected event type for num ", &mNextNum);
+ }
+ if (mNextNum == 11) {
+ // This was our last callback. Everything is good.
+ sendSuccessToHost();
+ }
+ }
+
+ mNextNum++;
+ sInMethod = false;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/send_event_test.h b/apps/chqts/src/general_test/send_event_test.h
new file mode 100644
index 0000000..51830cf
--- /dev/null
+++ b/apps/chqts/src/general_test/send_event_test.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Checks that chreSendEvent() works by sending events to ourself.
+ *
+ * We send a number of events with different combinations of types
+ * and data and callbacks, and make sure things come through as we
+ * expect.
+ *
+ * Simple Protocol.
+ */
+class SendEventTest : public Test {
+ public:
+ SendEventTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ uint32_t mNextNum;
+ uint32_t mData[6];
+
+ static bool sInMethod;
+ static uint8_t sCallbacksInvoked;
+
+ template<uint8_t kCallbackIndex>
+ static void completeCallback(uint16_t eventType, void *data);
+
+ static void completeCallback0(uint16_t eventType, void *data);
+ static void completeCallback1(uint16_t eventType, void *data);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_SEND_EVENT_TEST_H_
diff --git a/apps/chqts/src/general_test/send_message_to_host_test.cc b/apps/chqts/src/general_test/send_message_to_host_test.cc
new file mode 100644
index 0000000..ac0deb3
--- /dev/null
+++ b/apps/chqts/src/general_test/send_message_to_host_test.cc
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/send_message_to_host_test.h>
+
+#include <cinttypes>
+#include <cstddef>
+
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+
+/*
+ * Our test essentially has nine stages. The first eight stages all involve
+ * sending data to the Host. Here is a table describing them:
+ *
+ * Stage | Data length | Callback
+ * ------|-------------|--------------
+ * 0 | small | smallMessage0
+ * 1 | small | smallMessage1
+ * 2 | small | nullptr
+ * 3 | small | smallMessage0
+ * 4 | nullptr | nullptr
+ * 5 | 4 bytes | nullptr
+ * 6 | MAX + 1 | largeMessage
+ * 7 | MAX | largeMessage
+ *
+ * Stage 8 involves waiting for an incoming zero-sized message from the Host.
+ *
+ * The focus of the first four stages is making sure the correct callback
+ * gets invoked and a nullptr callback works.
+ *
+ * Stage 4 tests sending a null message to the Host (that should send).
+ *
+ * Stage 5 is not testing anything, but it's necessary to get data
+ * to the host to confirm the message in stage 7 is correct.
+ *
+ * Stage 6 tests that we properly reject oversized messages. This
+ * data should _not_ make it to the host.
+ *
+ * Stage 7 tests that we can send the maximum claimed size to the host.
+ *
+ * Every single stage which has a non-null callback is not considered a
+ * "success" until that callback has been invoked. There is no CHRE
+ * requirement in terms of the order in which these callbacks are
+ * invoked, which is why the markSuccess() method uses a bitmask and
+ * checks for overall success every time we gets success from a single
+ * stage.
+ *
+ * We consider the test successful only when all stages have reported success.
+ * Note that the Host will not perform Stage 8 until after it has received
+ * all the expected messages from the nanoapp. That's how we can confirm
+ * all messages actually made it through to the Host.
+ */
+
+// TODO(b/32114261): Remove this and actually test a variety of message types.
+constexpr uint32_t kUntestedMessageType = UINT32_C(0x51501984);
+
+namespace general_test {
+
+// TODO(b/32114261): Remove this variable.
+extern bool gUseNycMessageHack;
+
+uint8_t SendMessageToHostTest::sSmallMessageData[kSmallMessageTestCount][kSmallMessageSize];
+void *SendMessageToHostTest::sLargeMessageData[2];
+constexpr size_t SendMessageToHostTest::kLargeSizes[2];
+
+bool SendMessageToHostTest::sInMethod = false;
+uint32_t SendMessageToHostTest::sFinishedBitmask = 0;
+
+template<uint8_t kCallbackIndex>
+void SendMessageToHostTest::smallMessageCallback(void *message,
+ size_t messageSize) {
+ if (sInMethod) {
+ sendFatalFailureToHost("smallMessageCallback called while another "
+ "nanoapp method is running");
+ }
+ sInMethod = true;
+ if (message == nullptr) {
+ sendFatalFailureToHost("smallMessageCallback given null message");
+ }
+ if (messageSize != kSmallMessageSize) {
+ uint32_t size = static_cast<uint32_t>(messageSize);
+ sendFatalFailureToHost("smallMessageCallback given bad messageSize:",
+ &size);
+ }
+ const uint8_t *msg = static_cast<const uint8_t*>(message);
+ for (size_t i = 0; i < messageSize; i++) {
+ if (msg[i] != kDataByte) {
+ sendFatalFailureToHost("Corrupt data in smallMessageCallback");
+ }
+ }
+
+ uint32_t stage = getSmallDataIndex(msg);
+ uint8_t expectedCallbackIndex = 2;
+ switch (stage) {
+ case 0: // fall-through
+ case 3:
+ expectedCallbackIndex = 0;
+ break;
+ case 1:
+ expectedCallbackIndex = 1;
+ break;
+ case 2:
+ sendFatalFailureToHost("callback invoked when null callback "
+ "given");
+ break;
+ default:
+ sendInternalFailureToHost("Invalid index", &stage);
+ }
+ if (expectedCallbackIndex != kCallbackIndex) {
+ sendFatalFailureToHost("Incorrect callback function called.");
+ }
+
+ markSuccess(stage);
+ sInMethod = false;
+}
+
+void SendMessageToHostTest::smallMessageCallback0(void *message,
+ size_t messageSize) {
+ smallMessageCallback<0>(message, messageSize);
+}
+
+void SendMessageToHostTest::smallMessageCallback1(void *message,
+ size_t messageSize) {
+ smallMessageCallback<1>(message, messageSize);
+}
+
+uint32_t SendMessageToHostTest::getSmallDataIndex(const uint8_t *data) {
+ // O(N) is fine. N is small and this is test code.
+ for (uint32_t i = 0; i < kSmallMessageTestCount; i++) {
+ if (data == sSmallMessageData[i]) {
+ return i;
+ }
+ }
+ sendFatalFailureToHost("Bad memory sent to smallMessageCallback");
+ // We should never get here.
+ return kSmallMessageTestCount;
+}
+
+void SendMessageToHostTest::largeMessageCallback(void *message,
+ size_t messageSize) {
+ if (sInMethod) {
+ sendFatalFailureToHost("largeMessageCallback called while another "
+ "nanoapp method is running");
+ }
+ sInMethod = true;
+ if (message == nullptr) {
+ sendFatalFailureToHost("largeMessageCallback given null message");
+ }
+ uint32_t index = 2;
+ if (message == sLargeMessageData[0]) {
+ index = 0;
+ } else if (message == sLargeMessageData[1]) {
+ index = 1;
+ } else {
+ sendFatalFailureToHost("largeMessageCallback given bad message");
+ }
+ if (messageSize != kLargeSizes[index]) {
+ sendFatalFailureToHost("largeMessageCallback given incorrect "
+ "messageSize");
+ }
+ const uint8_t *msg = static_cast<const uint8_t*>(message);
+ for (size_t i = 0; i < messageSize; i++) {
+ if (msg[i] != kDataByte) {
+ sendFatalFailureToHost("Corrupt data in largeMessageCallback");
+ }
+ }
+ chreHeapFree(sLargeMessageData[index]);
+ // index 0 == stage 6, index 1 == stage 7
+ markSuccess(index + 6);
+
+ sInMethod = false;
+}
+
+void SendMessageToHostTest::markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if (sFinishedBitmask & finishedBit) {
+ sendFatalFailureToHost("callback called multiple times for stage:",
+ &stage);
+ }
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage", &stage);
+ }
+ sFinishedBitmask |= finishedBit;
+ if (sFinishedBitmask == kAllFinished) {
+ sendSuccessToHost();
+ }
+}
+
+void SendMessageToHostTest::prepTestMemory() {
+ nanoapp_testing::memset(sSmallMessageData, kDataByte,
+ sizeof(sSmallMessageData));
+
+ for (size_t i = 0; i < 2; i++) {
+ sLargeMessageData[i] = chreHeapAlloc(kLargeSizes[i]);
+ if (sLargeMessageData[i] == nullptr) {
+ sendFatalFailureToHost("Insufficient heap memory for test");
+ }
+ nanoapp_testing::memset(sLargeMessageData[i], kDataByte,
+ kLargeSizes[i]);
+ }
+}
+
+void SendMessageToHostTest::sendMessageMaxSize() {
+ // Our focus here is just sending this data; we're not trying to
+ // test anything. So we use the helper function.
+ uint32_t maxSize = CHRE_MESSAGE_TO_HOST_MAX_SIZE;
+ nanoapp_testing::hostToLittleEndian(&maxSize);
+ // TODO(b/32114261): We intentionally don't have a namespace using
+ // declaration for sendMessageToHost because it's generally
+ // incorrect to use while we're working around this bug. When the
+ // bug is fixed, we'll add this declaration, and use the method
+ // widely.
+ nanoapp_testing::sendMessageToHost(MessageType::kContinue,
+ &maxSize, sizeof(maxSize));
+}
+
+// Wrapper for chreSendMessageToHost() that sets sInMethod to false during its
+// execution, to allow for inline callbacks (this CHRE API is allowed to call
+// the free callback either within the function, or at an unspecified later time
+// when this nanoapp is not otherwise executing).
+bool SendMessageToHostTest::sendMessageToHost(
+ void *message, uint32_t messageSize, uint32_t reservedMessageType,
+ chreMessageFreeFunction *freeCallback) {
+ sInMethod = false;
+ bool success = chreSendMessageToHost(message, messageSize,
+ reservedMessageType, freeCallback);
+ sInMethod = true;
+
+ return success;
+}
+
+SendMessageToHostTest::SendMessageToHostTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void SendMessageToHostTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ // TODO(b/32114261): We need this hackery so we can get the raw bytes
+ // from the host, without the test infrastructure trying to
+ // interpret them. This won't be necessary when messageType is
+ // properly sent.
+ gUseNycMessageHack = false;
+
+ sInMethod = true;
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "SendMessageToHost message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ prepTestMemory();
+
+ // stage: 0
+ if (!sendMessageToHost(sSmallMessageData[0], kSmallMessageSize,
+ kUntestedMessageType, smallMessageCallback0)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 0");
+ }
+
+ // stage: 1
+ if (!sendMessageToHost(sSmallMessageData[1], kSmallMessageSize,
+ kUntestedMessageType, smallMessageCallback1)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 1");
+ }
+
+ // stage: 2
+ if (!sendMessageToHost(sSmallMessageData[2], kSmallMessageSize,
+ kUntestedMessageType, nullptr)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 2");
+ }
+ // There's no callback, so we mark this as a success.
+ markSuccess(2);
+
+ // stage: 3
+ if (!sendMessageToHost(sSmallMessageData[3], kSmallMessageSize,
+ kUntestedMessageType, smallMessageCallback0)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 3");
+ }
+
+ // stage: 4
+ if (!sendMessageToHost(nullptr, 0, kUntestedMessageType, nullptr)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 4");
+ }
+ // There's no callback, so we mark this as a success.
+ markSuccess(4);
+
+ // stage: 5
+ sendMessageMaxSize();
+ // There's no callback, so we mark this as a success.
+ markSuccess(5);
+
+ // stage: 6
+ if (sendMessageToHost(sLargeMessageData[0], kLargeSizes[0],
+ kUntestedMessageType, largeMessageCallback)) {
+ sendFatalFailureToHost("Oversized data to chreSendMessageToHost "
+ "claimed success");
+ }
+
+ // stage: 7
+ if (!sendMessageToHost(sLargeMessageData[1], kLargeSizes[1],
+ kUntestedMessageType, largeMessageCallback)) {
+ sendFatalFailureToHost("Failed chreSendMessageToHost stage 7");
+ }
+
+ sInMethod = false;
+}
+
+void SendMessageToHostTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ if (sInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ sInMethod = true;
+
+ // TODO(b/32114261): Use getMessageDataFromHostEvent(). We can't do
+ // that now because our messageType is probably wrong.
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventType != CHRE_EVENT_MESSAGE_FROM_HOST) {
+ unexpectedEvent(eventType);
+ }
+
+ auto dataStruct = static_cast<const chreMessageFromHostData *>(eventData);
+ // TODO(b/32114261): Test the message type.
+ if (dataStruct->messageSize != 0) {
+ sendFatalFailureToHost("handleEvent got non-zero message size",
+ &dataStruct->messageSize);
+ }
+ // We don't test dataStruct->message. We don't require this to be
+ // nullptr. If a CHRE choses to deal in 0-sized memory blocks, that's
+ // acceptable.
+
+ // Stage 8 was successful. Note that other stages might still be waiting
+ // for freeCallbacks. So we don't send success to the host, but just
+ // mark our stage as a success.
+ markSuccess(8);
+
+ sInMethod = false;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/send_message_to_host_test.h b/apps/chqts/src/general_test/send_message_to_host_test.h
new file mode 100644
index 0000000..3b1fb90
--- /dev/null
+++ b/apps/chqts/src/general_test/send_message_to_host_test.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_SEND_MESSAGE_TO_HOST_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_SEND_MESSAGE_TO_HOST_TEST_H_
+
+#include <general_test/test.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+/**
+ * Check chreSendMessageToHost() works, along with an empty message from the
+ * host to the nanoapp.
+ *
+ * TODO(b/32114261): This test is way more complicated than it should be.
+ * Specifically, the standard workaround for this bug involves
+ * putting more data within the 'message' to/from host/nanoapp. But
+ * since we're specifically testing that data, we can't use that
+ * workaround. When that workaround is gone, we can make this test
+ * much simpler.
+ *
+ * Protocol:
+ * Host: kSendMessageToHostTest, no data
+ * Nanoapp: 3 bytes of 0xFE
+ * Nanoapp: 3 bytes of 0xFE
+ * Nanoapp: 3 bytes of 0xFE
+ * Nanoapp: 3 bytes of 0xFE
+ * Nanoapp: 0 bytes
+ * Nanoapp: kContinue, 4 bytes (little endian) with <MessageMaxSize>
+ * Nanoapp: <MessageMaxSize> bytes of 0xFE
+ * Host: 0 bytes
+ * [nanoapp waits for all 'freeCallback's to have been invoked]
+ * Nanoapp: kSuccess
+ */
+class SendMessageToHostTest : public Test {
+ public:
+ SendMessageToHostTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ // Note, most of our data and methods are static, because much of our test
+ // logic happens in callbacks which must be static members. This class
+ // instance is a singleton, so there's no issue with this approach.
+
+ static constexpr uint8_t kDataByte = UINT8_C(0xFE);
+
+ static constexpr size_t kSmallMessageSize = 3;
+ static constexpr size_t kSmallMessageTestCount = 4;
+ static uint8_t sSmallMessageData[kSmallMessageTestCount][kSmallMessageSize];
+
+ static constexpr size_t kLargeSizes[2] = {
+ CHRE_MESSAGE_TO_HOST_MAX_SIZE + 1,
+ CHRE_MESSAGE_TO_HOST_MAX_SIZE
+ };
+ static void *sLargeMessageData[2];
+
+ // Catch if CHRE implementation illegally reenters nanoapp code.
+ static bool sInMethod;
+
+ // We have nine stages. We set a bit in our finishedBitmask
+ // when each has succeeded.
+ static constexpr uint32_t kAllFinished = (1 << 9) - 1;
+ static uint32_t sFinishedBitmask;
+
+ template<uint8_t kCallbackIndex>
+ static void smallMessageCallback(void *message, size_t messageSize);
+
+ static void smallMessageCallback0(void *message, size_t messageSize);
+ static void smallMessageCallback1(void *message, size_t messageSize);
+
+ static void largeMessageCallback(void *message, size_t messageSize);
+
+ static uint32_t getSmallDataIndex(const uint8_t *data);
+
+ static void markSuccess(uint32_t stage);
+
+ static bool sendMessageToHost(void *message, uint32_t messageSize,
+ uint32_t reservedMessageType,
+ chreMessageFreeFunction *freeCallback);
+
+ void prepTestMemory();
+ void sendMessageMaxSize();
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_SEND_MESSAGE_TO_HOST_TEST_H_
diff --git a/apps/chqts/src/general_test/sensor_info_test.cc b/apps/chqts/src/general_test/sensor_info_test.cc
new file mode 100644
index 0000000..de34b82
--- /dev/null
+++ b/apps/chqts/src/general_test/sensor_info_test.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/sensor_info_test.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+SensorInfoTest::SensorInfoTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void SensorInfoTest::setUp(uint32_t messageSize, const void * /* message */) {
+ if (messageSize != 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Expected 0 byte message, got more bytes:", &messageSize);
+ } else if (!chreSensorFindDefault(CHRE_SENSOR_TYPE_ACCELEROMETER,
+ &mSensorHandle)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "CHRE implementation does not have an accelerometer");
+ } else {
+ struct chreSensorInfo info;
+
+ if (!chreGetSensorInfo(mSensorHandle, &info)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Failed to gather sensor info");
+ } else {
+ mCompleted = true;
+ validateSensorInfo(info);
+ }
+ }
+}
+
+void SensorInfoTest::validateSensorInfo(const struct chreSensorInfo& info) const {
+ if ((mApiVersion < CHRE_API_VERSION_1_1) && (info.minInterval != 0)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Sensor minimum interval is non-zero");
+ } else if (info.minInterval == 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Sensor minimum interval is unknown");
+ } else if (!chreSensorConfigure(mSensorHandle,
+ CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS,
+ info.minInterval,
+ CHRE_SENSOR_LATENCY_DEFAULT)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Sensor failed configuration with minimum interval");
+ } else if (!chreSensorConfigureModeOnly(mSensorHandle,
+ CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to configure sensor mode to DONE");
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+}
+
+void SensorInfoTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void * /* eventData */) {
+ if (!mCompleted) {
+ unexpectedEvent(eventType);
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/sensor_info_test.h b/apps/chqts/src/general_test/sensor_info_test.h
new file mode 100644
index 0000000..6743a81
--- /dev/null
+++ b/apps/chqts/src/general_test/sensor_info_test.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_SENSOR_INFO_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_SENSOR_INFO_TEST_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace general_test {
+
+class SensorInfoTest : public Test {
+ public:
+ SensorInfoTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ void validateSensorInfo(const struct chreSensorInfo& info) const;
+ uint32_t mSensorHandle;
+ bool mCompleted = false;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_SENSOR_INFO_TEST_H_
diff --git a/apps/chqts/src/general_test/simple_heap_alloc_test.cc b/apps/chqts/src/general_test/simple_heap_alloc_test.cc
new file mode 100644
index 0000000..d425370
--- /dev/null
+++ b/apps/chqts/src/general_test/simple_heap_alloc_test.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/simple_heap_alloc_test.h>
+
+#include <cstddef>
+
+#include <general_test/test_names.h>
+#include <shared/abort.h>
+#include <shared/array_length.h>
+#include <shared/nano_string.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::MessageType;
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendMessageToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+// For most platforms, we expect that what the compiler toolchain claims
+// is the maximum alignment needed for any type is accurate. However, we
+// do support one CHRE implementation where it is configured for a lower
+// max alignment than what the toolchain claims.
+// To support this, we allow for a compiler define set for building this
+// test. For the most part, we need to just trust the CHRE implementation
+// that this number is correct. However, we make a basic sanity check of
+// this in testMaxAlignment().
+
+constexpr size_t kMaxAlignment =
+#ifdef CHRE_CUSTOM_MAX_ALIGNMENT
+ CHRE_CUSTOM_MAX_ALIGNMENT;
+#else
+ alignof(max_align_t);
+#endif // else CHRE_CUSTOM_MAX_ALIGNMENT
+
+#ifdef CHRE_CUSTOM_MAX_ALIGNMENT
+// We only test this when a CHRE implementation claims a custom max aligment.
+// We use an argument here to try to keep the compiler from performing any
+// of these calculations at compile-time, so they're forced to happen at
+// runtime. We do a mixture of multiplication and division, to force
+// various instructions which might have alignment constraints.
+static void testMaxAlignment(uint32_t zero) {
+ // It's not sufficient to use alignas(kMaxAlignment). Say kMaxAlignment
+ // is 4. Then alignas(4) could legally give something aligned on 32 bytes,
+ // and we wouldn't be testing what we hoped to test. So we ask for double
+ // the alignment (alignas(8), in our example), and then offset into that
+ // to assure that we're at exactly kMaxAlignment, and no more.
+
+#ifdef CHRE_NO_DOUBLE_SUPPORT
+ typedef float MyFloat;
+#define FLOAT_C(value) value##f
+#else
+ typedef long double myFloat;
+#define FLOAT_C(value) value
+#endif
+
+ alignas(kMaxAlignment * 2) uint8_t
+ myFloatMemory[sizeof(MyFloat) * 3 + kMaxAlignment];
+ MyFloat *mfArray =
+ reinterpret_cast<MyFloat*>(myFloatMemory + kMaxAlignment);
+ mfArray[0] = static_cast<MyFloat>(zero) + FLOAT_C(1.0);
+ mfArray[1] = static_cast<MyFloat>(zero) + FLOAT_C(3.0);
+ mfArray[2] = mfArray[0] / mfArray[1];
+ if ((mfArray[0] * mfArray[1] + mfArray[2]) / FLOAT_C(3.0) == FLOAT_C(1.0)) {
+ sendFatalFailureToHost("Float math is wrong");
+ }
+
+ constexpr size_t kUllSize = sizeof(unsigned long long);
+ static_assert(kUllSize >= 8, "Size of long long violates spec");
+ alignas(kMaxAlignment * 2) uint8_t
+ longlongMemory[kUllSize * 3 + kMaxAlignment];
+ unsigned long long *ullArray =
+ reinterpret_cast<unsigned long long*>(longlongMemory + kMaxAlignment);
+ ullArray[0] = static_cast<unsigned long long>(zero) +
+ (1ULL << (kUllSize * 8 - 4));
+ ullArray[1] = static_cast<unsigned long long>(zero) + (1ULL << 3);
+ ullArray[2] = ullArray[0] * ullArray[1];
+ constexpr unsigned long long kExpected = 747134227367742ULL;
+ unsigned long long result = ullArray[2] / 12345ULL;
+ if (((kUllSize == 8) && (result != kExpected)) ||
+ ((kUllSize > 8) && (result <= kExpected))) {
+ sendFatalFailureToHost("Long long math is wrong");
+ }
+}
+#endif // CHRE_CUSTOM_MAX_ALIGNMENT
+
+
+SimpleHeapAllocTest::SimpleHeapAllocTest()
+ : Test(CHRE_API_VERSION_1_0), mHasFreed(false) {
+}
+
+void SimpleHeapAllocTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ nanoapp_testing::memset(mPtrs, 0, sizeof(mPtrs));
+
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "SimpleHeapAlloc message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ // Allocate random small-ish sizes.
+ static constexpr size_t kSizes[5] = {
+ 16, 53, 2, 32, 40 };
+
+ mPtrs[0] = chreHeapAlloc(kSizes[0]);
+ mPtrs[1] = chreHeapAlloc(kSizes[1]);
+ // For mPtrs[2] we do _not_ use kSizes[2], because we're going to free
+ // this in a moment, and intentionally want a different size.
+ mPtrs[2] = chreHeapAlloc(23);
+ mPtrs[3] = chreHeapAlloc(kSizes[3]);
+ // We want to mix in a free among the allocs, just to make sure there
+ // isn't some issue there.
+ if (mPtrs[2] == nullptr) {
+ sendFatalFailureToHost("Failed first allocation of mPtrs[2]");
+ } else {
+ chreHeapFree(mPtrs[2]);
+ }
+ mPtrs[4] = chreHeapAlloc(kSizes[4]);
+ mPtrs[2] = chreHeapAlloc(kSizes[2]);
+
+ for (uint32_t i = 0; i < arrayLength(mPtrs); i++) {
+ if (mPtrs[i] == nullptr) {
+ // If we're getting this failure, but convinced the CHRE is
+ // correct, make sure that we're actually performing an allocation
+ // for each element of mPtrs.
+ sendFatalFailureToHost("Failed to allocate index ", &i);
+ }
+ const uintptr_t ptrValue = reinterpret_cast<uintptr_t>(mPtrs[i]);
+ if ((ptrValue & (kMaxAlignment - 1)) != 0) {
+ sendFatalFailureToHost("Misaligned allocation at index ", &i);
+ }
+ // Make sure all of the bytes are addressable. Our assumption
+ // is we'll crash here if that's not the case. Not the most
+ // friendly test, but it's better than allowing a bad CHRE.
+ // TODO: If we convince ourselves that chreLog() should be
+ // safe enough to use here, we could log an 'info' message
+ // prior to each memset attempt.
+ nanoapp_testing::memset(mPtrs[i], 0xFF, kSizes[i]);
+ }
+#ifdef CHRE_CUSTOM_MAX_ALIGNMENT
+ testMaxAlignment(messageSize);
+#endif // CHRE_CUSTOM_MAX_ALIGNMENT
+ sendMessageToHost(MessageType::kContinue);
+}
+
+void SimpleHeapAllocTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void* eventData) {
+ // We ignore the return value, since we expect no data.
+ getMessageDataFromHostEvent(senderInstanceId, eventType, eventData,
+ MessageType::kContinue, 0);
+ if (mHasFreed) {
+ sendFatalFailureToHost("Multiple kContinue messages sent");
+ }
+
+ chreHeapFree(mPtrs[3]);
+ chreHeapFree(mPtrs[1]);
+ chreHeapFree(mPtrs[2]);
+ chreHeapFree(mPtrs[0]);
+ chreHeapFree(mPtrs[4]);
+ mHasFreed = true;
+
+ sendSuccessToHost();
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/simple_heap_alloc_test.h b/apps/chqts/src/general_test/simple_heap_alloc_test.h
new file mode 100644
index 0000000..2fa3d55
--- /dev/null
+++ b/apps/chqts/src/general_test/simple_heap_alloc_test.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_SIMPLE_HEAP_ALLOC_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_SIMPLE_HEAP_ALLOC_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Performs simple allocation and freeing from the heap.
+ *
+ * Requires the host to send an additional message to tell us to free.
+ *
+ * Protocol:
+ * Host: kSimpleHeapAlloc, no data
+ * Nanoapp: kContinue, no data
+ * Host: kContinue, no data
+ * Nanoapp: kSuccess, no data
+ */
+class SimpleHeapAllocTest : public Test {
+ public:
+ SimpleHeapAllocTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ bool mHasFreed;
+ void *mPtrs[5];
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_SIMPLE_HEAP_ALLOC_TEST_H_
diff --git a/apps/chqts/src/general_test/test.cc b/apps/chqts/src/general_test/test.cc
new file mode 100644
index 0000000..0b65067
--- /dev/null
+++ b/apps/chqts/src/general_test/test.cc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/test.h>
+
+#include <shared/abort.h>
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+
+namespace general_test {
+
+Test::Test(uint32_t minSupportedVersion)
+ : mApiVersion(chreGetApiVersion())
+ , mIsSupported(mApiVersion >= minSupportedVersion) {
+}
+
+void Test::testSetUp(uint32_t messageSize, const void *message) {
+ if (mIsSupported) {
+ setUp(messageSize, message);
+ } else {
+ sendMessageToHost(nanoapp_testing::MessageType::kSkipped);
+ }
+}
+
+void Test::testHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) {
+ if (mIsSupported) {
+ handleEvent(senderInstanceId, eventType, eventData);
+ }
+}
+
+void Test::unexpectedEvent(uint16_t eventType) {
+ uint32_t localEvent = eventType;
+ sendFatalFailureToHost("Test received unexpected event:", &localEvent);
+}
+
+const void *Test::getMessageDataFromHostEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData,
+ nanoapp_testing::MessageType expectedMessageType,
+ uint32_t expectedMessageSize) {
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("Unexpected sender ID:", &senderInstanceId);
+ }
+ if (eventType != CHRE_EVENT_MESSAGE_FROM_HOST) {
+ unexpectedEvent(eventType);
+ }
+ if (eventData == nullptr) {
+ sendFatalFailureToHost("NULL eventData given");
+ }
+ auto data = static_cast<const chreMessageFromHostData*>(eventData);
+ if (data->reservedMessageType != uint32_t(expectedMessageType)) {
+ sendFatalFailureToHost("Unexpected reservedMessageType:",
+ &(data->reservedMessageType));
+ }
+ if (data->messageSize != expectedMessageSize) {
+ sendFatalFailureToHost("Unexpected messageSize:", &(data->messageSize));
+ }
+ return data->message;
+}
+
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/test.h b/apps/chqts/src/general_test/test.h
new file mode 100644
index 0000000..83d164c
--- /dev/null
+++ b/apps/chqts/src/general_test/test.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_TEST_H_
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+namespace general_test {
+
+/**
+ * Abstract base for all test cases.
+ */
+class Test {
+ public:
+ Test(uint32_t minSupportedVersion);
+ virtual ~Test() {}
+
+ void testHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData);
+
+ void testSetUp(uint32_t messageSize, const void *message);
+
+ protected:
+ /**
+ * Report a test-ending error due to an unexpectedEvent.
+ *
+ * @param eventType The event type
+ * @returns Never. This method aborts execution.
+ */
+ static void unexpectedEvent(uint16_t eventType);
+
+ /**
+ * Get the message data sent from the host, after performing sanity checks.
+ *
+ * The method centralizes a number of common sanity checks that tests
+ * will perform in taking the given CHRE event data and extracting out
+ * the raw data payload sent by the host. This method is still useful
+ * when no message data is expected from the host, as we'll still
+ * perform the sanity checks.
+ *
+ * This method will end the test in failure if any of the following happen:
+ * o 'senderInstanceId' != CHRE_INSTANCE_ID
+ * o 'eventType' != CHRE_EVENT_MESSAGE_FROM_HOST
+ * o 'eventData'->reservedMessageType != expectedMessageType
+ * o 'eventData'->messageSize != expectedMessageSize
+ *
+ * @param senderInstanceId From handleEvent()
+ * @param eventType From handleEvent()
+ * @param eventData From handleEvent()
+ * @param expectedMessageType The expected 'reservedMessageType' field
+ * when 'eventData' is seen as a chreMessageFromHostData.
+ * @param expectedMessageSize The expected 'messageSize' field
+ * when 'eventData' is seen as a chreMessageFromHostData.
+ * @returns 'eventData'->message, assuming all the sanity checks pass.
+ */
+ static const void *getMessageDataFromHostEvent(
+ uint32_t senderInstanceId, uint16_t eventType, const void* eventData,
+ nanoapp_testing::MessageType expectedMessageType,
+ uint32_t expectedMessageSize);
+
+ virtual void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) = 0;
+ virtual void setUp(uint32_t messageSize, const void *message) = 0;
+
+ /**
+ * The platform reported CHRE API version.
+ *
+ * Nanoapps may use this to determine what version they are running
+ * on and perform any version specific behaviours.
+ */
+ const uint32_t mApiVersion;
+
+ private:
+ /**
+ * Is the nanoapp supported by the platform reported CHRE API version.
+ *
+ * Nanoapps specify the minimum CHRE API version required during
+ * construction. If it is at least the version that is being reported
+ * by the platform then mIsSupported will be true. Else, the nanoapp
+ * will skip the test.
+ */
+ const bool mIsSupported;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_TEST_H_
diff --git a/apps/chqts/src/general_test/test_names.h b/apps/chqts/src/general_test/test_names.h
new file mode 100644
index 0000000..1411c88
--- /dev/null
+++ b/apps/chqts/src/general_test/test_names.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_TEST_NAMES_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_TEST_NAMES_H_
+
+#include <cstdint>
+
+/**
+ * NOTE: These values are manually synced in the GTS Java's
+ * ContextHubTestConstants.java. If you make a change here, be sure
+ * to update ContextHubTestContants.java as well.
+ */
+
+namespace general_test {
+
+/**
+ * Names of the tests we support.
+ */
+enum class TestNames : uint32_t {
+ /**
+ * Value which should never be used.
+ *
+ * This starts at CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE.
+ */
+ kInvalidTest = 0x0400,
+
+ /**
+ * Test: HelloWorldTest
+ */
+ kHelloWorld = 0x0401,
+
+ /**
+ * Test: SimpleHeapAllocTest
+ */
+ kSimpleHeapAlloc = 0x0402,
+
+ /**
+ * Test: HeapAllocStressTest
+ */
+ kHeapAllocStress = 0x0403,
+
+ /**
+ * Test: GetTimeTest
+ */
+ kGetTime = 0x0404,
+
+ /**
+ * Test: EventBetweenApps0
+ */
+ kEventBetweenApps0 = 0x0405,
+
+ /**
+ * Test: EventBetweenApps1
+ */
+ kEventBetweenApps1 = 0x0406,
+
+ /**
+ * Test: SendEventTest
+ */
+ kSendEvent = 0x0407,
+
+ /**
+ * Test: BasicAccelerometerTest
+ */
+ kBasicAccelerometer = 0x0408,
+
+ /**
+ * Test: BasicInstantMotionDetectTest
+ */
+ kBasicInstantMotionDetect = 0x0409,
+
+ /**
+ * Test: BasicStationaryDetectTest
+ */
+ kBasicStationaryDetect = 0x040A,
+
+ /**
+ * Test: BasicGyroscopeTest
+ */
+ kBasicGyroscope = 0x040B,
+
+ /**
+ * Test: BasicMagnetometerTest
+ */
+ kBasicMagnetometer = 0x040C,
+
+ /**
+ * Test: BasicBarometerTest
+ */
+ kBasicBarometer = 0x040D,
+
+ /**
+ * Test: BasicLightSensorTest
+ */
+ kBasicLightSensor = 0x040E,
+
+ /**
+ * Test: BasicProximityTest
+ */
+ kBasicProximity = 0x040F,
+
+ /**
+ * Test: VersionSanityTest
+ */
+ kVersionSanity = 0x0410,
+
+ /**
+ * Test: LoggingSanityTest
+ */
+ kLoggingSanity = 0x0411,
+
+ /**
+ * Test: SendMessageToHostTest
+ */
+ kSendMessageToHost = 0x0412,
+
+ /**
+ * Test: TimerSetTest
+ */
+ kTimerSet = 0x0413,
+
+ /**
+ * Test: TimerCancelTest
+ */
+ kTimerCancel = 0x0414,
+
+ /**
+ * Test: TimerStressTest
+ */
+ kTimerStress = 0x0415,
+
+ /**
+ * Test: SendEventStressTest
+ */
+ kSendEventStress = 0x0416,
+
+ /**
+ * Test: HeapExhaustionStabilityTest
+ */
+ kHeapExhaustionStability = 0x0417,
+
+ /**
+ * Test: GnssCapabilitiesTest
+ */
+ kGnssCapabilities = 0x0418,
+
+ /**
+ * Test: WifiCapablitiesTest
+ */
+ kWifiCapabilities = 0x0419,
+
+ /**
+ * Test: WwanCapabilitiesTest
+ */
+ kWwanCapabilities = 0x041A,
+
+ /**
+ * Test: SensorInfoTest
+ */
+ kSensorInfo = 0x041B,
+
+ /**
+ * Test: WwanCellInfoTest
+ */
+ kWwanCellInfoTest = 0x041C,
+
+ /**
+ * Test: EstimatedHostTimeTest
+ */
+ kEstimatedHostTime = 0x041D,
+
+ /**
+ * Test: NanoappInfoByAppId
+ */
+ kNanoappInfoByAppId = 0x041E,
+
+ /**
+ * Test: NanoappInfoByInstanceId
+ */
+ kNanoappInfoByInstanceId = 0x041F,
+
+ /**
+ * Test: NanoAppInfoEventsTest
+ */
+ kNanoAppInfoEventsPerformer = 0x0420,
+
+ /**
+ * Test: NanoAppInfoEventsTest
+ */
+ kNanoAppInfoEventsObserver = 0x0421,
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_TEST_NAMES_H_
diff --git a/apps/chqts/src/general_test/timer_cancel_test.cc b/apps/chqts/src/general_test/timer_cancel_test.cc
new file mode 100644
index 0000000..689d868
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_cancel_test.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/timer_cancel_test.h>
+
+#include <cinttypes>
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * This test has four stages where we cancel one-shot and recurring timers,
+ * before and after they're triggered.
+ *
+ * See the TimerCancelTest constructor to see which stage tests which setup.
+ *
+ * When all of our stages have succeeded, then we send success to the host.
+ */
+
+// 10 milliseconds
+static uint64_t kDuration = UINT64_C(10000000);
+
+namespace general_test {
+
+void TimerCancelTest::startStages() {
+ for (uint32_t i = 0; i < kStageCount; i++) {
+ Stage *stage = &mStages[i];
+ stage->timerId = chreTimerSet(kDuration, stage, stage->oneShot);
+ if (stage->timerId == CHRE_TIMER_INVALID) {
+ sendFatalFailureToHost("Unable to set timer:", &i);
+ }
+ if (stage->expectCallback) {
+ // Go on to the next stage. Note this stage will markSuccess()
+ // in handleStageEvent().
+ continue;
+ }
+ if (!chreTimerCancel(stage->timerId)) {
+ sendFatalFailureToHost("Unable to cancel timer:", &i);
+ }
+ if (chreTimerCancel(stage->timerId)) {
+ sendFatalFailureToHost("Claimed success in second cancel:", &i);
+ }
+ markSuccess(i);
+ }
+}
+
+TimerCancelTest::TimerCancelTest()
+ : Test(CHRE_API_VERSION_1_0),
+ mInMethod(false),
+ mStages{
+ // expectCallback:false ==> We're canceling before the timer fires.
+ // expectCallback:true ==> We'll cancel after the timer fires once.
+ //
+ // stage, oneShot, expectCallback
+ Stage(0, false, false),
+ Stage(1, true, false),
+ Stage(2, false, true ),
+ Stage(3, true, true )},
+ mFinishedBitmask(0) {
+}
+
+void TimerCancelTest::setUp(uint32_t messageSize, const void * /* message */) {
+ mInMethod = true;
+
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "TimerCancel message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ constexpr uint32_t kUnownedTimer = 0;
+ static_assert((kUnownedTimer != CHRE_TIMER_INVALID), "Bad test");
+ if (chreTimerCancel(kUnownedTimer)) {
+ sendFatalFailureToHost("Claimed success canceling timer we don't own");
+ }
+
+ startStages();
+
+ // Now we wait for some events from the timers to fire.
+
+ mInMethod = false;
+}
+
+void TimerCancelTest::handleStageEvent(Stage *stage) {
+ if (!stage->expectCallback) {
+ sendFatalFailureToHost("Timer didn't cancel:", &stage->stage);
+ }
+ // Now we're going to cancel the timer, so we don't expect an
+ // additional call.
+ stage->expectCallback = false;
+
+ bool cancelSucceeded = chreTimerCancel(stage->timerId);
+ if (stage->oneShot) {
+ if (cancelSucceeded) {
+ sendFatalFailureToHost("Claimed success canceling one-shot after "
+ "it fired:", &stage->stage);
+ }
+ } else {
+ if (!cancelSucceeded) {
+ sendFatalFailureToHost("Unable to cancel recurring timer:",
+ &stage->stage);
+ }
+ }
+ if (chreTimerCancel(stage->timerId)) {
+ sendFatalFailureToHost("Claimed success in second cancel:",
+ &stage->stage);
+ }
+ markSuccess(stage->stage);
+}
+
+void TimerCancelTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ if (mInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ mInMethod = true;
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventType != CHRE_EVENT_TIMER) {
+ unexpectedEvent(eventType);
+ }
+ const Stage *stage = static_cast<const Stage*>(eventData);
+ if (stage->stage >= kStageCount) {
+ sendFatalFailureToHost("Invalid handleEvent data:", &stage->stage);
+ }
+ handleStageEvent(const_cast<Stage *>(stage));
+
+ mInMethod = false;
+}
+
+void TimerCancelTest::markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage:", &stage);
+ }
+ if ((mFinishedBitmask & finishedBit) != 0) {
+ sendInternalFailureToHost("markSuccess multiple times:", &stage);
+ }
+ mFinishedBitmask |= finishedBit;
+ if (mFinishedBitmask == kAllFinished) {
+ sendSuccessToHost();
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/timer_cancel_test.h b/apps/chqts/src/general_test/timer_cancel_test.h
new file mode 100644
index 0000000..0a82a6f
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_cancel_test.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_TIMER_CANCEL_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_TIMER_CANCEL_TEST_H_
+
+#include <general_test/test.h>
+#include <chre.h>
+
+namespace general_test {
+
+/**
+ * Checks that chreTimerCancel() works by trying various usages.
+ *
+ * Simple Protocol.
+ */
+class TimerCancelTest : public Test {
+ public:
+ TimerCancelTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ struct Stage {
+ uint32_t stage;
+ uint32_t timerId;
+ bool oneShot;
+ bool expectCallback;
+
+ // Leave timerId invalid.
+ Stage(uint32_t stage_, bool oneShot_, bool expectCallback_) :
+ stage(stage_), timerId(CHRE_TIMER_INVALID), oneShot(oneShot_),
+ expectCallback(expectCallback_) {}
+ };
+
+ bool mInMethod;
+
+ static constexpr size_t kStageCount = 4;
+ Stage mStages[kStageCount];
+
+ static constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
+ uint32_t mFinishedBitmask;
+
+ void startStages();
+ void handleStageEvent(Stage *stage);
+ void markSuccess(uint32_t stage);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_TIMER_CANCEL_TEST_H_
diff --git a/apps/chqts/src/general_test/timer_set_test.cc b/apps/chqts/src/general_test/timer_set_test.cc
new file mode 100644
index 0000000..fb7165b
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_set_test.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/timer_set_test.h>
+
+#include <cinttypes>
+#include <cstddef>
+#include <new>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * We have various "stages" for different timer setups we want to test.
+ * To speed up the test, we run all our stages simultaneously. That
+ * requires having 6 timers available, but with a 32 timer minimum
+ * and a presumption that tests aren't going to be run alongside a lot
+ * of other nanoapps, this should be fine.
+ *
+ * See initStages() for the description of each stage. Since these all
+ * happen in parallel, we leave it to each stage to mark itself has having
+ * succeeded, and have markSuccess() tell the Host when all stages have
+ * reported in.
+ *
+ * Note that we intentionally place the one-shot timers first, to give
+ * us more time to notice them (incorrectly) firing multiple times.
+ */
+
+// 10 milliseconds
+static uint64_t kShortDuration = UINT64_C(10000000);
+// 1 second
+static uint64_t kOneSecond = UINT64_C(1000000000);
+static uint64_t kLongDuration = kOneSecond;
+
+namespace general_test {
+
+TimerSetTest::Stage::Stage(uint32_t stage, uint64_t duration,
+ const void *cookie, bool oneShot)
+ : mSetTime(0), mDuration(duration), mStage(stage), mEventCount(0),
+ mCookie(cookie), mOneShot(oneShot) {}
+
+void TimerSetTest::Stage::start() {
+ mSetTime = chreGetTime();
+ mTimerHandle = chreTimerSet(mDuration, mCookie, mOneShot);
+ if (mTimerHandle == CHRE_TIMER_INVALID) {
+ sendFatalFailureToHost("Unable to set timer ", &mStage);
+ }
+ if (mSetTime == 0) {
+ sendFatalFailureToHost("chreGetTime() gave 0");
+ }
+}
+
+void TimerSetTest::Stage::processEvent(uint64_t timestamp, TimerSetTest *test) {
+ if (mSetTime == 0) {
+ sendInternalFailureToHost("Didn't initialize mSetTime");
+ }
+ mEventCount++;
+
+ uint64_t expectedTime = mSetTime + (mEventCount * mDuration);
+ if (timestamp < expectedTime) {
+ sendFatalFailureToHost("Timer triggered too soon ", &mStage);
+ }
+ // TODO(b/32179037): Make this check much stricter.
+ if (timestamp > (expectedTime + kOneSecond)) {
+ sendFatalFailureToHost("Timer triggered over a second late ", &mStage);
+ }
+
+ if (mOneShot) {
+ if (mEventCount > 1) {
+ sendFatalFailureToHost("One shot timer called multiple times ",
+ &mStage);
+ } else {
+ test->markSuccess(mStage);
+ }
+ } else if (mEventCount == 3) {
+ // We mark recurring timers as successful on their third firing, if we
+ // can cancel it.
+ if (chreTimerCancel(mTimerHandle)) {
+ test->markSuccess(mStage);
+ } else {
+ sendFatalFailureToHost("Could not cancel recurring timer", &mStage);
+ }
+ }
+}
+
+void TimerSetTest::initStages() {
+ // To avoid fragmentation, we do one large allocation, and use
+ // placement new to initialize it.
+ mStages = static_cast<Stage*>(chreHeapAlloc(sizeof(*mStages) *
+ kStageCount));
+ if (mStages == nullptr) {
+ sendFatalFailureToHost("Insufficient heap");
+ }
+
+#define COOKIE(num) reinterpret_cast<const void*>(num)
+
+ // Stage 0: Test NULL cookie
+ new(&mStages[0]) Stage(0, kShortDuration, nullptr, true);
+ // Stage 1: Test (void*)-1 cookie
+ new(&mStages[1]) Stage(1, kShortDuration, COOKIE(-1), true);
+ // Stage 2: Test one shot with short duration
+ new(&mStages[2]) Stage(2, kShortDuration, COOKIE(2), true);
+ // Stage 3: Test one shot with long duration
+ new(&mStages[3]) Stage(3, kLongDuration, COOKIE(3), true);
+ // Stage 4: Test recurring with long duration
+ new(&mStages[4]) Stage(4, kLongDuration, COOKIE(4), false);
+ // Stage 5: Test recurring with short duration
+ new(&mStages[5]) Stage(5, kShortDuration, COOKIE(5), false);
+ static_assert((5 + 1) == kStageCount, "Missized array");
+
+#undef COOKIE
+}
+
+TimerSetTest::TimerSetTest()
+ : Test(CHRE_API_VERSION_1_0), mInMethod(false), mFinishedBitmask(0) {
+}
+
+void TimerSetTest::setUp(uint32_t messageSize, const void * /* message */) {
+ mInMethod = true;
+
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "TimerSet message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ initStages();
+ for (size_t i = 0; i < kStageCount; i++) {
+ mStages[i].start();
+ }
+
+ mInMethod = false;
+}
+
+TimerSetTest::~TimerSetTest() {
+ chreHeapFree(mStages);
+}
+
+void TimerSetTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ uint64_t timestamp = chreGetTime();
+ if (mInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ mInMethod = true;
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventType != CHRE_EVENT_TIMER) {
+ unexpectedEvent(eventType);
+ }
+ Stage *stage = getStageFromCookie(eventData);
+ if (stage == nullptr) {
+ sendFatalFailureToHost("handleEvent got invalid eventData");
+ }
+ stage->processEvent(timestamp, this);
+
+ mInMethod = false;
+}
+
+void TimerSetTest::markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage", &stage);
+ }
+ mFinishedBitmask |= finishedBit;
+ if (mFinishedBitmask == kAllFinished) {
+ sendSuccessToHost();
+ }
+}
+
+TimerSetTest::Stage *TimerSetTest::getStageFromCookie(const void *cookie) {
+ Stage *ret = nullptr;
+ for (size_t i = 0; i < kStageCount; i++) {
+ if (mStages[i].getCookie() == cookie) {
+ if (ret != nullptr) {
+ sendInternalFailureToHost("Multiple stages with the same "
+ "cookie");
+ }
+ ret = &mStages[i];
+ // It's cheap enough to go through the whole array, and will
+ // catch if we screw up this test setup by duplicating a cookie.
+ }
+ }
+ return ret;
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/timer_set_test.h b/apps/chqts/src/general_test/timer_set_test.h
new file mode 100644
index 0000000..504f615
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_set_test.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_TIMER_SET_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_TIMER_SET_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Checks that chreTimerSet() works by trying various timers.
+ *
+ * Simple Protocol.
+ */
+class TimerSetTest : public Test {
+ public:
+ TimerSetTest();
+ ~TimerSetTest();
+
+ void markSuccess(uint32_t stage);
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ class Stage {
+ public:
+ Stage(uint32_t stage, uint64_t duration, const void *cookie,
+ bool oneShot);
+ void start();
+ // We take 'test' so we can call markSuccess if appropriate.
+ void processEvent(uint64_t timestamp, TimerSetTest *test);
+
+ const void *getCookie() const { return mCookie; }
+
+ private:
+ uint64_t mSetTime;
+ uint64_t mDuration;
+ uint32_t mStage;
+ uint32_t mEventCount;
+ const void *mCookie;
+ bool mOneShot;
+ uint32_t mTimerHandle;
+ };
+ // In the interest in keeping our static memory usage low (since we
+ // are just one of many, many tests in this nanoapp), we get this
+ // memory from the heap instead of statically declaring an array here.
+ Stage *mStages;
+ static constexpr size_t kStageCount = 6;
+
+ bool mInMethod;
+ static constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
+ uint32_t mFinishedBitmask;
+
+ void initStages();
+ Stage *getStageFromCookie(const void *cookie);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_TIMER_SET_TEST_H_
diff --git a/apps/chqts/src/general_test/timer_stress_test.cc b/apps/chqts/src/general_test/timer_stress_test.cc
new file mode 100644
index 0000000..055622d
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_stress_test.cc
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/timer_stress_test.h>
+
+#include <cinttypes>
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendInternalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+/*
+ * We stress the system by setting more and more timers until the system
+ * runs out. We then cancel one (CT) and set a new timer post-cancel (NT).
+ * We make sure all the timers we set fire.
+ *
+ * Our stages are:
+ * Stage 0: Successfully cancel CT.
+ * Stage 1: All of our "exhaustion" timers fire.
+ * Stage 2: The new timer, NT, fires.
+ *
+ * After all of our stages have succeeded, we send success to the host. Note
+ * there is no system requirement that Stage 2 happens after Stage 1, so
+ * we use markSuccess() to track this.
+ */
+
+// Allow 1000ms to create the large number of timers specified below. This
+// equates to approximately 1ms per timer which should give ample time for
+// timer creation to complete.
+constexpr uint64_t kDuration = UINT64_C(1000000000);
+
+// If the system keeps claiming it can set more timers, we don't let it
+// continue forever. Instead, we'll cut it off at this limit. And then
+// we'll call its bluff, and make sure that all of these timers legitimately
+// fire. While it won't be an actual exhaustion test (we never took the
+// system down to no more timers available), it will still give us confidence
+// that this CHRE can properly handle any semi-reasonable timer load properly.
+// 1030 is an arbitrary number, slightly over 2^10. The hope is this
+// balances between catching incorrect behavior and the test taking too long.
+constexpr int32_t kMaxTimersToSet = INT32_C(1030);
+
+namespace general_test {
+
+#define COOKIE(num) reinterpret_cast<const void*>(num)
+
+void TimerStressTest::startStages() {
+ uint32_t cancelId = chreTimerSet(kDuration, COOKIE(0), true);
+ if (cancelId == CHRE_TIMER_INVALID) {
+ sendFatalFailureToHost("No timers available");
+ }
+
+ mStage1CallbacksLeft = 0;
+ // We anticipate most CHREs will not reach kMaxTimersToSet.
+ while (mStage1CallbacksLeft < kMaxTimersToSet) {
+ if (chreTimerSet(kDuration, COOKIE(1), true) == CHRE_TIMER_INVALID) {
+ break;
+ }
+ mStage1CallbacksLeft++;
+ }
+ if (mStage1CallbacksLeft == 0) {
+ sendFatalFailureToHost("Insufficient timers available");
+ }
+ if (!chreTimerCancel(cancelId)) {
+ sendFatalFailureToHost("Unable to cancel timer");
+ }
+ markSuccess(0);
+ if (chreTimerSet(kDuration, COOKIE(2), true) == CHRE_TIMER_INVALID) {
+ sendFatalFailureToHost("Unable to set new timer after successful "
+ "cancel.");
+ }
+}
+
+#undef COOKIE
+
+TimerStressTest::TimerStressTest()
+ : Test(CHRE_API_VERSION_1_0),
+ mInMethod(false),
+ mFinishedBitmask(0),
+ mStage1CallbacksLeft(0) {
+}
+
+void TimerStressTest::setUp(uint32_t messageSize, const void * /* message */) {
+ mInMethod = true;
+
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "TimerStress message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ startStages();
+
+ mInMethod = false;
+}
+
+void TimerStressTest::handleStageEvent(uint32_t index) {
+ switch (index) {
+ case 0:
+ sendFatalFailureToHost("Canceled timer fired:", &index);
+ break;
+
+ case 1:
+ --mStage1CallbacksLeft;
+ if (mStage1CallbacksLeft <= 0) {
+ markSuccess(index);
+ }
+ break;
+
+ case 2:
+ markSuccess(index);
+ break;
+
+ default:
+ sendFatalFailureToHost("Unexpected event stage:", &index);
+ }
+}
+
+void TimerStressTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ if (mInMethod) {
+ sendFatalFailureToHost("handleEvent invoked while another nanoapp "
+ "method is running");
+ }
+ mInMethod = true;
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ sendFatalFailureToHost("handleEvent got event from unexpected sender:",
+ &senderInstanceId);
+ }
+ if (eventType != CHRE_EVENT_TIMER) {
+ unexpectedEvent(eventType);
+ }
+ handleStageEvent(reinterpret_cast<uint32_t>(eventData));
+
+ mInMethod = false;
+}
+
+void TimerStressTest::markSuccess(uint32_t stage) {
+ chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
+ uint32_t finishedBit = (1 << stage);
+ if ((kAllFinished & finishedBit) == 0) {
+ sendFatalFailureToHost("markSuccess bad stage:", &stage);
+ }
+ if ((mFinishedBitmask & finishedBit) != 0) {
+ sendFatalFailureToHost("timer over-triggered:", &stage);
+ }
+ mFinishedBitmask |= finishedBit;
+ if (mFinishedBitmask == kAllFinished) {
+ sendSuccessToHost();
+ }
+}
+
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/timer_stress_test.h b/apps/chqts/src/general_test/timer_stress_test.h
new file mode 100644
index 0000000..d142d90
--- /dev/null
+++ b/apps/chqts/src/general_test/timer_stress_test.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_TIMER_STRESS_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_TIMER_STRESS_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Exhausts all of our system timers and makes sure things still work.
+ *
+ * Simple Protocol.
+ */
+class TimerStressTest : public Test {
+ public:
+ TimerStressTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ bool mInMethod;
+
+ static constexpr size_t kStageCount = 3;
+ static constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
+ uint32_t mFinishedBitmask;
+
+ int32_t mStage1CallbacksLeft;
+
+ void startStages();
+ void handleStageEvent(uint32_t index);
+ void markSuccess(uint32_t stage);
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_TIMER_STRESS_TEST_H_
diff --git a/apps/chqts/src/general_test/version_sanity_test.cc b/apps/chqts/src/general_test/version_sanity_test.cc
new file mode 100644
index 0000000..ffe9705
--- /dev/null
+++ b/apps/chqts/src/general_test/version_sanity_test.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <general_test/version_sanity_test.h>
+
+#include <cstddef>
+
+#include <shared/send_message.h>
+
+#include <chre.h>
+
+using nanoapp_testing::sendFatalFailureToHost;
+using nanoapp_testing::sendSuccessToHost;
+
+namespace general_test {
+
+VersionSanityTest::VersionSanityTest()
+ : Test(CHRE_API_VERSION_1_0) {
+}
+
+void VersionSanityTest::setUp(uint32_t messageSize, const void * /* message */) {
+ if (messageSize != 0) {
+ sendFatalFailureToHost(
+ "VersionSanity message expects 0 additional bytes, got ",
+ &messageSize);
+ }
+
+ if (mApiVersion < CHRE_API_VERSION_1_0) {
+ sendFatalFailureToHost("API version less than 1.0", &mApiVersion);
+ }
+ if ((mApiVersion & UINT32_C(0xFFFF)) != 0) {
+ sendFatalFailureToHost("API version has two LSB set", &mApiVersion);
+ }
+ uint32_t platformVersion = chreGetVersion();
+ constexpr uint32_t kMajorMinorMask = UINT32_C(0xFFFF0000);
+ // Both mApiVersion and platformVersion refer to what the CHRE was
+ // built against, so they must agree in major and minor version.
+ if ((platformVersion & kMajorMinorMask) != (mApiVersion & kMajorMinorMask)) {
+ sendFatalFailureToHost("API and platform version mismatch",
+ &platformVersion);
+ }
+
+ // Confirm that our app (CHRE_API_VERSION) and CHRE (mApiVersion) were
+ // both compiled against the same major version. They're allowed to
+ // differ in minor version.
+ constexpr uint32_t kMajorMask = UINT32_C(0xFF000000);
+ if ((mApiVersion & kMajorMask) != (CHRE_API_VERSION & kMajorMask)) {
+ uint32_t appVersion = CHRE_API_VERSION;
+ sendFatalFailureToHost("App built against different major version",
+ &appVersion);
+ }
+
+ uint64_t platformId = chreGetPlatformId();
+ if ((platformId == UINT64_C(0)) || (platformId == UINT64_C(-1))) {
+ sendFatalFailureToHost("Bogus platform ID");
+ }
+ // TODO(b/30077401): We should send the Platform ID back to the Host, and
+ // have the Host confirm this is the Platform ID we used for loading
+ // the nanoapp.
+
+ sendSuccessToHost();
+}
+
+void VersionSanityTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType, const void* eventData) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/version_sanity_test.h b/apps/chqts/src/general_test/version_sanity_test.h
new file mode 100644
index 0000000..f04deb9
--- /dev/null
+++ b/apps/chqts/src/general_test/version_sanity_test.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_VERSION_SANITY_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_VERSION_SANITY_TEST_H_
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Checks that the chre_version.h methods give sane responses.
+ *
+ * Simple protocol.
+ *
+ * (Note: TODO(b/30077401): This test will have a non-simple protocol.)
+ */
+class VersionSanityTest : public Test {
+ public:
+ VersionSanityTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void* eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_VERSION_SANITY_TEST_H_
diff --git a/apps/chqts/src/general_test/wifi_capabilities_test.cc b/apps/chqts/src/general_test/wifi_capabilities_test.cc
new file mode 100644
index 0000000..f878baf
--- /dev/null
+++ b/apps/chqts/src/general_test/wifi_capabilities_test.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/wifi_capabilities_test.h>
+
+#include <chre.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+WifiCapabilitiesTest::WifiCapabilitiesTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void WifiCapabilitiesTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Expected 0 byte message, got more bytes:", &messageSize);
+ } else {
+ uint32_t allCapabilities = CHRE_WIFI_CAPABILITIES_NONE;
+
+ if (mApiVersion >= CHRE_API_VERSION_1_1) {
+ allCapabilities |= CHRE_WIFI_CAPABILITIES_SCAN_MONITORING
+ | CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN;
+ }
+
+ // Call the new API
+ uint32_t capabilities = chreWifiGetCapabilities();
+
+ // Clear out known capabilities, any remaining are unknown
+ if ((capabilities & ~allCapabilities) != 0) {
+ if (mApiVersion > CHRE_API_VERSION_1_1) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "New version with unknown capabilities encountered:",
+ &capabilities);
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received unexpected capabilities:", &capabilities);
+ }
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+void WifiCapabilitiesTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void * /* eventData */) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/wifi_capabilities_test.h b/apps/chqts/src/general_test/wifi_capabilities_test.h
new file mode 100644
index 0000000..787af3c
--- /dev/null
+++ b/apps/chqts/src/general_test/wifi_capabilities_test.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_WIFI_CAPABILITIES_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_WIFI_CAPABILITIES_TEST_H_
+
+#include <cstdint>
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Confirms that WiFi capabilities exist
+ *
+ * Simple protocol
+ * Host : kWiFiCapabilities, no data
+ * Nanoapp: kSuccess, no data
+ */
+class WifiCapabilitiesTest : public Test {
+ public:
+ WifiCapabilitiesTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_WIFI_CAPABILITIES_TEST_H_
diff --git a/apps/chqts/src/general_test/wwan_capabilities_test.cc b/apps/chqts/src/general_test/wwan_capabilities_test.cc
new file mode 100644
index 0000000..c443466
--- /dev/null
+++ b/apps/chqts/src/general_test/wwan_capabilities_test.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/wwan_capabilities_test.h>
+
+#include <chre.h>
+
+#include <shared/send_message.h>
+
+namespace general_test {
+
+WwanCapabilitiesTest::WwanCapabilitiesTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void WwanCapabilitiesTest::setUp(uint32_t messageSize,
+ const void * /* message */) {
+ if (messageSize != 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Expected 0 byte message, got more bytes:", &messageSize);
+ } else {
+ uint32_t allCapabilities = CHRE_WWAN_CAPABILITIES_NONE;
+
+ if (mApiVersion >= CHRE_API_VERSION_1_1) {
+ allCapabilities |= CHRE_WWAN_GET_CELL_INFO;
+ }
+
+ // Call the new API
+ uint32_t capabilities = chreWwanGetCapabilities();
+
+ // Clear out known capabilities, any remaining are unknown
+ if ((capabilities & ~allCapabilities) != 0) {
+ if (mApiVersion > CHRE_API_VERSION_1_1) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "New version with unknown capabilities encountered:",
+ &capabilities);
+ } else {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received unexpected capabilities:", &capabilities);
+ }
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+void WwanCapabilitiesTest::handleEvent(uint32_t /* senderInstanceId */,
+ uint16_t eventType,
+ const void * /* eventData */) {
+ unexpectedEvent(eventType);
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/wwan_capabilities_test.h b/apps/chqts/src/general_test/wwan_capabilities_test.h
new file mode 100644
index 0000000..9636daf
--- /dev/null
+++ b/apps/chqts/src/general_test/wwan_capabilities_test.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_WWAN_CAPABILITIES_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_WWAN_CAPABILITIES_TEST_H_
+
+#include <cstdint>
+
+#include <general_test/test.h>
+
+namespace general_test {
+
+/**
+ * Confirms that WWAN capabilities exist
+ *
+ * Simple protocol
+ * Host : kWWANCapabilities, no data
+ * Nanoapp: kSuccess, no data
+ */
+class WwanCapabilitiesTest : public Test {
+ public:
+ WwanCapabilitiesTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_WWAN_CAPABILITIES_TEST_H_
diff --git a/apps/chqts/src/general_test/wwan_cell_info_test.cc b/apps/chqts/src/general_test/wwan_cell_info_test.cc
new file mode 100644
index 0000000..470dbb8
--- /dev/null
+++ b/apps/chqts/src/general_test/wwan_cell_info_test.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <general_test/wwan_cell_info_test.h>
+
+#include <general_test/cell_info_base.h>
+#include <general_test/cell_info_cdma.h>
+#include <general_test/cell_info_gsm.h>
+#include <general_test/cell_info_lte.h>
+#include <general_test/cell_info_tdscdma.h>
+#include <general_test/cell_info_wcdma.h>
+
+#include <shared/send_message.h>
+
+/*
+ * General philosophy behind this test:
+ * Make a call to chreWwanGetCellInfoAsync and then ensure the following:
+ * 1) Data is received within CHRE_ASYNC_RESULT_TIMEOUT_NS + small buffer.
+ * 2) Various fields in the returned data are correct.
+ */
+
+namespace general_test {
+
+WwanCellInfoTest::WwanCellInfoTest()
+ : Test(CHRE_API_VERSION_1_1) {
+}
+
+void WwanCellInfoTest::setUp(uint32_t messageSize, const void * /* message */) {
+ if ((chreWwanGetCapabilities() & CHRE_WWAN_GET_CELL_INFO) == 0) {
+ sendMessageToHost(nanoapp_testing::MessageType::kSkipped);
+ } else if (!chreWwanGetCellInfoAsync(&mTimerHandle)) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "chreWwanGetCellInfo failed unexpectedly");
+ } else {
+ mTimerHandle = chreTimerSet(CHRE_ASYNC_RESULT_TIMEOUT_NS,
+ &mTimerHandle, true /* oneShot */);
+
+ if (mTimerHandle == CHRE_TIMER_INVALID) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Unable to set timer for automatic failure");
+ }
+ }
+}
+
+WwanCellInfoTest::~WwanCellInfoTest() {
+ // Ensure the timer is cancelled
+ cancelTimer();
+}
+
+void WwanCellInfoTest::handleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ // The only expected message is from the async call
+ if (senderInstanceId != CHRE_INSTANCE_ID) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "handleEvent received event from unexpected sender:",
+ &senderInstanceId);
+ } else if (eventType == CHRE_EVENT_WWAN_CELL_INFO_RESULT) {
+ cancelTimer();
+ validateCellInfoResult(eventData);
+ } else if (eventType == CHRE_EVENT_TIMER) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "chreWwanGetCellInfo did not return data in time");
+ } else {
+ uint32_t type = eventType;
+ nanoapp_testing::sendFatalFailureToHost(
+ "handleEvent received an unexpected eventType:", &type);
+ }
+}
+
+void WwanCellInfoTest::cancelTimer() {
+ if (mTimerHandle != CHRE_TIMER_INVALID) {
+ chreTimerCancel(mTimerHandle);
+ mTimerHandle = CHRE_TIMER_INVALID;
+ }
+}
+
+void WwanCellInfoTest::validateCellInfo(uint8_t count,
+ const struct chreWwanCellInfo *cells) const {
+ bool valid = true;
+
+ for (int i = 0; (i < count) && valid; ++i) {
+ if (cells[i].reserved != 0) {
+ valid = false;
+ CellInfoBase::sendFatalFailureUint8(
+ "Invalid reserved CellInfo field: %d", cells[i].reserved);
+ }
+
+ for (uint8_t byte : cells[i].reserved2) {
+ if (byte != 0) {
+ valid = false;
+ CellInfoBase::sendFatalFailureUint8(
+ "Invalid reserved2 field: %d", byte);
+ }
+ }
+
+ if ((cells[i].timeStampType != CHRE_WWAN_CELL_TIMESTAMP_TYPE_UNKNOWN)
+ && (cells[i].timeStampType
+ != CHRE_WWAN_CELL_TIMESTAMP_TYPE_ANTENNA)
+ && (cells[i].timeStampType
+ != CHRE_WWAN_CELL_TIMESTAMP_TYPE_MODEM)
+ && (cells[i].timeStampType
+ != CHRE_WWAN_CELL_TIMESTAMP_TYPE_OEM_RIL)
+ && (cells[i].timeStampType
+ != CHRE_WWAN_CELL_TIMESTAMP_TYPE_JAVA_RIL)) {
+ valid = false;
+ CellInfoBase::sendFatalFailureUint8(
+ "Invalid timeStampType: %d", cells[i].timeStampType);
+ }
+
+ if (cells[i].cellInfoType == CHRE_WWAN_CELL_INFO_TYPE_GSM) {
+ valid &= CellInfoGsm::validate(cells[i].CellInfo.gsm);
+ } else if (cells[i].cellInfoType == CHRE_WWAN_CELL_INFO_TYPE_CDMA) {
+ valid &= CellInfoCdma::validate(cells[i].CellInfo.cdma);
+ } else if (cells[i].cellInfoType == CHRE_WWAN_CELL_INFO_TYPE_LTE) {
+ valid &= CellInfoLte::validate(cells[i].CellInfo.lte);
+ } else if (cells[i].cellInfoType == CHRE_WWAN_CELL_INFO_TYPE_WCDMA) {
+ valid &= CellInfoWcdma::validate(cells[i].CellInfo.wcdma);
+ } else if (cells[i].cellInfoType == CHRE_WWAN_CELL_INFO_TYPE_TD_SCDMA) {
+ valid &= CellInfoTdscdma::validate(cells[i].CellInfo.tdscdma);
+ } else {
+ valid = false;
+ CellInfoBase::sendFatalFailureUint8(
+ "Invalid cellInfoType: %d", cells[i].cellInfoType);
+ }
+ }
+
+ if (valid) {
+ nanoapp_testing::sendSuccessToHost();
+ }
+}
+
+void WwanCellInfoTest::validateCellInfoResult(const void *eventData) const {
+ const struct chreWwanCellInfoResult *result =
+ static_cast<const chreWwanCellInfoResult *>(eventData);
+
+ if (eventData == nullptr) {
+ nanoapp_testing::sendFatalFailureToHost("Received eventData is null");
+ } else if (result->version != CHRE_WWAN_CELL_INFO_RESULT_VERSION) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received version is unexpected value");
+ } else if (result->reserved != 0) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received reserved field non-zero");
+ } else {
+ const uint32_t *receivedCookie =
+ static_cast<const uint32_t *>(result->cookie);
+
+ if (receivedCookie != &mTimerHandle) {
+ nanoapp_testing::sendFatalFailureToHost(
+ "Received cookie does not match");
+ } else if (result->cellInfoCount != 0) {
+ validateCellInfo(result->cellInfoCount, result->cells);
+ } else {
+ nanoapp_testing::sendSuccessToHost();
+ }
+ }
+}
+
+} // namespace general_test
diff --git a/apps/chqts/src/general_test/wwan_cell_info_test.h b/apps/chqts/src/general_test/wwan_cell_info_test.h
new file mode 100644
index 0000000..7360575
--- /dev/null
+++ b/apps/chqts/src/general_test/wwan_cell_info_test.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _GTS_NANOAPPS_GENERAL_TEST_WWAN_CELL_INFO_TEST_H_
+#define _GTS_NANOAPPS_GENERAL_TEST_WWAN_CELL_INFO_TEST_H_
+
+#include <general_test/test.h>
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace general_test {
+
+class WwanCellInfoTest : public Test {
+ public:
+ WwanCellInfoTest();
+ ~WwanCellInfoTest();
+
+ protected:
+ void handleEvent(uint32_t senderInstanceId, uint16_t eventType,
+ const void *eventData) override;
+ void setUp(uint32_t messageSize, const void *message) override;
+
+ private:
+ void cancelTimer();
+ void validateCellInfo(uint8_t count, const struct chreWwanCellInfo *cells) const;
+ void validateCellInfoResult(const void *eventData) const;
+
+ uint32_t mTimerHandle = CHRE_TIMER_INVALID;
+};
+
+} // namespace general_test
+
+#endif // _GTS_NANOAPPS_GENERAL_TEST_WWAN_CELL_INFO_TEST_H_
diff --git a/apps/chqts/src/shared/Android.mk b/apps/chqts/src/shared/Android.mk
new file mode 100644
index 0000000..4a3d628
--- /dev/null
+++ b/apps/chqts/src/shared/Android.mk
@@ -0,0 +1,50 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# While it's arguably overkill to have unit test for our testing code,
+# this testing code runs under an embedded environment which is much more
+# difficult to test. To the extent that we have platform-independent
+# code here, it seems a long-term time saver to have Linux tests for
+# what we can easily test.
+
+LOCAL_PATH := $(call my-dir)
+
+FILES_UNDER_TEST := \
+ dumb_allocator.cc \
+ nano_endian.cc \
+ nano_string.cc \
+
+TESTING_SOURCE := \
+ dumb_allocator_test.cc \
+ nano_endian_be_test.cc \
+ nano_endian_le_test.cc \
+ nano_endian_test.cc \
+ nano_string_test.cc \
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+
+LOCAL_SRC_FILES := $(FILES_UNDER_TEST) $(TESTING_SOURCE)
+
+LOCAL_MODULE := nanoapp_chqts_shared_tests
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CPPFLAGS += -Wall -Wextra -Werror
+LOCAL_CPPFLAGS += -DINTERNAL_TESTING
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+endif # HOST_OS == linux
diff --git a/apps/chqts/src/shared/abort.cc b/apps/chqts/src/shared/abort.cc
new file mode 100644
index 0000000..9340918
--- /dev/null
+++ b/apps/chqts/src/shared/abort.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/abort.h>
+
+#include <cstdint>
+
+#include <chre.h>
+
+namespace nanoapp_testing {
+
+void abort(AbortBlame reason) {
+ uint32_t abortCode = UINT32_C(0x10000) + static_cast<uint16_t>(reason);
+ chreAbort(abortCode);
+
+ // We should never get here. But in the case that the CHRE has a bug
+ // in its chreAbort() implementation, let's try to blow things up.
+ // Some compilers are smart about not wanting to crash like this, so
+ // we need to fool them via indirection.
+ uint16_t zero = static_cast<uint16_t>(reason)
+ - static_cast<uint8_t>(reason);
+ uint8_t *badPointer = reinterpret_cast<uint8_t*>(zero);
+ *badPointer = 1;
+
+ // If we're here, we have a buggy CHRE on a platform where it's okay
+ // to write to nullptr. At the very least, let's not return from this
+ // function.
+ while (true) {}
+}
+
+} // namespace nanoapp_testing
diff --git a/apps/chqts/src/shared/abort.h b/apps/chqts/src/shared/abort.h
new file mode 100644
index 0000000..b76f2b3
--- /dev/null
+++ b/apps/chqts/src/shared/abort.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_ABORT_H_
+#define _GTS_NANOAPPS_SHARED_ABORT_H_
+
+#include <cstdint>
+
+namespace nanoapp_testing {
+
+/**
+ * Enumeration of what should be blamed for why the test was aborted.
+ */
+enum class AbortBlame : uint16_t {
+ /**
+ * Most common failure mode, a presumed issue within the CHRE
+ * implementation.
+ *
+ * The test should have sent out an error message prior to aborting.
+ */
+ kChre = 0,
+
+ /**
+ * A presumed issue within the CHRE implementation, discovered while
+ * in the nanoappEnd() method of the nanoapp.
+ *
+ * This differs from kChre in that we're not able to send out an
+ * error message prior to aborting, so we want a distinct value.
+ */
+ kChreInNanoappEnd = 1,
+
+ /**
+ * A presumed bug in the test framework itself.
+ *
+ * The test should have sent out a kInternalError message prior to
+ * aborting.
+ */
+ kTestFramework = 2
+};
+
+/**
+ * This should fatally abort the nanoapp, and hence the current test.
+ *
+ * This is useful in cases where we cannot proceed due to previous issues,
+ * or in cases where we want to make unambigously clear that we've
+ * experienced a failure.
+ *
+ * The presumption is that code will send out a message prior to calling
+ * this method, if at all practical.
+ *
+ * This will invoke chreAbort() with the code (0x10000 + uint16_t(reason)),
+ * preserving the lower two bytes for tests to use for testing of
+ * chreAbort().
+ *
+ * @param reason Optional. The appropriate AbortBlame. Defaults to kChre.
+ * @returns This function should never return.
+ */
+void abort(AbortBlame reason = AbortBlame::kChre);
+
+} // namespace nanoapp_testing
+
+#endif // _GTS_NANOAPPS_SHARED_ABORT_H_
diff --git a/apps/chqts/src/shared/array_length.h b/apps/chqts/src/shared/array_length.h
new file mode 100644
index 0000000..73ac436
--- /dev/null
+++ b/apps/chqts/src/shared/array_length.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_ARRAY_LENGTH_H_
+#define _GTS_NANOAPPS_SHARED_ARRAY_LENGTH_H_
+
+#include <stddef.h>
+
+// We chose to have this at the global name scope, as this method
+// isn't exclusive to nanoapp testing, and hopefully someday will
+// move to some place more central.
+
+/**
+ * Determine the length of a statically-sized array at compile time using
+ * template trickery which makes sure we only match statically-sized arrays.
+ */
+template <typename T, size_t N>
+constexpr size_t arrayLength(T (&)[N]) { return N; }
+
+
+#endif // _GTS_NANOAPPS_SHARED_ARRAY_LENGTH_H_
diff --git a/apps/chqts/src/shared/dumb_allocator.cc b/apps/chqts/src/shared/dumb_allocator.cc
new file mode 100644
index 0000000..1b40f48
--- /dev/null
+++ b/apps/chqts/src/shared/dumb_allocator.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/dumb_allocator.h>
+
+#include <shared/nano_string.h>
+
+namespace nanoapp_testing {
+
+DumbAllocatorBase::DumbAllocatorBase(size_t allocSize, size_t slotCount,
+ uint8_t *rawMemory)
+ : mAllocSize(allocSize),
+ mSlotCount(slotCount),
+ mRawMemory(rawMemory),
+ mAllocatedSlots(0) {
+ // We're not worried about runtime efficiency, since this is testing
+ // code. In case of an issue within the tests, though, we do want
+ // to have consistent behavior. Thus, we initialize this memory to
+ // aid tracking problems.
+ memset(mRawMemory, 0xCD, mSlotCount * mAllocSize);
+}
+
+void *DumbAllocatorBase::alloc(size_t bytes) {
+ if (bytes > mAllocSize) {
+ // Oversized for our allocator.
+ return nullptr;
+ }
+ size_t slot = 0;
+ for (uint32_t mask = 1; slot < mSlotCount; slot++, mask <<= 1) {
+ if ((mAllocatedSlots & mask) == 0) {
+ // This space is available, let's claim it.
+ mAllocatedSlots |= mask;
+ break;
+ }
+ }
+ if (slot == mSlotCount) {
+ // We're out of space.
+ return nullptr;
+ }
+ return mRawMemory + (slot * mAllocSize);
+}
+
+bool DumbAllocatorBase::free(void *pointer) {
+ size_t slot;
+ if (!getSlot(pointer, &slot)) {
+ return false;
+ }
+ mAllocatedSlots &= ~(1 << slot);
+ return true;
+}
+
+bool DumbAllocatorBase::contains(const void *pointer) const {
+ size_t slot;
+ return getSlot(pointer, &slot);
+}
+
+bool DumbAllocatorBase::getSlot(const void *pointer, size_t *slot) const {
+ const uint8_t *ptr = static_cast<const uint8_t *>(pointer);
+ if (ptr < mRawMemory) {
+ // Out of range.
+ return false;
+ }
+ *slot = (ptr - mRawMemory) / mAllocSize;
+ if (*slot >= mSlotCount) {
+ // Out of range.
+ return false;
+ }
+ // Also confirm alignment.
+ return ((mRawMemory + (*slot * mAllocSize)) == ptr);
+}
+
+
+} // namespace nanoapp_testing
diff --git a/apps/chqts/src/shared/dumb_allocator.h b/apps/chqts/src/shared/dumb_allocator.h
new file mode 100644
index 0000000..06ce081
--- /dev/null
+++ b/apps/chqts/src/shared/dumb_allocator.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_
+#define _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace nanoapp_testing {
+
+// Implementation Note: We chose the pattern of having DumbAllocatorBase to
+// reduce the code duplication from multiple instances of DumbAllocator with
+// different template parameters.
+// See DumbAllocator below for usage and API documentation.
+class DumbAllocatorBase {
+ protected:
+ DumbAllocatorBase(size_t allocSize, size_t slotCount, uint8_t *rawMemory);
+
+ void *alloc(size_t bytes);
+ bool free(void *ptr);
+ bool contains(const void *ptr) const;
+
+ static constexpr size_t MaxSlotCount() {
+ // Our mAllocatedSlots is treated as a bit array, so we get 8 slots for
+ // each byte it has.
+ return (sizeof(mAllocatedSlots) * 8);
+ }
+
+ private:
+ const size_t mAllocSize;
+ const size_t mSlotCount;
+ uint8_t * const mRawMemory;
+ uint32_t mAllocatedSlots;
+
+ bool getSlot(const void *ptr, size_t *slot) const;
+};
+
+
+/**
+ * This dumb allocator is designed to allow us to easily get chunks of
+ * memory without needing to go through heap allocation. The idea is to
+ * reduce our dependency on CHRE for some aspects of our tests.
+ *
+ * This allocator is non-reentrant. It's also inefficient and a bad idea
+ * for shipping code, but useful for reducing dependencies during testing.
+ *
+ * This will allow up to kSlotCount allocations of up to kAllocSize bytes
+ * each, and costs (kSlotCount * kAllocSize) bytes of underlying storage.
+ */
+template<size_t kAllocSize, size_t kSlotCount>
+class DumbAllocator : DumbAllocatorBase {
+ public:
+ DumbAllocator()
+ : DumbAllocatorBase(kAllocSize, kSlotCount, mRawMemoryArray) {}
+
+ /**
+ * If "bytes" <= kAllocSize, and there are less than kSlotCount allocations,
+ * return a valid pointer. Otherwise, nullptr.
+ *
+ * Reminder this is non-reentrant.
+ */
+ void *alloc(size_t bytes) {
+ return DumbAllocatorBase::alloc(bytes);
+ }
+
+ /**
+ * If contains(ptr) is true, free the allocation and return true.
+ * Otherwise, do nothing and return false.
+ *
+ * Reminder this is non-reentrant.
+ */
+ bool free(void *ptr) {
+ return DumbAllocatorBase::free(ptr);
+ }
+
+ /**
+ * If "ptr" was a non-null pointer returned from alloc() on this instance,
+ * return true. Otherwise, do nothing and return false.
+ */
+ bool contains(const void *ptr) const {
+ return DumbAllocatorBase::contains(ptr);
+ }
+
+ private:
+ uint8_t mRawMemoryArray[kAllocSize * kSlotCount];
+
+ static_assert(kSlotCount <= MaxSlotCount(), "kSlotCount is too high");
+};
+
+
+} // namespace nanoapp_testing
+
+#endif // _GTS_NANOAPPS_SHARED_DUMB_ALLOCATOR_H_
diff --git a/apps/chqts/src/shared/dumb_allocator_test.cc b/apps/chqts/src/shared/dumb_allocator_test.cc
new file mode 100644
index 0000000..5d28538
--- /dev/null
+++ b/apps/chqts/src/shared/dumb_allocator_test.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/dumb_allocator.h>
+
+#include <gtest/gtest.h>
+
+static constexpr size_t kAllocSize = 128;
+static constexpr size_t kSlotCount = 5;
+
+typedef nanoapp_testing::DumbAllocator<kAllocSize, kSlotCount> DA;
+
+static void ExpectGoodAlloc(const DA &da, const void *ptr) {
+ EXPECT_NE(nullptr, ptr);
+ EXPECT_TRUE(da.contains(ptr));
+}
+
+TEST(DumbAllocatorTests, SimpleAlloc) {
+ DA da;
+ void *ptr = da.alloc(kAllocSize);
+ ExpectGoodAlloc(da, ptr);
+ EXPECT_TRUE(da.free(ptr));
+}
+
+TEST(DumbAllocatorTests, AllocsAfterFree) {
+ DA da;
+ void *ptrs[kSlotCount];
+ for (size_t i = 0; i < kSlotCount; i++) {
+ ptrs[i] = da.alloc(kAllocSize);
+ ExpectGoodAlloc(da, ptrs[i]);
+ // Also confirm we're not doubly allocating the same pointer.
+ for (size_t j = 0; j < i; j++) {
+ EXPECT_NE(ptrs[j], ptrs[i]);
+ }
+ }
+ // Out of slots, allocation should fail.
+ EXPECT_EQ(nullptr, da.alloc(kAllocSize));
+
+ constexpr size_t kFreeIndex = kSlotCount / 2;
+ EXPECT_TRUE(da.free(ptrs[kFreeIndex]));
+ ptrs[kFreeIndex] = nullptr;
+
+ // Now our allocation should succeed.
+ void *newPtr = da.alloc(kAllocSize);
+ ExpectGoodAlloc(da, newPtr);
+ for (size_t i = 0; i < kSlotCount; i++) {
+ EXPECT_NE(newPtr, ptrs[i]);
+ }
+}
+
+TEST(DumbAllocatorTests, ContainsIsFalseForBadPtrs) {
+ DA da;
+ uint8_t *ptr = static_cast<uint8_t*>(da.alloc(kAllocSize));
+ ASSERT_NE(nullptr, ptr);
+ EXPECT_FALSE(da.contains(ptr - 1));
+ EXPECT_FALSE(da.contains(ptr + 1));
+ EXPECT_FALSE(da.contains(nullptr));
+}
+
+TEST(DumbAllocatorTests, FailLargeAllocations) {
+ DA da;
+ EXPECT_EQ(nullptr, da.alloc(kAllocSize + 1));
+ EXPECT_EQ(nullptr, da.alloc(kAllocSize * 2));
+}
+
+TEST(DumbAllocatorTests, SucceedSmallAllocations) {
+ DA da;
+ ExpectGoodAlloc(da, da.alloc(kAllocSize - 1));
+ ExpectGoodAlloc(da, da.alloc(1));
+ ExpectGoodAlloc(da, da.alloc(0));
+}
diff --git a/platform/shared/memory.cc b/apps/chqts/src/shared/nano_endian.cc
similarity index 67%
copy from platform/shared/memory.cc
copy to apps/chqts/src/shared/nano_endian.cc
index c137eb8..68d39e9 100644
--- a/platform/shared/memory.cc
+++ b/apps/chqts/src/shared/nano_endian.cc
@@ -14,18 +14,16 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
+#include <shared/nano_endian.h>
-#include <stdlib.h>
+namespace nanoapp_testing {
-namespace chre {
-
-void *memoryAlloc(size_t size) {
- return malloc(size);
+void swapBytes(uint8_t *bytes, size_t size) {
+ for (size_t front = 0, end = size - 1; front < end; front++, end--) {
+ uint8_t tmp = bytes[front];
+ bytes[front] = bytes[end];
+ bytes[end] = tmp;
+ }
}
-void memoryFree(void *pointer) {
- free(pointer);
-}
-
-} // namespace chre
+} // namespace nanoapp_testing
diff --git a/apps/chqts/src/shared/nano_endian.h b/apps/chqts/src/shared/nano_endian.h
new file mode 100644
index 0000000..3684ac6
--- /dev/null
+++ b/apps/chqts/src/shared/nano_endian.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_NANO_ENDIAN_H_
+#define _GTS_NANOAPPS_SHARED_NANO_ENDIAN_H_
+
+// If the platform has no endian.h, then have the build system set
+// CHRE_NO_ENDIAN_H, and set __BYTE_ORDER, __LITTLE_ENDIAN, and
+// __BIG_ENDIAN appropriately.
+#ifndef CHRE_NO_ENDIAN_H
+#include <endian.h>
+#endif
+
+#include <cstddef>
+#include <cstdint>
+
+
+#if !(defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ defined(__BIG_ENDIAN))
+#error "Need to define the preprocessor defines __BYTE_ORDER, __LITTLE_ENDIAN" \
+ " and __BIG_ENDIAN."
+#endif
+
+#if __LITTLE_ENDIAN == __BIG_ENDIAN
+#error "__LITTLE_ENDIAN and __BIG_ENDIAN must have different values."
+#endif
+
+#if ((__BYTE_ORDER != __LITTLE_ENDIAN) && (__BYTE_ORDER != __BIG_ENDIAN))
+#error "__BYTE_ORDER must be either __LITTLE_ENDIAN or __BIG_ENDIAN."
+#endif
+
+
+namespace nanoapp_testing {
+
+void swapBytes(uint8_t *bytes, size_t size);
+
+// Note: The 'static' with these 'inline' methods is required for our
+// unit tests to work, since they compile this header with different
+// endianness. Without the 'static', we'll just use the first version
+// of this we compile with, and fail some of the tests.
+
+template<typename T>
+static inline void hostToLittleEndian(T *value) {
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+ swapBytes(reinterpret_cast<uint8_t*>(value), sizeof(T));
+#else
+ // Nothing to do for little endian machines.
+ (void)value;
+#endif
+}
+
+template<typename T>
+static inline void littleEndianToHost(T *value) {
+ // This has identical behavior to hostToLittleEndian. We provide both
+ // so code reads more cleanly.
+ hostToLittleEndian(value);
+}
+
+} // namespace nanoapp_testing
+
+#endif // _GTS_NANOAPPS_SHARED_NANO_ENDIAN_H_
diff --git a/apps/chqts/src/shared/nano_endian_be_test.cc b/apps/chqts/src/shared/nano_endian_be_test.cc
new file mode 100644
index 0000000..3702f88
--- /dev/null
+++ b/apps/chqts/src/shared/nano_endian_be_test.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// For this test, we pretend we're a big endian platform, even if we don't
+// happen to be.
+#define CHRE_NO_ENDIAN_H 1
+#define __LITTLE_ENDIAN 0
+#define __BIG_ENDIAN 1
+#define __BYTE_ORDER __BIG_ENDIAN
+
+#include <shared/nano_endian.h>
+
+#include <cstdint>
+#include <cstring>
+
+#include <gtest/gtest.h>
+#include <shared/array_length.h>
+
+
+static constexpr uint8_t kLittleEndianRepresentation[4] = {
+ 0x01, 0x02, 0x03, 0x04 };
+static constexpr uint8_t kBigEndianRepresentation[4] = {
+ 0x04, 0x03, 0x02, 0x01 };
+
+TEST(EndianTest, LittleEndianToBigEndianHost) {
+ alignas(alignof(uint32_t)) uint8_t bytes[4];
+ ::memcpy(bytes, kLittleEndianRepresentation, sizeof(bytes));
+ uint32_t *valuePtr = reinterpret_cast<uint32_t*>(bytes);
+
+ nanoapp_testing::littleEndianToHost(valuePtr);
+ EXPECT_EQ(0, ::memcmp(bytes, kBigEndianRepresentation, sizeof(bytes)));
+}
+
+TEST(EndianTest, BigEndianHostToLittleEndian) {
+ alignas(alignof(uint32_t)) uint8_t bytes[4];
+ ::memcpy(bytes, kBigEndianRepresentation, sizeof(bytes));
+ uint32_t *valuePtr = reinterpret_cast<uint32_t*>(bytes);
+
+ nanoapp_testing::hostToLittleEndian(valuePtr);
+ EXPECT_EQ(0, ::memcmp(bytes, kLittleEndianRepresentation, sizeof(bytes)));
+}
diff --git a/apps/chqts/src/shared/nano_endian_le_test.cc b/apps/chqts/src/shared/nano_endian_le_test.cc
new file mode 100644
index 0000000..4dbe202
--- /dev/null
+++ b/apps/chqts/src/shared/nano_endian_le_test.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// For this test, we pretend we're a little endian platform, even if we don't
+// happen to be.
+#define CHRE_NO_ENDIAN_H 1
+#define __LITTLE_ENDIAN 0
+#define __BIG_ENDIAN 1
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#include <shared/nano_endian.h>
+
+#include <cstdint>
+#include <cstring>
+
+#include <gtest/gtest.h>
+#include <shared/array_length.h>
+
+
+static constexpr uint8_t kLittleEndianRepresentation[4] = {
+ 0x01, 0x02, 0x03, 0x04 };
+
+TEST(EndianTest, LittleEndianToLittleEndianHost) {
+ alignas(alignof(uint32_t)) uint8_t bytes[4];
+ ::memcpy(bytes, kLittleEndianRepresentation, sizeof(bytes));
+ uint32_t *valuePtr = reinterpret_cast<uint32_t*>(bytes);
+
+ nanoapp_testing::littleEndianToHost(valuePtr);
+ EXPECT_EQ(0, ::memcmp(bytes, kLittleEndianRepresentation, sizeof(bytes)));
+}
+
+TEST(EndianTest, LittleEndianHostToLittleEndian) {
+ alignas(alignof(uint32_t)) uint8_t bytes[4];
+ ::memcpy(bytes, kLittleEndianRepresentation, sizeof(bytes));
+ uint32_t *valuePtr = reinterpret_cast<uint32_t*>(bytes);
+
+ nanoapp_testing::hostToLittleEndian(valuePtr);
+ EXPECT_EQ(0, ::memcmp(bytes, kLittleEndianRepresentation, sizeof(bytes)));
+}
diff --git a/apps/chqts/src/shared/nano_endian_test.cc b/apps/chqts/src/shared/nano_endian_test.cc
new file mode 100644
index 0000000..6314ad8
--- /dev/null
+++ b/apps/chqts/src/shared/nano_endian_test.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/nano_endian.h>
+
+#include <cstdint>
+#include <cstring>
+
+#include <gtest/gtest.h>
+#include <shared/array_length.h>
+
+
+template<size_t kByteCount>
+static void swapByteTest() {
+ uint8_t bytes[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };
+ static constexpr uint8_t postSwap[] = {
+ 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09,
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
+
+#ifdef __clang__
+ // This static_assert crashes g++, but it's legit and works in clang.
+ static_assert(arrayLength(bytes) == arrayLength(postSwap),
+ "Mismatched arrays");
+#endif
+ static_assert((kByteCount > 0) && (kByteCount <= arrayLength(bytes)),
+ "Invalid test");
+
+ constexpr const uint8_t *kExpected =
+ postSwap + (arrayLength(postSwap) - kByteCount);
+ nanoapp_testing::swapBytes(bytes, kByteCount);
+ EXPECT_EQ(0, ::memcmp(bytes, kExpected, kByteCount));
+
+ if (arrayLength(bytes) < kByteCount) {
+ // Confirm that we didn't modify out of bounds.
+ EXPECT_EQ(kByteCount + 1, bytes[kByteCount]);
+ }
+}
+
+
+TEST(EndianTest, SwapBytes1) {
+ swapByteTest<1>();
+}
+
+TEST(EndianTest, SwapBytes2) {
+ swapByteTest<2>();
+}
+
+TEST(EndianTest, SwapBytes4) {
+ swapByteTest<4>();
+}
+
+TEST(EndianTest, SwapBytes8) {
+ swapByteTest<8>();
+}
+
+TEST(EndianTest, SwapBytes16) {
+ swapByteTest<16>();
+}
+
+
+// These tests should work regardless of which endian platform this
+// test happens to be built and running on.
+
+static constexpr uint32_t kValue = UINT32_C(0x04030201);
+static constexpr uint8_t kLittleEndianRepresentation[4] = {
+ 0x01, 0x02, 0x03, 0x04 };
+
+TEST(EndianTest, LittleEndianToHost) {
+ alignas(alignof(uint32_t)) uint8_t bytes[4];
+ ::memcpy(bytes, kLittleEndianRepresentation, sizeof(bytes));
+ uint32_t *valuePtr = reinterpret_cast<uint32_t*>(bytes);
+
+ nanoapp_testing::littleEndianToHost(valuePtr);
+ EXPECT_EQ(kValue, *valuePtr);
+}
+
+TEST(EndianTest, HostToLittleEndian) {
+ uint32_t value = kValue;
+ nanoapp_testing::hostToLittleEndian(&value);
+
+ const uint8_t *bytes = reinterpret_cast<uint8_t*>(&value);
+ EXPECT_EQ(0, ::memcmp(kLittleEndianRepresentation, bytes,
+ sizeof(kLittleEndianRepresentation)));
+}
diff --git a/apps/chqts/src/shared/nano_string.cc b/apps/chqts/src/shared/nano_string.cc
new file mode 100644
index 0000000..9a74d80
--- /dev/null
+++ b/apps/chqts/src/shared/nano_string.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/nano_string.h>
+
+#ifndef INTERNAL_TESTING
+
+#include <shared/send_message.h>
+#define REPORT_INTERNAL_ERROR(msg) \
+ sendInternalFailureToHost(msg)
+
+#else
+
+#include <gtest/gtest.h>
+#define REPORT_INTERNAL_ERROR(msg) FAIL() << msg
+
+#endif
+
+
+namespace nanoapp_testing {
+
+void memset(void *mem, int val, size_t count) {
+ uint8_t *bytes = static_cast<uint8_t*>(mem);
+ for (size_t i = 0; i < count; i++) {
+ bytes[i] = val;
+ }
+}
+
+void memcpy(void *d, const void *s, size_t bytes) {
+ uint8_t *dst = static_cast<uint8_t*>(d);
+ const uint8_t *src = static_cast<const uint8_t*>(s);
+ for (size_t i = 0; i < bytes; i++) {
+ dst[i] = src[i];
+ }
+}
+
+size_t strlen(char const *str) {
+ size_t ret = 0;
+ for (; str[ret] != '\0'; ret++) {}
+ return ret;
+}
+
+char *strncpy(char *dest, const char *src, size_t len) {
+ size_t i;
+ for (i = 0; (i < len) && (src[i] != '\0'); i++) {
+ dest[i] = src[i];
+ }
+ for (; i < len; i++) {
+ dest[i] = '\0';
+ }
+ return dest;
+}
+
+void uint32ToHexAscii(char *buffer, size_t buffer_len, uint32_t value) {
+ constexpr char lookup[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ if (buffer_len < kUint32ToHexAsciiBufferMinLen) {
+ // We chose not to send our buffer_len here, as that would invoke
+ // another call to this method and risk infinite recursion if something
+ // was really screwed up.
+ REPORT_INTERNAL_ERROR("uint32ToHexAscii got undersized buffer_len");
+ return;
+ }
+ buffer[0] = '0';
+ buffer[1] = 'x';
+ for (size_t i = 0, shift = 28; i < 8; i++, shift -= 4) {
+ buffer[2 + i] = lookup[(value >> shift) & 0xF];
+ }
+}
+
+} // namespace nanoapp_testing
diff --git a/apps/chqts/src/shared/nano_string.h b/apps/chqts/src/shared/nano_string.h
new file mode 100644
index 0000000..d502c8f
--- /dev/null
+++ b/apps/chqts/src/shared/nano_string.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_NANO_STRING_H_
+#define _GTS_NANOAPPS_SHARED_NANO_STRING_H_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace nanoapp_testing {
+
+void memset(void *mem, int val, size_t count);
+
+void memcpy(void *dst, const void *src, size_t bytes);
+
+size_t strlen(char const *str);
+
+char *strncpy(char *dest, const char *src, size_t len);
+
+// Does not NUL terminate. buffer_len must be >= kUint32ToHexAsciiBufferMinLen.
+// Example: Given 'value' of 1234, 'buffer' will be filled with "0x000004D2".
+void uint32ToHexAscii(char *buffer, size_t buffer_len, uint32_t value);
+
+constexpr size_t kUint32ToHexAsciiBufferMinLen = 10;
+
+} // namespace nanoapp_testing
+
+#endif // _GTS_NANOAPPS_SHARED_NANO_STRING_H_
diff --git a/apps/chqts/src/shared/nano_string_test.cc b/apps/chqts/src/shared/nano_string_test.cc
new file mode 100644
index 0000000..7ad3c37
--- /dev/null
+++ b/apps/chqts/src/shared/nano_string_test.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/nano_string.h>
+
+#include <string.h> // brings strlen, strncpy, and memset into scope.
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+#include <shared/array_length.h>
+
+// This "using directive" intentionally makes the symbols 'strlen', 'strncpy',
+// and 'memset' "ambigious" at this point. This means that every use of these
+// needs to be fully qualified in our tests below. That's as desired for
+// clarity and to avoid accidentally invoking the wrong version.
+// Note that a leading bare "::" is the fully qualified version of the
+// C library methods.
+using namespace nanoapp_testing;
+
+static constexpr size_t kMemsetBufferLen = 16;
+static constexpr int kUnsetValue = 0x5F;
+static constexpr int kNewValue = 0xB8;
+
+template<size_t kLenToSet>
+static void testMemset() {
+ uint8_t expected[kMemsetBufferLen];
+ uint8_t actual[arrayLength(expected)];
+
+ static_assert(kLenToSet <= arrayLength(expected), "Bad test invocation");
+
+ ::memset(expected, kUnsetValue, sizeof(expected));
+ ::memset(actual, kUnsetValue, sizeof(actual));
+
+ ::memset(expected, kNewValue, kLenToSet);
+ nanoapp_testing::memset(actual, kNewValue, kLenToSet);
+
+ EXPECT_EQ(0, memcmp(expected, actual, sizeof(expected)));
+}
+
+TEST(NanoStringsTest, MemsetZeroBytes) {
+ testMemset<0>();
+}
+
+TEST(NanoStringsTest, MemsetPartialArray) {
+ testMemset<(kMemsetBufferLen / 2) - 1>();
+}
+
+TEST(NanoStringsTest, MemsetFullArray) {
+ testMemset<kMemsetBufferLen>();
+}
+
+
+static constexpr size_t kMemcpyBufferLen = 8;
+static constexpr uint8_t kMemcpySrc[kMemcpyBufferLen] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+template<size_t kLenToCopy>
+static void testMemcpy() {
+ uint8_t expected[arrayLength(kMemcpySrc)];
+ uint8_t actual[arrayLength(expected)];
+
+ static_assert(kLenToCopy <= arrayLength(kMemcpySrc), "Bad test invocation");
+
+ ::memset(expected, kUnsetValue, sizeof(expected));
+ ::memset(actual, kUnsetValue, sizeof(actual));
+
+ ::memcpy(expected, kMemcpySrc, kLenToCopy);
+ nanoapp_testing::memcpy(actual, kMemcpySrc, kLenToCopy);
+
+ EXPECT_EQ(0, memcmp(expected, actual, sizeof(expected)));
+}
+
+TEST(NanoStringsTest, MemcpyZeroBytes) {
+ testMemcpy<0>();
+}
+
+TEST(NanoStringsTest, MemcpyPartialArray) {
+ testMemcpy<(kMemcpyBufferLen / 2) - 1>();
+}
+
+TEST(NanoStringsTest, MemcpyFullArray) {
+ testMemcpy<kMemcpyBufferLen>();
+}
+
+
+TEST(NanoStringsTest, StrlenEmptyString) {
+ const char *str = "";
+ EXPECT_EQ(::strlen(str), nanoapp_testing::strlen(str));
+}
+
+TEST(NanoStringsTest, StrlenNormal) {
+ const char *str = "random string\n";
+ EXPECT_EQ(::strlen(str), nanoapp_testing::strlen(str));
+}
+
+static constexpr size_t kStrncpyMax = 10;
+static constexpr char kShortString[] = "short";
+static constexpr char kLongString[] = "Kind of long string";
+static constexpr char kExactString[] = "0123456789";
+
+static void testStrncpy(const char *str, size_t len) {
+ char expected[kStrncpyMax];
+ char actual[arrayLength(expected)];
+
+ ::memset(expected, kUnsetValue, sizeof(expected));
+ ::memset(actual, kUnsetValue, sizeof(actual));
+
+ ::strncpy(expected, str, len);
+ nanoapp_testing::strncpy(actual, str, len);
+
+ EXPECT_EQ(0, memcmp(expected, actual, sizeof(expected)));
+}
+
+TEST(NanoStringsTest, Strncpy) {
+ testStrncpy(kShortString, ::strlen(kShortString));
+}
+
+TEST(NanoStringsTest, StrncpySetsTrailingBytes) {
+ ASSERT_LT(::strlen(kShortString), kStrncpyMax);
+ testStrncpy(kShortString, kStrncpyMax);
+}
+
+TEST(NanoStringsTest, StrncpyMax) {
+ ASSERT_GT(::strlen(kLongString), kStrncpyMax);
+ testStrncpy(kLongString, kStrncpyMax);
+}
+
+TEST(NanoStringsTest, StrncpyNothing) {
+ testStrncpy(kLongString, 0);
+}
+
+TEST(NanoStringsTest, StrncpyExactFit) {
+ ASSERT_EQ(::strlen(kExactString), kStrncpyMax);
+ testStrncpy(kExactString, kStrncpyMax);
+}
+
+
+static void testHexAscii(uint32_t value, const char *str) {
+ static constexpr size_t kAsciiLen =
+ nanoapp_testing::kUint32ToHexAsciiBufferMinLen;
+
+ char array[kAsciiLen + 1];
+ array[kAsciiLen] = kUnsetValue;
+ uint32ToHexAscii(array, sizeof(array), value);
+ EXPECT_EQ(kUnsetValue, array[kAsciiLen]);
+ array[kAsciiLen] = '\0';
+ EXPECT_STREQ(str, array);
+}
+
+TEST(NanoStringsTest, Uint32ToHexAscii) {
+ testHexAscii(0x1234ABCD, "0x1234ABCD");
+}
+
+TEST(NanoStringsTest, Uint32ToHexAsciiMin) {
+ testHexAscii(0, "0x00000000");
+}
+
+TEST(NanoStringsTest, Uint32ToHexAsciiMax) {
+ testHexAscii(0xFFFFFFFF, "0xFFFFFFFF");
+}
+
diff --git a/apps/chqts/src/shared/send_message.cc b/apps/chqts/src/shared/send_message.cc
new file mode 100644
index 0000000..f535dd5
--- /dev/null
+++ b/apps/chqts/src/shared/send_message.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shared/send_message.h>
+
+#include <inttypes.h>
+
+#include <shared/abort.h>
+#include <shared/dumb_allocator.h>
+#include <shared/nano_endian.h>
+#include <shared/nano_string.h>
+
+#include <chre.h>
+
+namespace nanoapp_testing {
+
+constexpr size_t kAllocSize = 128;
+
+static DumbAllocator<kAllocSize, 4> gDumbAlloc;
+
+static void freeDumbAllocMessage(void *message, size_t messageSize) {
+ if (messageSize > kAllocSize) {
+ uint32_t localSize = uint32_t(messageSize);
+ sendFatalFailureToHost("freeDumbAllocMessage given oversized message:",
+ &localSize);
+ }
+ if (!gDumbAlloc.free(message)) {
+ uint32_t localPtr =
+ reinterpret_cast<size_t>(message) & UINT32_C(0xFFFFFFFF);
+ sendFatalFailureToHost("freeDumbAllocMessage given bad pointer:",
+ &localPtr);
+ }
+}
+
+static void freeHeapMessage(void *message, size_t /* messageSize */) {
+ if (gDumbAlloc.contains(message)) {
+ uint32_t localPtr =
+ reinterpret_cast<size_t>(message) & UINT32_C(0xFFFFFFFF);
+ sendFatalFailureToHost("freeHeapMessage given DumbAlloc pointer:",
+ &localPtr);
+ }
+ chreHeapFree(message);
+}
+
+static void fatalError() {
+ // Attempt to send a context-less failure message, in the hopes that
+ // might get through.
+ chreSendMessageToHost(nullptr, 0,
+ static_cast<uint32_t>(MessageType::kFailure),
+ nullptr);
+ // Whether or not that made it through, unambigiously fail this test
+ // by aborting.
+ nanoapp_testing::abort();
+}
+
+// TODO(b/32114261): Remove this method.
+static bool needToPrependMessageType() {
+ // TODO: When we have a new API that properly send the messageType,
+ // this method should get the API version and return appropriately.
+ // Eventually we should remove this hacky method.
+ return true;
+}
+
+static void *getMessageMemory(size_t *size, bool *dumbAlloc) {
+ if (needToPrependMessageType()) {
+ *size += sizeof(uint32_t);
+ }
+ void *ret = gDumbAlloc.alloc(*size);
+ if (ret != nullptr) {
+ *dumbAlloc = true;
+ } else {
+ // Not expected, but possible if the CHRE is lagging in freeing
+ // these messages, or if we're sending a huge message.
+ *dumbAlloc = false;
+ ret = chreHeapAlloc(*size);
+ if (ret == nullptr) {
+ fatalError();
+ }
+ }
+ return ret;
+}
+
+// TODO(b/32114261): Remove this method.
+static void *prependMessageType(MessageType messageType, void *memory) {
+ if (!needToPrependMessageType()) {
+ return memory;
+ }
+ uint32_t type = static_cast<uint32_t>(messageType);
+ nanoapp_testing::hostToLittleEndian(&type);
+ memcpy(memory, &type, sizeof(type));
+ uint8_t *ptr = static_cast<uint8_t*>(memory);
+ ptr += sizeof(type);
+ return ptr;
+}
+
+static void internalSendMessage(MessageType messageType, void *data,
+ size_t dataSize, bool dumbAlloc) {
+ // Note that if the CHRE implementation occasionally drops a message
+ // here, then tests will become flaky. For now, we consider that to
+ // be a flaky CHRE implementation which should fail testing.
+ if (!chreSendMessageToHost(data, dataSize,
+ static_cast<uint32_t>(messageType),
+ dumbAlloc ? freeDumbAllocMessage :
+ freeHeapMessage)) {
+ fatalError();
+ }
+}
+
+void sendMessageToHost(MessageType messageType, const void *data,
+ size_t dataSize) {
+ if ((dataSize == 0) && (data != nullptr)) {
+ sendInternalFailureToHost("Bad sendMessageToHost args");
+ }
+ bool dumbAlloc = true;
+ size_t fullMessageSize = dataSize;
+ void *myMessageBase = getMessageMemory(&fullMessageSize, &dumbAlloc);
+ void *ptr = prependMessageType(messageType, myMessageBase);
+ memcpy(ptr, data, dataSize);
+ internalSendMessage(messageType, myMessageBase, fullMessageSize, dumbAlloc);
+}
+
+void sendStringToHost(MessageType messageType, const char *message,
+ const uint32_t *value) {
+ if (message == nullptr) {
+ sendInternalFailureToHost("sendStringToHost 'message' is NULL");
+ }
+ bool dumbAlloc = true;
+ const size_t messageStrlen = strlen(message);
+ size_t myMessageLen = messageStrlen;
+ if (value != nullptr) {
+ myMessageLen += kUint32ToHexAsciiBufferMinLen;
+ }
+ // Add null terminator
+ myMessageLen++;
+
+ size_t fullMessageLen = myMessageLen;
+ char *fullMessage =
+ static_cast<char*>(getMessageMemory(&fullMessageLen, &dumbAlloc));
+ char *ptr = static_cast<char*>(prependMessageType(messageType,
+ fullMessage));
+ memcpy(ptr, message, messageStrlen);
+ ptr += messageStrlen;
+ if (value != nullptr) {
+ uint32ToHexAscii(ptr, fullMessageLen - (ptr - fullMessage), *value);
+ }
+ // Add the terminator.
+ fullMessage[fullMessageLen - 1] = '\0';
+
+ internalSendMessage(messageType, fullMessage, fullMessageLen, dumbAlloc);
+}
+
+// Before we abort the nanoapp, we also put this message in the chreLog().
+// We have no assurance our message will make it to the Host (not required
+// for CHRE implementations), but this will at least make sure our message
+// hits the log.
+static void logFatalMessage(const char *message, const uint32_t *value) {
+ if (value != nullptr) {
+ chreLog(CHRE_LOG_ERROR, "TEST ABORT: %s0x%08" PRIX32, message, *value);
+ } else {
+ chreLog(CHRE_LOG_ERROR, "TEST ABORT: %s", message);
+ }
+}
+
+void sendFatalFailureToHost(const char *message, const uint32_t *value,
+ AbortBlame reason) {
+ sendFailureToHost(message, value);
+ logFatalMessage(message, value);
+ nanoapp_testing::abort(reason);
+}
+
+void sendInternalFailureToHost(const char *message, const uint32_t *value,
+ AbortBlame reason) {
+ sendStringToHost(MessageType::kInternalFailure, message, value);
+ logFatalMessage(message, value);
+ nanoapp_testing::abort(reason);
+}
+
+} // namespace nanoapp_testing
diff --git a/apps/chqts/src/shared/send_message.h b/apps/chqts/src/shared/send_message.h
new file mode 100644
index 0000000..18919ac
--- /dev/null
+++ b/apps/chqts/src/shared/send_message.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GTS_NANOAPPS_SHARED_SEND_MESSAGE_H_
+#define _GTS_NANOAPPS_SHARED_SEND_MESSAGE_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include <shared/abort.h>
+
+/**
+ * NOTE: The MessageType values are manually synced in the GTS Java's
+ * ContextHubTestConstants.java. If you make a change here, be sure
+ * to update ContextHubTestContants.java as well.
+ */
+
+namespace nanoapp_testing {
+
+/**
+ * Messages types which are sent between Nanoapps and the Java Host testing
+ * code.
+ */
+enum class MessageType : uint32_t {
+ /**
+ * Value which should never be used.
+ *
+ * This value starts at CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE.
+ *
+ * This type should never be sent by Host or Nanoapp code.
+ */
+ kInvalidMessageType = 0x0400,
+
+ /**
+ * Test has completed in success.
+ *
+ * This type should only be sent by the Nanoapp code.
+ */
+ kSuccess = 0x0401,
+
+ /**
+ * Indicates a failure in the CHRE implementation.
+ *
+ * This should be followed by null-terminated string
+ * giving details of failure.
+ *
+ * This type should only be sent by the Nanoapp code.
+ */
+ kFailure = 0x0402,
+
+ /**
+ * Indicate a failure within the testing infrastructure.
+ *
+ * This should be followed by null-terminated string
+ * giving details of failure.
+ *
+ * This type should only be sent by the Nanoapp code.
+ */
+ kInternalFailure = 0x0403,
+
+ /**
+ * Indicate a test is being skipped.
+ *
+ * This should be followed by null-terminated string
+ * giving an explanation of why this test was skipped.
+ *
+ * This type should only be sent by the Nanoapp code.
+ */
+ kSkipped = 0x0404,
+
+ /**
+ * A generic message indicating that the test should continue.
+ *
+ * The meaning of this generic message depends on the specific test.
+ * In general, it means something along the lines of "The test is
+ * successful thus far, please proceed to the next stage."
+ *
+ * This type can be sent by the Host or Nanoapp code.
+ */
+ kContinue = 0x0405,
+
+ // Tests wanting to add custom message types for their protocols should
+ // add them below. Remember to update ContextHubTestConstants.java as
+ // well (see NOTE at the top of this header).
+};
+
+/**
+ * Sends a message to the host with the given data.
+ *
+ * This method will make a copy of 'data', so there's no need for the
+ * caller to keep that alive after this call.
+ *
+ * Note it may often be more convenient to use one the other methods
+ * when sending a text string.
+ *
+ * @param messageType The type of the message.
+ * @param data The data to send. This can be nullptr, but then 'dataSize'
+ * must be 0.
+ * @param dataSize The number of bytes of 'data' to send. If 'data' is
+ * not 'nullptr', then this must be non-zero.
+ */
+void sendMessageToHost(MessageType messageType, const void *data = nullptr,
+ size_t dataSize = 0);
+
+/**
+ * Sends a message to the host, optionally with the 'value' appended as in
+ * hex.
+ *
+ * This method will make a copy of 'message' and 'value', so there's no
+ * need for the caller to keep those alive after this call.
+ *
+ * Note it may often be more convenient to use one of the other methods
+ * below.
+ *
+ * @param messageType The type of the message.
+ * @param message The text of the message. This cannot be nullptr.
+ * @param value Optional, defaults to nullptr. If non-null, this value will
+ * be output as hexadecimal at the end of the message to the host.
+ */
+void sendStringToHost(MessageType messageType, const char *message,
+ const uint32_t *value = nullptr);
+
+/**
+ * Same as sendStringToHost(), but using MessageType::kFailure for the 'status'.
+ */
+inline void sendFailureToHost(const char *message,
+ const uint32_t *value = nullptr) {
+ sendStringToHost(MessageType::kFailure, message, value);
+}
+
+/**
+ * Same as sendFailureToHost(), but aborts the test with the given 'reason',
+ * and never returns.
+ */
+void sendFatalFailureToHost(const char *message,
+ const uint32_t *value = nullptr,
+ AbortBlame reason = AbortBlame::kChre);
+
+/**
+ * Same as sendStringToHost(), but uses MessageType::kInternalFailure for the
+ * 'status', and aborts the test with the given 'reason' and never returns.
+ */
+void sendInternalFailureToHost(const char *message,
+ const uint32_t *value = nullptr,
+ AbortBlame reason = AbortBlame::kTestFramework);
+
+/**
+ * Invoke sendMessageToHost with MessageType::kSuccess and no other information.
+ */
+inline void sendSuccessToHost() {
+ sendMessageToHost(MessageType::kSuccess);
+}
+
+
+} // namespace nanoapp_testing
+
+
+#endif // _GTS_NANOAPPS_SHARED_SEND_MESSAGE_H_
diff --git a/apps/gnss_world/gnss_world.cc b/apps/gnss_world/gnss_world.cc
index 78d9691..f324efb 100644
--- a/apps/gnss_world/gnss_world.cc
+++ b/apps/gnss_world/gnss_world.cc
@@ -17,6 +17,7 @@
#include <chre.h>
#include <cinttypes>
+#include "chre/util/macros.h"
#include "chre/util/nanoapp/log.h"
#include "chre/util/time.h"
@@ -27,29 +28,99 @@
namespace {
#endif // CHRE_NANOAPP_INTERNAL
-//! A dummy cookie to pass into the location session start async request.
-const uint32_t kLocationSessionStartCookie = 0x1337;
-
-//! The interval between location updates.
-constexpr Milliseconds kLocationInterval(Seconds(10));
+//! A dummy cookie to pass into the location session async request.
+const uint32_t kLocationSessionCookie = 0x1337;
//! The minimum time to the next fix for a location.
constexpr Milliseconds kLocationMinTimeToNextFix(0);
+//! The interval in seconds between location updates.
+const uint32_t kLocationIntervals[] = {
+ 30,
+ 15,
+ 30,
+ 15,
+ 0,
+ 10,
+};
+
+//! Whether Gnss Location capability is supported by the platform
+bool gLocationSupported = false;
+
+uint32_t gTimerHandle;
+uint32_t gTimerCount = 0;
+
+//! Whether an async result has been received.
+bool gAsyncResultReceived = false;
+
+void makeLocationRequest() {
+ uint32_t interval = kLocationIntervals[gTimerCount++];
+ LOGI("Modifying location update interval to %" PRIu32 " sec", interval);
+
+ if (interval > 0) {
+ if (chreGnssLocationSessionStartAsync(
+ interval * 1000,
+ kLocationMinTimeToNextFix.getMilliseconds(),
+ &kLocationSessionCookie)) {
+ LOGI("Location session start request sent");
+ } else {
+ LOGE("Error sending location session start request");
+ }
+ } else {
+ if (chreGnssLocationSessionStopAsync(
+ &kLocationSessionCookie)) {
+ LOGI("Location session stop request sent");
+ } else {
+ LOGE("Error sending location session stop request");
+ }
+ }
+
+ // set a timer to verify reception of async result.
+ gTimerHandle = chreTimerSet(
+ CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS, /* 5 sec in CHRE 1.1 */
+ nullptr /* data */, true /* oneShot */);
+}
+
+void handleTimerEvent(const void *eventData) {
+ LOGI("Timer event received, count %d", gTimerCount);
+ if (!gAsyncResultReceived) {
+ LOGE("Async result not received!");
+ }
+ gAsyncResultReceived = false;
+
+ if (gLocationSupported && gTimerCount < ARRAY_SIZE(kLocationIntervals)) {
+ makeLocationRequest();
+ }
+}
+
void handleGnssAsyncResult(const chreAsyncResult *result) {
if (result->requestType == CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START) {
if (result->success) {
LOGI("Successfully requested a GNSS location session");
+ gAsyncResultReceived = true;
} else {
LOGE("Error requesting GNSS scan monitoring with %" PRIu8,
result->errorCode);
}
- if (result->cookie != &kLocationSessionStartCookie) {
+ if (result->cookie != &kLocationSessionCookie) {
LOGE("Location session start request cookie mismatch");
}
+ } else if (result->requestType
+ == CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP) {
+ if (result->success) {
+ LOGI("Successfully stopped a GNSS location session");
+ gAsyncResultReceived = true;
+ } else {
+ LOGE("Error stoppinging GNSS scan monitoring with %" PRIu8,
+ result->errorCode);
+ }
+
+ if (result->cookie != &kLocationSessionCookie) {
+ LOGE("Location session stop request cookie mismatch");
+ }
} else {
- LOGE("Received invalid async result");
+ LOGE("Received invalid async result %" PRIu8, result->requestType);
}
}
@@ -73,9 +144,11 @@
case CHRE_GNSS_CAPABILITIES_LOCATION
| CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
gnssCapabilitiesStr = "LOCATION | MEASUREMENTS";
+ gLocationSupported = true;
break;
case CHRE_GNSS_CAPABILITIES_LOCATION:
gnssCapabilitiesStr = "LOCATION";
+ gLocationSupported = true;
break;
case CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
gnssCapabilitiesStr = "MEASUREMENTS";
@@ -90,15 +163,8 @@
LOGI("Detected GNSS support as: %s (%" PRIu32 ")",
gnssCapabilitiesStr, gnssCapabilities);
- if (gnssCapabilities & CHRE_GNSS_CAPABILITIES_LOCATION) {
- if (chreGnssLocationSessionStartAsync(
- kLocationInterval.getMilliseconds(),
- kLocationMinTimeToNextFix.getMilliseconds(),
- &kLocationSessionStartCookie)) {
- LOGI("Location session start request sent");
- } else {
- LOGE("Error sending location session request");
- }
+ if (gLocationSupported) {
+ makeLocationRequest();
}
return true;
@@ -115,6 +181,9 @@
handleGnssLocationEvent(
static_cast<const chreGnssLocationEvent *>(eventData));
break;
+ case CHRE_EVENT_TIMER:
+ handleTimerEvent(eventData);
+ break;
default:
LOGW("Unhandled event type %" PRIu16, eventType);
}
diff --git a/apps/imu_cal/imu_cal.cc b/apps/imu_cal/imu_cal.cc
index abaca97..f2cb5e2 100644
--- a/apps/imu_cal/imu_cal.cc
+++ b/apps/imu_cal/imu_cal.cc
@@ -64,7 +64,7 @@
[SENSOR_INDEX_TEMP] = {
.type = CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE,
.enable = true,
- .interval = Seconds(1).toRawNanoseconds(),
+ .interval = Milliseconds(500).toRawNanoseconds(),
.highPerformanceLatency = 0,
// TODO(b/63908396): this sensor should be disabled in stand-by mode
.standByLatency = Seconds(60).toRawNanoseconds(),
diff --git a/apps/imu_cal/imu_cal.mk b/apps/imu_cal/imu_cal.mk
index 3b97639..1c6f1af 100644
--- a/apps/imu_cal/imu_cal.mk
+++ b/apps/imu_cal/imu_cal.mk
@@ -34,7 +34,6 @@
COMMON_CFLAGS += -DNANO_SENSOR_CAL_DBG_ENABLED
# Debug options.
-#COMMON_CFLAGS += -DGYRO_CAL_DBG_TUNE_ENABLED
#COMMON_CFLAGS += -DOVERTEMPCAL_DBG_LOG_TEMP
#COMMON_CFLAGS += -DIMU_TEMP_DBG_ENABLED
diff --git a/apps/imu_cal/nano_calibration.cc b/apps/imu_cal/nano_calibration.cc
index 627dbf4..c2dc5b0 100644
--- a/apps/imu_cal/nano_calibration.cc
+++ b/apps/imu_cal/nano_calibration.cc
@@ -24,6 +24,7 @@
#include "calibration/util/cal_log.h"
#include "chre/util/nanoapp/log.h"
+#include "common/math/macros.h"
namespace nano_calibration {
@@ -56,15 +57,22 @@
chreLogNull(format, ##__VA_ARGS__)
#endif // NANO_SENSOR_CAL_DBG_ENABLED
+// Indicates and invalid sensor temperature.
+constexpr float kInvalidTemperatureCelsius = -274.0f;
+
+#ifdef GYRO_CAL_ENABLED
// Limits NanoSensorCal gyro notifications to once every minute.
-constexpr uint64_t kNanoSensorCalMessageIntervalNanos = 60e9;
+constexpr uint64_t kNanoSensorCalMessageIntervalNanos = MIN_TO_NANOS(1);
+#endif // GYRO_CAL_ENABLED
+
+#ifdef MAG_CAL_ENABLED
+// Unit conversion from nanoseconds to microseconds.
+constexpr float kNanoToMicroseconds = 1e-3f;
+#endif // MAG_CAL_ENABLED
#ifdef SPHERE_FIT_ENABLED
constexpr size_t kSamplesToAverageForOdrEstimateMag = 10;
-// Unit conversion: nanoseconds to seconds.
-constexpr float kNanosToSec = 1.0e-9f;
-
// Helper function that estimates the ODR based on the incoming data timestamp.
void SamplingRateEstimate(struct SampleRateData *sample_rate_data,
float *mean_sampling_rate_hz,
@@ -79,7 +87,7 @@
*mean_sampling_rate_hz =
sample_rate_data->num_samples /
(static_cast<float>(sample_rate_data->time_delta_accumulator) *
- kNanosToSec);
+ NANOS_TO_SEC);
} else {
// Not enough samples to compute a valid sample rate estimate. Indicate
// this with a -1 value.
@@ -296,6 +304,9 @@
ResetCalParams(&accel_cal_params_);
ResetCalParams(&gyro_cal_params_);
ResetCalParams(&mag_cal_params_);
+
+ // Initializes sensor temperature.
+ temperature_celsius_ = kInvalidTemperatureCelsius;
}
void NanoSensorCal::Initialize() {
@@ -323,21 +334,25 @@
// Initializes the gyroscope offset calibration algorithm.
gyroCalInit(
&gyro_cal_,
- 1.4e9, // min stillness period = 1.4 seconds
- 1.5e9, // max stillness period = 1.5 seconds
- 0, 0, 0, // initial bias offset calibration
- 0, // time stamp of initial bias calibration
- 0.5e9f, // analysis window length = 0.5 seconds
- 3.0e-5f, // gyroscope variance threshold [rad/sec]^2
- 3.0e-6f, // gyroscope confidence delta [rad/sec]^2
- 9.0e-3f, // accelerometer variance threshold [m/sec^2]^2
- 9.0e-4f, // accelerometer confidence delta [m/sec^2]^2
- 5.0f, // magnetometer variance threshold [uT]^2
- 1.0f, // magnetometer confidence delta [uT]^2
- 0.95f, // stillness threshold [0,1]
- 60.0f * kPi / 180.0f, // stillness mean variation limit [rad/sec]
- 1.5f, // maximum temperature deviation during stillness [C]
- true); // gyro calibration enable
+ SEC_TO_NANOS(1.4f), // Min stillness period = 1.4 seconds
+ SEC_TO_NANOS(1.4f), // Max stillness period = 1.5 seconds (NOTE 1)
+ 0, 0, 0, // Initial bias offset calibration
+ 0, // Time stamp of initial bias calibration
+ SEC_TO_NANOS(0.5f), // Analysis window length = 0.5 seconds
+ 3.0e-5f, // Gyroscope variance threshold [rad/sec]^2
+ 3.0e-6f, // Gyroscope confidence delta [rad/sec]^2
+ 4.5e-3f, // Accelerometer variance threshold [m/sec^2]^2
+ 9.0e-4f, // Accelerometer confidence delta [m/sec^2]^2
+ 5.0f, // Magnetometer variance threshold [uT]^2
+ 1.0f, // Magnetometer confidence delta [uT]^2
+ 0.95f, // Stillness threshold [0,1]
+ 60.0f * MDEG_TO_RAD, // Stillness mean variation limit [rad/sec]
+ 1.5f, // Max temperature delta during stillness [C]
+ true); // Gyro calibration enable
+ // NOTE 1: This parameter is set to 1.4 seconds to achieve a max stillness
+ // period of 1.5 seconds and avoid buffer boundary conditions that could push
+ // the max stillness to the next multiple of the analysis window length
+ // (i.e., 2.0 seconds).
#ifdef OVERTEMPCAL_GYRO_ENABLED
// Initializes the over-temperature compensated gyroscope (OTC-Gyro) offset
@@ -345,14 +360,14 @@
overTempCalInit(
&over_temp_gyro_cal_,
5, // Min num of points to enable model update
- 100000000, // Min temperature update interval [nsec]
+ SEC_TO_NANOS(0.1f), // Min temperature update interval [nsec]
0.75f, // Temperature span of bin method [C]
- 40.0e-3f * kPi / 180.0f, // Jump tolerance [rad/sec]
- 250.0e-3f * kPi / 180.0f, // Outlier rejection tolerance [rad/sec]
- 172800000000000, // Model data point age limit [nsec]
- 250.0e-3f * kPi / 180.0f, // Limit for temp. sensitivity [rad/sec/C]
- 8.0f * kPi / 180.0f, // Limit for model intercept parameter [rad/sec]
- 0.1e-3f * kPi / 180.0f, // Significant offset change [rad/sec]
+ 40.0f * MDEG_TO_RAD, // Jump tolerance [rad/sec]
+ 100.0f * MDEG_TO_RAD, // Outlier rejection tolerance [rad/sec]
+ DAYS_TO_NANOS(2), // Model data point age limit [nsec]
+ 250.0f * MDEG_TO_RAD, // Limit for temp. sensitivity [rad/sec/C]
+ 8.0e3f * MDEG_TO_RAD, // Limit for model intercept parameter [rad/sec]
+ 0.1f * MDEG_TO_RAD, // Significant offset change [rad/sec]
true); // Over-temp compensation enable
#endif // OVERTEMPCAL_GYRO_ENABLED
@@ -371,18 +386,18 @@
// TODO: Replace function parameters with a struct, to avoid swapping them per
// accident.
initMagCalSphere(&mag_cal_sphere_,
- 0.0f, 0.0f, 0.0f, // bias x, y, z.
- 1.0f, 0.0f, 0.0f, // c00, c01, c02.
- 0.0f, 1.0f, 0.0f, // c10, c11, c12.
- 0.0f, 0.0f, 1.0f, // c20, c21, c22.
+ 0.0f, 0.0f, 0.0f, // Bias x, y, z
+ 1.0f, 0.0f, 0.0f, // c00, c01, c02
+ 0.0f, 1.0f, 0.0f, // c10, c11, c12
+ 0.0f, 0.0f, 1.0f, // c20, c21, c22
7357000, // min_batch_window_in_micros
- 15, // min_num_diverse_vectors.
- 1, // max_num_max_distance.
- 5.0f, // var_threshold.
- 8.0f, // max_min_threshold.
- 48.f, // local_field.
- 0.49f, // threshold_tuning_param.
- 2.5f); // max_distance_tuning_param.
+ 15, // min_num_diverse_vectors
+ 1, // max_num_max_distance
+ 5.0f, // var_threshold
+ 8.0f, // max_min_threshold
+ 48.f, // local_field
+ 0.49f, // threshold_tuning_param
+ 2.5f); // max_distance_tuning_param
magCalSphereOdrUpdate(&mag_cal_sphere_, 50 /* Default sample rate Hz */);
// ODR init.
@@ -505,8 +520,8 @@
// TODO: Factor common code to shorten function and improve readability.
void NanoSensorCal::HandleSensorSamplesGyroCal(
uint16_t event_type, const chreSensorThreeAxisData *event_data) {
- uint64_t timestamp_nanos = 0;
#ifdef GYRO_CAL_ENABLED
+ uint64_t timestamp_nanos = 0;
// Only updates the gyroscope calibration algorithm when measured
// temperature is valid.
if (temperature_celsius_ <= kInvalidTemperatureCelsius) {
diff --git a/apps/imu_cal/nano_calibration.h b/apps/imu_cal/nano_calibration.h
index badfdea..8c195a0 100644
--- a/apps/imu_cal/nano_calibration.h
+++ b/apps/imu_cal/nano_calibration.h
@@ -57,16 +57,6 @@
namespace nano_calibration {
-// TODO: Clean up, unifying constexpr variables location.
-// Indicates and invalid sensor temperature.
-constexpr float kInvalidTemperatureCelsius = -274.0f;
-
-// The mathematical constant, Pi.
-constexpr float kPi = 3.141592653589793238f;
-
-// Unit conversion from nanoseconds to microseconds.
-constexpr float kNanoToMicroseconds = 1e-3f;
-
// Data struct for sample rate estimate fuction. Visible for the class in order
// to allow usage in all algorithms.
struct SampleRateData {
@@ -155,6 +145,9 @@
#ifdef GYRO_CAL_ENABLED
// Gyroscope runtime calibration.
struct GyroCal gyro_cal_;
+
+ // Used to limit the rate of gyro debug notification messages.
+ uint64_t gyro_notification_time_check_ = 0;
#ifdef OVERTEMPCAL_GYRO_ENABLED
// Gyroscope over-temperature runtime calibration.
struct OverTempCal over_temp_gyro_cal_;
@@ -171,9 +164,6 @@
#endif // SPHERE_FIT_ENABLED
#endif // MAG_CAL_ENABLED
- // Used to limit the rate of gyro debug notification messages.
- uint64_t gyro_notification_time_check_ = 0;
-
// Flag to indicate whether the NanoSensorCal object has been initialized.
bool nanosensorcal_initialized_ = false;
@@ -183,7 +173,7 @@
mutable bool mag_calibration_ready_ = false;
// Sensor temperature.
- float temperature_celsius_ = kInvalidTemperatureCelsius;
+ float temperature_celsius_;
// Sensor calibration parameter containers.
struct ashCalParams accel_cal_params_;
diff --git a/apps/wifi_offload/Android.bp b/apps/wifi_offload/Android.bp
new file mode 100644
index 0000000..ce2a07b
--- /dev/null
+++ b/apps/wifi_offload/Android.bp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+cc_library_static {
+ name: "wifi_offload_types",
+ export_include_dirs: [
+ "include",
+ ],
+ srcs: [
+ "channel_histogram.cc",
+ "flatbuffers_serialization.cc",
+ "preferred_network.cc",
+ "rpc_log_record.cc",
+ "scan_config.cc",
+ "scan_filter.cc",
+ "scan_params.cc",
+ "scan_record.cc",
+ "scan_result.cc",
+ "scan_result_message.cc",
+ "scan_stats.cc",
+ "ssid.cc",
+ "utility.cc",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ header_libs: [
+ "chre_flatbuffers",
+ "chre_api",
+ ],
+ export_header_lib_headers: [
+ "chre_flatbuffers",
+ "chre_api",
+ ],
+ vendor: true,
+ proprietary: true,
+}
diff --git a/apps/wifi_offload/README.md b/apps/wifi_offload/README.md
new file mode 100644
index 0000000..305b072
--- /dev/null
+++ b/apps/wifi_offload/README.md
@@ -0,0 +1,4 @@
+This folder contains definitions for data types and serialization APIs used for
+sending messages between wifi_offload nanoapp and offload HAL. This library is
+being included by both offload HAL and wifi_offload nanoapp, and is NOT a
+standard nanoapp by itself.
diff --git a/apps/wifi_offload/channel_histogram.cc b/apps/wifi_offload/channel_histogram.cc
new file mode 100644
index 0000000..03dff89
--- /dev/null
+++ b/apps/wifi_offload/channel_histogram.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "chre/apps/wifi_offload/utility.h"
+
+namespace wifi_offload {
+namespace {
+
+/* Strictly increasing sequence of supported channel numbers in
+ * 2.4GHz (802.11b/g/n) and 5GHz (802.11a/h/j/n/ac) */
+constexpr uint8_t kAllChannels[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120,
+ 122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 149, 151, 153,
+ 155, 157, 159, 161, 165, 183, 184, 185, 187, 188, 189, 192, 196,
+};
+static_assert(sizeof(kAllChannels) / sizeof(kAllChannels[0]) ==
+ ChannelHistogram::kNumChannels,
+ "some elements unspecified");
+
+/**
+ * Returns the channel number of a given frequency based on 802.11.
+ *
+ * @param frequency Frequncy of the channel in MHz
+ *
+ * @return Channel number of the given frequency. Zero if unsupported
+ * frequency or channel number. Returned value will be in the range of
+ * [0, 255]
+ */
+uint8_t GetChannelNumber(uint32_t frequency) {
+ int channel_number =
+ utility::Ieee80211FrequencyToChannel(static_cast<int>(frequency));
+ if (channel_number <= 0 || channel_number > 255) {
+ LOGE("Unknown channel frequency %" PRIu32 " MHz.", frequency);
+ channel_number = 0;
+ }
+ return static_cast<uint8_t>(channel_number);
+}
+
+/**
+ * Returns the index of a given channel number in kAllChannels.
+ *
+ * @param channel_number Channel number we want to map to an index
+ *
+ * @return Index of the given channel number in kAllChannels. kNumChannels if
+ * not found. Returned value will be in the range of [0, kNumChannels]
+ */
+size_t GetChannelIndex(uint8_t channel_number) {
+ for (size_t i = 0; i < ChannelHistogram::kNumChannels; i++) {
+ if (channel_number == kAllChannels[i]) {
+ return i;
+ }
+ }
+
+ LOGE("Unsupported channel number: %" PRIu8, channel_number);
+ return ChannelHistogram::kNumChannels;
+}
+
+} // namespace
+
+ChannelHistogram::ChannelHistogram() {
+ std::memset(scan_count_internal_high_res_, 0,
+ sizeof(scan_count_internal_high_res_));
+}
+
+bool ChannelHistogram::IsSupportedFrequency(uint32_t frequency) {
+ return GetChannelNumber(frequency) != 0;
+}
+
+uint8_t ChannelHistogram::GetChannelScanCount(uint8_t channel_number) const {
+ size_t index = GetChannelIndex(channel_number);
+ if (index == kNumChannels) {
+ return 0;
+ }
+
+ if (scan_count_internal_high_res_[index] == 0) {
+ return 0;
+ }
+
+ uint32_t max_count = 0;
+ // since there is at least one non-zero value, max_count won't be 0
+ for (const auto count : scan_count_internal_high_res_) {
+ if (max_count < count) {
+ max_count = count;
+ }
+ }
+
+ // linearly map from [1,max_count] to [1,255]
+ uint64_t scaled_value = scan_count_internal_high_res_[index];
+ scaled_value = scaled_value * 254 / max_count + 1;
+ return static_cast<uint8_t>(scaled_value);
+}
+
+bool ChannelHistogram::IncrementScanCountForFrequency(uint32_t frequency) {
+ size_t index = GetChannelIndex(GetChannelNumber(frequency));
+ if (index == kNumChannels) {
+ return false;
+ }
+
+ scan_count_internal_high_res_[index]++;
+ return true;
+}
+
+bool ChannelHistogram::IncrementScanCountForFrequencyForTest(
+ uint32_t frequency, uint32_t increase_count) {
+ return IncrementScanCountForChannelForTest(GetChannelNumber(frequency),
+ increase_count);
+}
+
+bool ChannelHistogram::IncrementScanCountForChannelForTest(
+ uint8_t channel, uint32_t increase_count) {
+ size_t index = GetChannelIndex(channel);
+ if (index == kNumChannels) {
+ return false;
+ }
+
+ scan_count_internal_high_res_[index] += increase_count;
+ return true;
+}
+
+bool ChannelHistogram::operator==(const ChannelHistogram &other) const {
+ if (this == &other) {
+ return true;
+ }
+
+ for (const auto channel : kAllChannels) {
+ // Compare scaled values, rather than raw values
+ if (GetChannelScanCount(channel) != other.GetChannelScanCount(channel)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+flatbuffers::Offset<flatbuffers::Vector<uint8_t>> ChannelHistogram::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ uint8_t lowResScanCount[kNumChannels];
+ for (size_t i = 0; i < kNumChannels; i++) {
+ lowResScanCount[i] = GetChannelScanCount(kAllChannels[i]);
+ }
+ return builder->CreateVector(lowResScanCount, kNumChannels);
+}
+
+bool ChannelHistogram::Deserialize(
+ const flatbuffers::Vector<uint8_t> &fbs_scan_count) {
+ if (fbs_scan_count.size() != kNumChannels) {
+ LOGE("Failed to deserialize ChannelHistogram. Null or incomplete members.");
+ return false;
+ }
+
+ for (uint8_t i = 0; i < kNumChannels; i++) {
+ scan_count_internal_high_res_[i] = fbs_scan_count.Get(i);
+ }
+ return true;
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/chre_scan_params_safe.cc b/apps/wifi_offload/chre_scan_params_safe.cc
new file mode 100644
index 0000000..8c4b460
--- /dev/null
+++ b/apps/wifi_offload/chre_scan_params_safe.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include "chre/apps/wifi_offload/chre_scan_params_safe.h"
+#include "chre/apps/wifi_offload/utility.h"
+
+namespace wifi_offload {
+
+ChreScanParamsSafe::ChreScanParamsSafe(const ScanParams &scan_params) {
+ // point chre_scan_params_ pointers to allocated buffers
+ chre_scan_params_.frequencyList = chre_scan_params_frequencies_;
+ chre_scan_params_.ssidList = chre_scan_params_ssids_;
+
+ UpdateFromScanParams(scan_params);
+}
+
+const chreWifiScanParams *ChreScanParamsSafe::GetChreWifiScanParams() {
+ return &chre_scan_params_;
+}
+
+void ChreScanParamsSafe::Log() const {
+ LOGI("chreWifiScanParams:");
+ LOGI(" scan type: %" PRIu8, chre_scan_params_.scanType);
+ LOGI(" max scan age (ms): %" PRIu32, chre_scan_params_.maxScanAgeMs);
+ LOGI(" frequency list length: %" PRIu16, chre_scan_params_.frequencyListLen);
+ for (size_t i = 0; i < chre_scan_params_.frequencyListLen; i++) {
+ LOGI(" frequency: %" PRIu32, chre_scan_params_.frequencyList[i]);
+ }
+ LOGI(" ssid list length: %" PRIu8, chre_scan_params_.ssidListLen);
+ for (size_t i = 0; i < chre_scan_params_.ssidListLen; i++) {
+ utility::LogSsid(chre_scan_params_.ssidList[i].ssid,
+ chre_scan_params_.ssidList[i].ssidLen);
+ }
+}
+
+void ChreScanParamsSafe::UpdateFromScanParams(const ScanParams &scan_params) {
+ chre_scan_params_.scanType = CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS;
+ chre_scan_params_.maxScanAgeMs = 0;
+
+ chre_scan_params_.frequencyListLen = std::min(
+ static_cast<uint16_t>(scan_params.frequencies_to_scan_mhz_.size()),
+ static_cast<uint16_t>(CHRE_WIFI_FREQUENCY_LIST_MAX_LEN));
+ if (chre_scan_params_.frequencyListLen <
+ scan_params.frequencies_to_scan_mhz_.size()) {
+ LOGW("Too many ScanParams frequencies %zu truncated to max %" PRIu16,
+ scan_params.frequencies_to_scan_mhz_.size(),
+ chre_scan_params_.frequencyListLen);
+ }
+
+ std::copy(scan_params.frequencies_to_scan_mhz_.begin(),
+ scan_params.frequencies_to_scan_mhz_.begin() +
+ chre_scan_params_.frequencyListLen,
+ chre_scan_params_frequencies_);
+
+ chre_scan_params_.ssidListLen =
+ std::min(static_cast<uint8_t>(scan_params.ssids_to_scan_.size()),
+ static_cast<uint8_t>(CHRE_WIFI_SSID_LIST_MAX_LEN));
+ if (chre_scan_params_.ssidListLen < scan_params.ssids_to_scan_.size()) {
+ LOGW("Too many ScanParams ssids %zu truncated to max %" PRIu8,
+ scan_params.ssids_to_scan_.size(), chre_scan_params_.ssidListLen);
+ }
+
+ for (size_t i = 0; i < chre_scan_params_.ssidListLen; i++) {
+ scan_params.ssids_to_scan_[i].ToChreWifiSsidListItem(
+ &chre_scan_params_ssids_[i]);
+ }
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/flatbuffers_serialization.cc b/apps/wifi_offload/flatbuffers_serialization.cc
new file mode 100644
index 0000000..0f36222
--- /dev/null
+++ b/apps/wifi_offload/flatbuffers_serialization.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/flatbuffers_serialization.h"
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+namespace wifi_offload {
+namespace fbs {
+
+size_t Serialize(const wifi_offload::ScanStats &stats, uint8_t *buffer,
+ size_t buffer_len) {
+ return Serialize(stats, buffer, buffer_len, "ScanStats");
+}
+
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::ScanStats *stats) {
+ return Deserialize<wifi_offload::ScanStats>(buffer, buffer_len, stats,
+ "ScanStats");
+}
+
+size_t Serialize(const wifi_offload::ScanConfig &config, uint8_t *buffer,
+ size_t buffer_len) {
+ return Serialize(config, buffer, buffer_len, "ScanConfig");
+}
+
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::ScanConfig *config) {
+ return Deserialize<wifi_offload::ScanConfig>(buffer, buffer_len, config,
+ "ScanConfig");
+}
+
+size_t Serialize(const wifi_offload::Vector<wifi_offload::ScanResult> &results,
+ uint8_t *buffer, size_t buffer_len) {
+ wifi_offload::ScanResultMessage msg;
+ msg.SetScanResults(results);
+ return Serialize(msg, buffer, buffer_len, "ScanResults");
+}
+
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::Vector<wifi_offload::ScanResult> *results) {
+ wifi_offload::ScanResultMessage msg;
+ if (Deserialize<wifi_offload::ScanResultMessage>(buffer, buffer_len, &msg,
+ "ScanResults")) {
+ msg.GetScanResults(results);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace fbs
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/channel_histogram.h b/apps/wifi_offload/include/chre/apps/wifi_offload/channel_histogram.h
new file mode 100644
index 0000000..336362a
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/channel_histogram.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_CHANNEL_HISTOGRAM_H_
+#define CHRE_WIFI_OFFLOAD_CHANNEL_HISTOGRAM_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+
+namespace wifi_offload {
+
+/**
+ * A class to keep scan count for each channel. It provides an indexer to access
+ * each channel's scan count with channel number.
+ */
+class ChannelHistogram {
+ public:
+ static constexpr uint8_t kNumChannels = 69;
+
+ ChannelHistogram();
+
+ ~ChannelHistogram() = default;
+
+ static bool IsSupportedFrequency(uint32_t frequency);
+
+ /* Gets the scaled scan count for a given channel number. All scan counts are
+ * scaled such that the largest non-zero count is always mapped to 255 */
+ uint8_t GetChannelScanCount(uint8_t channel_number) const;
+
+ bool IncrementScanCountForFrequency(uint32_t frequency);
+
+ bool IncrementScanCountForFrequencyForTest(uint32_t frequency,
+ uint32_t increase_count);
+
+ bool IncrementScanCountForChannelForTest(uint8_t channel,
+ uint32_t increase_count);
+
+ bool operator==(const ChannelHistogram &other) const;
+
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const flatbuffers::Vector<uint8_t> &fbs_scan_count);
+
+ private:
+ uint32_t scan_count_internal_high_res_[kNumChannels];
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_CHANNEL_HISTOGRAM_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/chre_scan_params_safe.h b/apps/wifi_offload/include/chre/apps/wifi_offload/chre_scan_params_safe.h
new file mode 100644
index 0000000..c6c42c1
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/chre_scan_params_safe.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_CHRE_SCAN_PARAMS_SAFE_H_
+#define CHRE_WIFI_OFFLOAD_CHRE_SCAN_PARAMS_SAFE_H_
+
+#include "chre/apps/wifi_offload/scan_params.h"
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+namespace wifi_offload {
+
+/* A class to encapsulate chreWifiScanParams struct with pointers to its const
+ * fields and some useful conversion methods */
+class ChreScanParamsSafe {
+ public:
+ ChreScanParamsSafe() = delete;
+
+ ChreScanParamsSafe(const ScanParams &scan_params);
+
+ ~ChreScanParamsSafe() = default;
+
+ const chreWifiScanParams *GetChreWifiScanParams();
+
+ void Log() const;
+
+ private:
+ void UpdateFromScanParams(const ScanParams &scan_params);
+
+ chreWifiScanParams chre_scan_params_;
+ // Storage for *(chre_scan_params_.frequencyList)
+ uint32_t chre_scan_params_frequencies_[CHRE_WIFI_FREQUENCY_LIST_MAX_LEN];
+ // Storage for *(chre_scan_params_.ssidList)
+ chreWifiSsidListItem chre_scan_params_ssids_[CHRE_WIFI_SSID_LIST_MAX_LEN];
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_CHRE_SCAN_PARAMS_SAFE_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/error_codes.h b/apps/wifi_offload/include/chre/apps/wifi_offload/error_codes.h
new file mode 100644
index 0000000..8d3d4ce
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/error_codes.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_ERROR_CODES_H_
+#define CHRE_WIFI_OFFLOAD_ERROR_CODES_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+namespace wifi_offload {
+
+enum ErrorCode : uint32_t {
+ SUCCESS = 0,
+
+ FAILED_TO_ALLOCATE_MESSAGE_BUFFER,
+ FAILED_TO_SERIALIZE_MESSAGE,
+ FAILED_TO_SEND_MESSAGE,
+
+ FAILED_TO_DESERIALIZE_SCAN_CONFIG,
+ INVALID_SUBSCRIBE_MESSAGE_SIZE,
+ SCAN_CONFIG_NOT_INITIALIZED,
+ UNSPECIFIED_HOST_ENDPOINT,
+
+ FAILED_TO_SEND_SCAN_RESULTS,
+ FAILED_TO_SEND_SCAN_STATS,
+
+ SCAN_MONITORING_NOT_SUPPORTED,
+ FAILED_TO_START_SCAN_MONITORING,
+ FAILED_TO_STOP_SCAN_MONITORING,
+ FAILED_TO_CONFIGURE_SCAN_MONITORING_ASYNC,
+
+ ONDEMAND_SCAN_NOT_SUPPORTED,
+ FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST,
+ FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST_ASYNC,
+
+ OUT_OF_ORDER_SCAN_RESULTS,
+ INCOMPLETE_SCAN_RESULTS_BEFORE_SCAN_REQUEST,
+
+ FAILED_TO_SET_SCAN_TIMER,
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_ERROR_CODES_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_serialization.h b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_serialization.h
new file mode 100644
index 0000000..639c36c
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_serialization.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_FLATBUFFERS_SERIALIZATION_H_
+#define CHRE_WIFI_OFFLOAD_FLATBUFFERS_SERIALIZATION_H_
+
+#include <cstring>
+
+/**
+ * @file
+ * Serialize/deserialize API for messages passed between WifiOffload nanoapp
+ * and Offload HAL.
+ */
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/scan_config.h"
+#include "chre/apps/wifi_offload/scan_result.h"
+#include "chre/apps/wifi_offload/scan_result_message.h"
+#include "chre/apps/wifi_offload/scan_stats.h"
+
+namespace wifi_offload {
+namespace fbs {
+
+constexpr size_t kInitialFlatBufferSize = 256;
+
+/**
+ * Serializes the input object into a given buffer.
+ *
+ * @param stats/config/results object to be serialized
+ * @param buffer Buffer to hold the result of serialization. Caller is
+ * responsible for allocating enough buffer size to hold the serialized
+ * data. If buffer is not large enough, serialization will abort and
+ * buffer will stay unmodified. If set to null, serialize function will
+ * return the required buffer size to hold the serialized data.
+ * @param buffer_len Length of the buffer allocated by the caller
+ *
+ * @return zero if buffer is not big enough to hold the serialized data,
+ * otherwise size of serialized data in buffer.
+ */
+size_t Serialize(const wifi_offload::ScanStats &stats, uint8_t *buffer,
+ size_t buffer_len);
+size_t Serialize(const wifi_offload::ScanConfig &config, uint8_t *buffer,
+ size_t buffer_len);
+size_t Serialize(const wifi_offload::Vector<wifi_offload::ScanResult> &results,
+ uint8_t *buffer, size_t buffer_len);
+
+/**
+ * Deserializes from buffer into a given output object.
+ *
+ * @param buffer Buffer that holds the serialized data
+ * @param buffer_len Length of buffer
+ * @param stats/config/results Pointer to the output object to hold the result
+ * of deserialization
+ *
+ * @return true if deserialized successfully, false otherwise
+ */
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::ScanStats *stats);
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::ScanConfig *config);
+bool Deserialize(const uint8_t *buffer, size_t buffer_len,
+ wifi_offload::Vector<wifi_offload::ScanResult> *results);
+
+template <typename SerializeType>
+size_t Serialize(const SerializeType &obj, uint8_t *out_buffer,
+ size_t out_buffer_len, const char *log_tag = "") {
+ flatbuffers::FlatBufferBuilder builder(kInitialFlatBufferSize);
+ const auto fbs_obj = obj.Serialize(&builder);
+ builder.Finish(fbs_obj);
+
+ const uint8_t *data = builder.GetBufferPointer();
+ const size_t size = builder.GetSize();
+
+ if (out_buffer == nullptr) {
+ LOGI("%s output buffer is null. Returning serialized size %zu.", log_tag,
+ size);
+ return size;
+ }
+
+ if (size > out_buffer_len) {
+ LOGE("Serialized %s size %zu too big for provided buffer %zu; dropping",
+ log_tag, size, out_buffer_len);
+ return 0;
+ }
+
+ std::memcpy(out_buffer, data, size);
+ LOGI("Serialized %s to buffer size %zu", log_tag, size);
+ return size;
+}
+
+template <typename SerializeType>
+bool Deserialize(const uint8_t *in_buffer, size_t in_buffer_len,
+ SerializeType *obj, const char *log_tag = "") {
+ if (in_buffer == nullptr || in_buffer_len == 0) {
+ LOGE("%s deserialize buffer is null or has size zero.", log_tag);
+ return false;
+ }
+
+ if (obj == nullptr) {
+ LOGE("%s deserialize output pointer is null.", log_tag);
+ return false;
+ }
+
+ flatbuffers::Verifier verifier(in_buffer, in_buffer_len);
+ if (!verifier.VerifyBuffer<typename SerializeType::FbsType>(nullptr)) {
+ LOGE("Failed to verify %s deserialize buffer.", log_tag);
+ return false;
+ }
+
+ const auto fbs_obj =
+ flatbuffers::GetRoot<typename SerializeType::FbsType>(in_buffer);
+ if (fbs_obj == nullptr) {
+ LOGE("Deserialized %s object is null or has missing members.", log_tag);
+ return false;
+ }
+
+ return obj->Deserialize(*fbs_obj);
+}
+
+} // namespace fbs
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_FLATBUFFERS_SERIALIZATION_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types.fbs b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types.fbs
new file mode 100644
index 0000000..5848f87
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types.fbs
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace wifi_offload.fbs;
+
+/* SSID of the Access Point, maximum 32 characters */
+table Ssid {
+ ssid:[ubyte];
+}
+
+/**
+ * Preferred network information
+ * SSID and associated security mode(s)
+ */
+table PreferredNetwork {
+ ssid:Ssid;
+ /* SecurityMode flags that are associated with this SSID
+ * More than one security mode can be supported */
+ security_modes:ubyte;
+}
+
+/**
+ * Scan Results returned by offload nanoapp
+ */
+table ScanResult {
+ ssid:Ssid;
+ security_modes:ubyte;
+ bssid:[ubyte];
+ capability:ushort; // Multiple bits may be set
+ frequency_scanned_mhz:uint;
+ rssi_dbm:byte; // Signal strength
+ tsf:ulong; // TSF found in beacon/probe response
+}
+
+/**
+ * Message to send scan result(s) from nanoapp to host
+ */
+table ScanResultMessage {
+ scan_results:[ScanResult];
+}
+
+/**
+ * Parameters for performing offload scans
+ */
+table ScanParams {
+ ssids_to_scan:[Ssid];
+ frequencies_to_scan_mhz:[uint];
+ disconnected_mode_scan_interval_ms:uint; // 0 means disable
+}
+
+/**
+ * Instruction on how to filter scan results before waking up the applications
+ * processor.
+ */
+table ScanFilter {
+ networks_to_match:[PreferredNetwork]; // emtpy means match all
+ min_rssi_threshold_dbm:byte;
+}
+
+/**
+ * Structure to send scan params and filter from host to nanoapp
+ */
+table ScanConfig {
+ scan_params:ScanParams;
+ scan_filter:ScanFilter;
+}
+
+/**
+ * Defines the structure of each scan record
+ */
+table ScanRecord {
+ time_spent_scanning_ms:uint;
+ num_channels_scanned:uint;
+ /* Number of ScanRecords aggregated into this record. Multiple ScanRecords
+ * may be combined together into a single ScanRecord by adding up values to
+ * save space if needed */
+ num_entries_aggregated:uint;
+}
+
+/**
+ * Defines the structure of each log record
+ */
+table RpcLogRecord {
+ record_type:ubyte;
+ timestamp_chre_ms:uint; // See chreGetTime()
+}
+
+/**
+ * Structure to keep and send scan statistics from nanoapp to host
+ */
+table ScanStats {
+ num_scans_requested_by_nanoapp:uint;
+ num_scans_serviced_by_hardware:uint;
+ num_scans_serviced_by_cache:uint;
+ updated_at_chre_ms:uint;
+ sent_at_chre_ms:uint;
+ /* The duration between when the framework subscribed for scan results to min
+ * of time when the framework unsubscribed vs. current time */
+ last_subscription_duration_ms:uint;
+ channel_scan_count:[ubyte];
+ scan_records:[ScanRecord];
+ rpc_log_records:[RpcLogRecord];
+}
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types_generated.h b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types_generated.h
new file mode 100644
index 0000000..d75ba9b
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/flatbuffers_types_generated.h
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// automatically generated by the FlatBuffers compiler, do not modify
+
+#ifndef FLATBUFFERS_GENERATED_FLATBUFFERSTYPES_WIFI_OFFLOAD_FBS_H_
+#define FLATBUFFERS_GENERATED_FLATBUFFERSTYPES_WIFI_OFFLOAD_FBS_H_
+
+#include "flatbuffers/flatbuffers.h"
+
+namespace wifi_offload {
+namespace fbs {
+
+struct Ssid;
+
+struct PreferredNetwork;
+
+struct ScanResult;
+
+struct ScanResultMessage;
+
+struct ScanParams;
+
+struct ScanFilter;
+
+struct ScanConfig;
+
+struct ScanRecord;
+
+struct RpcLogRecord;
+
+struct ScanStats;
+
+struct Ssid FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_SSID = 4 };
+ const flatbuffers::Vector<uint8_t> *ssid() const {
+ return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_SSID);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SSID) &&
+ verifier.Verify(ssid()) && verifier.EndTable();
+ }
+};
+
+struct SsidBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_ssid(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> ssid) {
+ fbb_.AddOffset(Ssid::VT_SSID, ssid);
+ }
+ SsidBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ SsidBuilder &operator=(const SsidBuilder &);
+ flatbuffers::Offset<Ssid> Finish() {
+ const auto end = fbb_.EndTable(start_, 1);
+ auto o = flatbuffers::Offset<Ssid>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<Ssid> CreateSsid(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> ssid = 0) {
+ SsidBuilder builder_(_fbb);
+ builder_.add_ssid(ssid);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<Ssid> CreateSsidDirect(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ const std::vector<uint8_t> *ssid = nullptr) {
+ return wifi_offload::fbs::CreateSsid(
+ _fbb, ssid ? _fbb.CreateVector<uint8_t>(*ssid) : 0);
+}
+
+struct PreferredNetwork FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_SSID = 4, VT_SECURITY_MODES = 6 };
+ const Ssid *ssid() const { return GetPointer<const Ssid *>(VT_SSID); }
+ uint8_t security_modes() const {
+ return GetField<uint8_t>(VT_SECURITY_MODES, 0);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SSID) &&
+ verifier.VerifyTable(ssid()) &&
+ VerifyField<uint8_t>(verifier, VT_SECURITY_MODES) &&
+ verifier.EndTable();
+ }
+};
+
+struct PreferredNetworkBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_ssid(flatbuffers::Offset<Ssid> ssid) {
+ fbb_.AddOffset(PreferredNetwork::VT_SSID, ssid);
+ }
+ void add_security_modes(uint8_t security_modes) {
+ fbb_.AddElement<uint8_t>(PreferredNetwork::VT_SECURITY_MODES,
+ security_modes, 0);
+ }
+ PreferredNetworkBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ PreferredNetworkBuilder &operator=(const PreferredNetworkBuilder &);
+ flatbuffers::Offset<PreferredNetwork> Finish() {
+ const auto end = fbb_.EndTable(start_, 2);
+ auto o = flatbuffers::Offset<PreferredNetwork>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<PreferredNetwork> CreatePreferredNetwork(
+ flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset<Ssid> ssid = 0,
+ uint8_t security_modes = 0) {
+ PreferredNetworkBuilder builder_(_fbb);
+ builder_.add_ssid(ssid);
+ builder_.add_security_modes(security_modes);
+ return builder_.Finish();
+}
+
+struct ScanResult FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum {
+ VT_SSID = 4,
+ VT_SECURITY_MODES = 6,
+ VT_BSSID = 8,
+ VT_CAPABILITY = 10,
+ VT_FREQUENCY_SCANNED_MHZ = 12,
+ VT_RSSI_DBM = 14,
+ VT_TSF = 16
+ };
+ const Ssid *ssid() const { return GetPointer<const Ssid *>(VT_SSID); }
+ uint8_t security_modes() const {
+ return GetField<uint8_t>(VT_SECURITY_MODES, 0);
+ }
+ const flatbuffers::Vector<uint8_t> *bssid() const {
+ return GetPointer<const flatbuffers::Vector<uint8_t> *>(VT_BSSID);
+ }
+ uint16_t capability() const { return GetField<uint16_t>(VT_CAPABILITY, 0); }
+ uint32_t frequency_scanned_mhz() const {
+ return GetField<uint32_t>(VT_FREQUENCY_SCANNED_MHZ, 0);
+ }
+ int8_t rssi_dbm() const { return GetField<int8_t>(VT_RSSI_DBM, 0); }
+ uint64_t tsf() const { return GetField<uint64_t>(VT_TSF, 0); }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SSID) &&
+ verifier.VerifyTable(ssid()) &&
+ VerifyField<uint8_t>(verifier, VT_SECURITY_MODES) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_BSSID) &&
+ verifier.Verify(bssid()) &&
+ VerifyField<uint16_t>(verifier, VT_CAPABILITY) &&
+ VerifyField<uint32_t>(verifier, VT_FREQUENCY_SCANNED_MHZ) &&
+ VerifyField<int8_t>(verifier, VT_RSSI_DBM) &&
+ VerifyField<uint64_t>(verifier, VT_TSF) && verifier.EndTable();
+ }
+};
+
+struct ScanResultBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_ssid(flatbuffers::Offset<Ssid> ssid) {
+ fbb_.AddOffset(ScanResult::VT_SSID, ssid);
+ }
+ void add_security_modes(uint8_t security_modes) {
+ fbb_.AddElement<uint8_t>(ScanResult::VT_SECURITY_MODES, security_modes, 0);
+ }
+ void add_bssid(flatbuffers::Offset<flatbuffers::Vector<uint8_t>> bssid) {
+ fbb_.AddOffset(ScanResult::VT_BSSID, bssid);
+ }
+ void add_capability(uint16_t capability) {
+ fbb_.AddElement<uint16_t>(ScanResult::VT_CAPABILITY, capability, 0);
+ }
+ void add_frequency_scanned_mhz(uint32_t frequency_scanned_mhz) {
+ fbb_.AddElement<uint32_t>(ScanResult::VT_FREQUENCY_SCANNED_MHZ,
+ frequency_scanned_mhz, 0);
+ }
+ void add_rssi_dbm(int8_t rssi_dbm) {
+ fbb_.AddElement<int8_t>(ScanResult::VT_RSSI_DBM, rssi_dbm, 0);
+ }
+ void add_tsf(uint64_t tsf) {
+ fbb_.AddElement<uint64_t>(ScanResult::VT_TSF, tsf, 0);
+ }
+ ScanResultBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanResultBuilder &operator=(const ScanResultBuilder &);
+ flatbuffers::Offset<ScanResult> Finish() {
+ const auto end = fbb_.EndTable(start_, 7);
+ auto o = flatbuffers::Offset<ScanResult>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanResult> CreateScanResult(
+ flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset<Ssid> ssid = 0,
+ uint8_t security_modes = 0,
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> bssid = 0,
+ uint16_t capability = 0, uint32_t frequency_scanned_mhz = 0,
+ int8_t rssi_dbm = 0, uint64_t tsf = 0) {
+ ScanResultBuilder builder_(_fbb);
+ builder_.add_tsf(tsf);
+ builder_.add_frequency_scanned_mhz(frequency_scanned_mhz);
+ builder_.add_bssid(bssid);
+ builder_.add_ssid(ssid);
+ builder_.add_capability(capability);
+ builder_.add_rssi_dbm(rssi_dbm);
+ builder_.add_security_modes(security_modes);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ScanResult> CreateScanResultDirect(
+ flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset<Ssid> ssid = 0,
+ uint8_t security_modes = 0, const std::vector<uint8_t> *bssid = nullptr,
+ uint16_t capability = 0, uint32_t frequency_scanned_mhz = 0,
+ int8_t rssi_dbm = 0, uint64_t tsf = 0) {
+ return wifi_offload::fbs::CreateScanResult(
+ _fbb, ssid, security_modes,
+ bssid ? _fbb.CreateVector<uint8_t>(*bssid) : 0, capability,
+ frequency_scanned_mhz, rssi_dbm, tsf);
+}
+
+struct ScanResultMessage FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_SCAN_RESULTS = 4 };
+ const flatbuffers::Vector<flatbuffers::Offset<ScanResult>> *scan_results()
+ const {
+ return GetPointer<
+ const flatbuffers::Vector<flatbuffers::Offset<ScanResult>> *>(
+ VT_SCAN_RESULTS);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SCAN_RESULTS) &&
+ verifier.Verify(scan_results()) &&
+ verifier.VerifyVectorOfTables(scan_results()) && verifier.EndTable();
+ }
+};
+
+struct ScanResultMessageBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_scan_results(
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScanResult>>>
+ scan_results) {
+ fbb_.AddOffset(ScanResultMessage::VT_SCAN_RESULTS, scan_results);
+ }
+ ScanResultMessageBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanResultMessageBuilder &operator=(const ScanResultMessageBuilder &);
+ flatbuffers::Offset<ScanResultMessage> Finish() {
+ const auto end = fbb_.EndTable(start_, 1);
+ auto o = flatbuffers::Offset<ScanResultMessage>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanResultMessage> CreateScanResultMessage(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScanResult>>>
+ scan_results = 0) {
+ ScanResultMessageBuilder builder_(_fbb);
+ builder_.add_scan_results(scan_results);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ScanResultMessage> CreateScanResultMessageDirect(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ const std::vector<flatbuffers::Offset<ScanResult>> *scan_results =
+ nullptr) {
+ return wifi_offload::fbs::CreateScanResultMessage(
+ _fbb,
+ scan_results
+ ? _fbb.CreateVector<flatbuffers::Offset<ScanResult>>(*scan_results)
+ : 0);
+}
+
+struct ScanParams FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum {
+ VT_SSIDS_TO_SCAN = 4,
+ VT_FREQUENCIES_TO_SCAN_MHZ = 6,
+ VT_DISCONNECTED_MODE_SCAN_INTERVAL_MS = 8
+ };
+ const flatbuffers::Vector<flatbuffers::Offset<Ssid>> *ssids_to_scan() const {
+ return GetPointer<const flatbuffers::Vector<flatbuffers::Offset<Ssid>> *>(
+ VT_SSIDS_TO_SCAN);
+ }
+ const flatbuffers::Vector<uint32_t> *frequencies_to_scan_mhz() const {
+ return GetPointer<const flatbuffers::Vector<uint32_t> *>(
+ VT_FREQUENCIES_TO_SCAN_MHZ);
+ }
+ uint32_t disconnected_mode_scan_interval_ms() const {
+ return GetField<uint32_t>(VT_DISCONNECTED_MODE_SCAN_INTERVAL_MS, 0);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SSIDS_TO_SCAN) &&
+ verifier.Verify(ssids_to_scan()) &&
+ verifier.VerifyVectorOfTables(ssids_to_scan()) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier,
+ VT_FREQUENCIES_TO_SCAN_MHZ) &&
+ verifier.Verify(frequencies_to_scan_mhz()) &&
+ VerifyField<uint32_t>(verifier,
+ VT_DISCONNECTED_MODE_SCAN_INTERVAL_MS) &&
+ verifier.EndTable();
+ }
+};
+
+struct ScanParamsBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_ssids_to_scan(
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Ssid>>>
+ ssids_to_scan) {
+ fbb_.AddOffset(ScanParams::VT_SSIDS_TO_SCAN, ssids_to_scan);
+ }
+ void add_frequencies_to_scan_mhz(
+ flatbuffers::Offset<flatbuffers::Vector<uint32_t>>
+ frequencies_to_scan_mhz) {
+ fbb_.AddOffset(ScanParams::VT_FREQUENCIES_TO_SCAN_MHZ,
+ frequencies_to_scan_mhz);
+ }
+ void add_disconnected_mode_scan_interval_ms(
+ uint32_t disconnected_mode_scan_interval_ms) {
+ fbb_.AddElement<uint32_t>(ScanParams::VT_DISCONNECTED_MODE_SCAN_INTERVAL_MS,
+ disconnected_mode_scan_interval_ms, 0);
+ }
+ ScanParamsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanParamsBuilder &operator=(const ScanParamsBuilder &);
+ flatbuffers::Offset<ScanParams> Finish() {
+ const auto end = fbb_.EndTable(start_, 3);
+ auto o = flatbuffers::Offset<ScanParams>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanParams> CreateScanParams(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Ssid>>>
+ ssids_to_scan = 0,
+ flatbuffers::Offset<flatbuffers::Vector<uint32_t>> frequencies_to_scan_mhz =
+ 0,
+ uint32_t disconnected_mode_scan_interval_ms = 0) {
+ ScanParamsBuilder builder_(_fbb);
+ builder_.add_disconnected_mode_scan_interval_ms(
+ disconnected_mode_scan_interval_ms);
+ builder_.add_frequencies_to_scan_mhz(frequencies_to_scan_mhz);
+ builder_.add_ssids_to_scan(ssids_to_scan);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ScanParams> CreateScanParamsDirect(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ const std::vector<flatbuffers::Offset<Ssid>> *ssids_to_scan = nullptr,
+ const std::vector<uint32_t> *frequencies_to_scan_mhz = nullptr,
+ uint32_t disconnected_mode_scan_interval_ms = 0) {
+ return wifi_offload::fbs::CreateScanParams(
+ _fbb,
+ ssids_to_scan
+ ? _fbb.CreateVector<flatbuffers::Offset<Ssid>>(*ssids_to_scan)
+ : 0,
+ frequencies_to_scan_mhz
+ ? _fbb.CreateVector<uint32_t>(*frequencies_to_scan_mhz)
+ : 0,
+ disconnected_mode_scan_interval_ms);
+}
+
+struct ScanFilter FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_NETWORKS_TO_MATCH = 4, VT_MIN_RSSI_THRESHOLD_DBM = 6 };
+ const flatbuffers::Vector<flatbuffers::Offset<PreferredNetwork>>
+ *networks_to_match() const {
+ return GetPointer<
+ const flatbuffers::Vector<flatbuffers::Offset<PreferredNetwork>> *>(
+ VT_NETWORKS_TO_MATCH);
+ }
+ int8_t min_rssi_threshold_dbm() const {
+ return GetField<int8_t>(VT_MIN_RSSI_THRESHOLD_DBM, 0);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) && VerifyField<flatbuffers::uoffset_t>(
+ verifier, VT_NETWORKS_TO_MATCH) &&
+ verifier.Verify(networks_to_match()) &&
+ verifier.VerifyVectorOfTables(networks_to_match()) &&
+ VerifyField<int8_t>(verifier, VT_MIN_RSSI_THRESHOLD_DBM) &&
+ verifier.EndTable();
+ }
+};
+
+struct ScanFilterBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_networks_to_match(
+ flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<PreferredNetwork>>>
+ networks_to_match) {
+ fbb_.AddOffset(ScanFilter::VT_NETWORKS_TO_MATCH, networks_to_match);
+ }
+ void add_min_rssi_threshold_dbm(int8_t min_rssi_threshold_dbm) {
+ fbb_.AddElement<int8_t>(ScanFilter::VT_MIN_RSSI_THRESHOLD_DBM,
+ min_rssi_threshold_dbm, 0);
+ }
+ ScanFilterBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanFilterBuilder &operator=(const ScanFilterBuilder &);
+ flatbuffers::Offset<ScanFilter> Finish() {
+ const auto end = fbb_.EndTable(start_, 2);
+ auto o = flatbuffers::Offset<ScanFilter>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanFilter> CreateScanFilter(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<PreferredNetwork>>>
+ networks_to_match = 0,
+ int8_t min_rssi_threshold_dbm = 0) {
+ ScanFilterBuilder builder_(_fbb);
+ builder_.add_networks_to_match(networks_to_match);
+ builder_.add_min_rssi_threshold_dbm(min_rssi_threshold_dbm);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ScanFilter> CreateScanFilterDirect(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ const std::vector<flatbuffers::Offset<PreferredNetwork>>
+ *networks_to_match = nullptr,
+ int8_t min_rssi_threshold_dbm = 0) {
+ return wifi_offload::fbs::CreateScanFilter(
+ _fbb,
+ networks_to_match
+ ? _fbb.CreateVector<flatbuffers::Offset<PreferredNetwork>>(
+ *networks_to_match)
+ : 0,
+ min_rssi_threshold_dbm);
+}
+
+struct ScanConfig FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_SCAN_PARAMS = 4, VT_SCAN_FILTER = 6 };
+ const ScanParams *scan_params() const {
+ return GetPointer<const ScanParams *>(VT_SCAN_PARAMS);
+ }
+ const ScanFilter *scan_filter() const {
+ return GetPointer<const ScanFilter *>(VT_SCAN_FILTER);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SCAN_PARAMS) &&
+ verifier.VerifyTable(scan_params()) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SCAN_FILTER) &&
+ verifier.VerifyTable(scan_filter()) && verifier.EndTable();
+ }
+};
+
+struct ScanConfigBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_scan_params(flatbuffers::Offset<ScanParams> scan_params) {
+ fbb_.AddOffset(ScanConfig::VT_SCAN_PARAMS, scan_params);
+ }
+ void add_scan_filter(flatbuffers::Offset<ScanFilter> scan_filter) {
+ fbb_.AddOffset(ScanConfig::VT_SCAN_FILTER, scan_filter);
+ }
+ ScanConfigBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanConfigBuilder &operator=(const ScanConfigBuilder &);
+ flatbuffers::Offset<ScanConfig> Finish() {
+ const auto end = fbb_.EndTable(start_, 2);
+ auto o = flatbuffers::Offset<ScanConfig>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanConfig> CreateScanConfig(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ flatbuffers::Offset<ScanParams> scan_params = 0,
+ flatbuffers::Offset<ScanFilter> scan_filter = 0) {
+ ScanConfigBuilder builder_(_fbb);
+ builder_.add_scan_filter(scan_filter);
+ builder_.add_scan_params(scan_params);
+ return builder_.Finish();
+}
+
+struct ScanRecord FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum {
+ VT_TIME_SPENT_SCANNING_MS = 4,
+ VT_NUM_CHANNELS_SCANNED = 6,
+ VT_NUM_ENTRIES_AGGREGATED = 8
+ };
+ uint32_t time_spent_scanning_ms() const {
+ return GetField<uint32_t>(VT_TIME_SPENT_SCANNING_MS, 0);
+ }
+ uint32_t num_channels_scanned() const {
+ return GetField<uint32_t>(VT_NUM_CHANNELS_SCANNED, 0);
+ }
+ uint32_t num_entries_aggregated() const {
+ return GetField<uint32_t>(VT_NUM_ENTRIES_AGGREGATED, 0);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<uint32_t>(verifier, VT_TIME_SPENT_SCANNING_MS) &&
+ VerifyField<uint32_t>(verifier, VT_NUM_CHANNELS_SCANNED) &&
+ VerifyField<uint32_t>(verifier, VT_NUM_ENTRIES_AGGREGATED) &&
+ verifier.EndTable();
+ }
+};
+
+struct ScanRecordBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_time_spent_scanning_ms(uint32_t time_spent_scanning_ms) {
+ fbb_.AddElement<uint32_t>(ScanRecord::VT_TIME_SPENT_SCANNING_MS,
+ time_spent_scanning_ms, 0);
+ }
+ void add_num_channels_scanned(uint32_t num_channels_scanned) {
+ fbb_.AddElement<uint32_t>(ScanRecord::VT_NUM_CHANNELS_SCANNED,
+ num_channels_scanned, 0);
+ }
+ void add_num_entries_aggregated(uint32_t num_entries_aggregated) {
+ fbb_.AddElement<uint32_t>(ScanRecord::VT_NUM_ENTRIES_AGGREGATED,
+ num_entries_aggregated, 0);
+ }
+ ScanRecordBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanRecordBuilder &operator=(const ScanRecordBuilder &);
+ flatbuffers::Offset<ScanRecord> Finish() {
+ const auto end = fbb_.EndTable(start_, 3);
+ auto o = flatbuffers::Offset<ScanRecord>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanRecord> CreateScanRecord(
+ flatbuffers::FlatBufferBuilder &_fbb, uint32_t time_spent_scanning_ms = 0,
+ uint32_t num_channels_scanned = 0, uint32_t num_entries_aggregated = 0) {
+ ScanRecordBuilder builder_(_fbb);
+ builder_.add_num_entries_aggregated(num_entries_aggregated);
+ builder_.add_num_channels_scanned(num_channels_scanned);
+ builder_.add_time_spent_scanning_ms(time_spent_scanning_ms);
+ return builder_.Finish();
+}
+
+struct RpcLogRecord FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum { VT_RECORD_TYPE = 4, VT_TIMESTAMP_CHRE_MS = 6 };
+ uint8_t record_type() const { return GetField<uint8_t>(VT_RECORD_TYPE, 0); }
+ uint32_t timestamp_chre_ms() const {
+ return GetField<uint32_t>(VT_TIMESTAMP_CHRE_MS, 0);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<uint8_t>(verifier, VT_RECORD_TYPE) &&
+ VerifyField<uint32_t>(verifier, VT_TIMESTAMP_CHRE_MS) &&
+ verifier.EndTable();
+ }
+};
+
+struct RpcLogRecordBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_record_type(uint8_t record_type) {
+ fbb_.AddElement<uint8_t>(RpcLogRecord::VT_RECORD_TYPE, record_type, 0);
+ }
+ void add_timestamp_chre_ms(uint32_t timestamp_chre_ms) {
+ fbb_.AddElement<uint32_t>(RpcLogRecord::VT_TIMESTAMP_CHRE_MS,
+ timestamp_chre_ms, 0);
+ }
+ RpcLogRecordBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ RpcLogRecordBuilder &operator=(const RpcLogRecordBuilder &);
+ flatbuffers::Offset<RpcLogRecord> Finish() {
+ const auto end = fbb_.EndTable(start_, 2);
+ auto o = flatbuffers::Offset<RpcLogRecord>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<RpcLogRecord> CreateRpcLogRecord(
+ flatbuffers::FlatBufferBuilder &_fbb, uint8_t record_type = 0,
+ uint32_t timestamp_chre_ms = 0) {
+ RpcLogRecordBuilder builder_(_fbb);
+ builder_.add_timestamp_chre_ms(timestamp_chre_ms);
+ builder_.add_record_type(record_type);
+ return builder_.Finish();
+}
+
+struct ScanStats FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+ enum {
+ VT_NUM_SCANS_REQUESTED_BY_NANOAPP = 4,
+ VT_NUM_SCANS_SERVICED_BY_HARDWARE = 6,
+ VT_NUM_SCANS_SERVICED_BY_CACHE = 8,
+ VT_UPDATED_AT_CHRE_MS = 10,
+ VT_SENT_AT_CHRE_MS = 12,
+ VT_LAST_SUBSCRIPTION_DURATION_MS = 14,
+ VT_CHANNEL_SCAN_COUNT = 16,
+ VT_SCAN_RECORDS = 18,
+ VT_RPC_LOG_RECORDS = 20
+ };
+ uint32_t num_scans_requested_by_nanoapp() const {
+ return GetField<uint32_t>(VT_NUM_SCANS_REQUESTED_BY_NANOAPP, 0);
+ }
+ uint32_t num_scans_serviced_by_hardware() const {
+ return GetField<uint32_t>(VT_NUM_SCANS_SERVICED_BY_HARDWARE, 0);
+ }
+ uint32_t num_scans_serviced_by_cache() const {
+ return GetField<uint32_t>(VT_NUM_SCANS_SERVICED_BY_CACHE, 0);
+ }
+ uint32_t updated_at_chre_ms() const {
+ return GetField<uint32_t>(VT_UPDATED_AT_CHRE_MS, 0);
+ }
+ uint32_t sent_at_chre_ms() const {
+ return GetField<uint32_t>(VT_SENT_AT_CHRE_MS, 0);
+ }
+ uint32_t last_subscription_duration_ms() const {
+ return GetField<uint32_t>(VT_LAST_SUBSCRIPTION_DURATION_MS, 0);
+ }
+ const flatbuffers::Vector<uint8_t> *channel_scan_count() const {
+ return GetPointer<const flatbuffers::Vector<uint8_t> *>(
+ VT_CHANNEL_SCAN_COUNT);
+ }
+ const flatbuffers::Vector<flatbuffers::Offset<ScanRecord>> *scan_records()
+ const {
+ return GetPointer<
+ const flatbuffers::Vector<flatbuffers::Offset<ScanRecord>> *>(
+ VT_SCAN_RECORDS);
+ }
+ const flatbuffers::Vector<flatbuffers::Offset<RpcLogRecord>>
+ *rpc_log_records() const {
+ return GetPointer<
+ const flatbuffers::Vector<flatbuffers::Offset<RpcLogRecord>> *>(
+ VT_RPC_LOG_RECORDS);
+ }
+ bool Verify(flatbuffers::Verifier &verifier) const {
+ return VerifyTableStart(verifier) &&
+ VerifyField<uint32_t>(verifier, VT_NUM_SCANS_REQUESTED_BY_NANOAPP) &&
+ VerifyField<uint32_t>(verifier, VT_NUM_SCANS_SERVICED_BY_HARDWARE) &&
+ VerifyField<uint32_t>(verifier, VT_NUM_SCANS_SERVICED_BY_CACHE) &&
+ VerifyField<uint32_t>(verifier, VT_UPDATED_AT_CHRE_MS) &&
+ VerifyField<uint32_t>(verifier, VT_SENT_AT_CHRE_MS) &&
+ VerifyField<uint32_t>(verifier, VT_LAST_SUBSCRIPTION_DURATION_MS) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier,
+ VT_CHANNEL_SCAN_COUNT) &&
+ verifier.Verify(channel_scan_count()) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_SCAN_RECORDS) &&
+ verifier.Verify(scan_records()) &&
+ verifier.VerifyVectorOfTables(scan_records()) &&
+ VerifyField<flatbuffers::uoffset_t>(verifier, VT_RPC_LOG_RECORDS) &&
+ verifier.Verify(rpc_log_records()) &&
+ verifier.VerifyVectorOfTables(rpc_log_records()) &&
+ verifier.EndTable();
+ }
+};
+
+struct ScanStatsBuilder {
+ flatbuffers::FlatBufferBuilder &fbb_;
+ flatbuffers::uoffset_t start_;
+ void add_num_scans_requested_by_nanoapp(
+ uint32_t num_scans_requested_by_nanoapp) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_NUM_SCANS_REQUESTED_BY_NANOAPP,
+ num_scans_requested_by_nanoapp, 0);
+ }
+ void add_num_scans_serviced_by_hardware(
+ uint32_t num_scans_serviced_by_hardware) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_NUM_SCANS_SERVICED_BY_HARDWARE,
+ num_scans_serviced_by_hardware, 0);
+ }
+ void add_num_scans_serviced_by_cache(uint32_t num_scans_serviced_by_cache) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_NUM_SCANS_SERVICED_BY_CACHE,
+ num_scans_serviced_by_cache, 0);
+ }
+ void add_updated_at_chre_ms(uint32_t updated_at_chre_ms) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_UPDATED_AT_CHRE_MS,
+ updated_at_chre_ms, 0);
+ }
+ void add_sent_at_chre_ms(uint32_t sent_at_chre_ms) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_SENT_AT_CHRE_MS, sent_at_chre_ms,
+ 0);
+ }
+ void add_last_subscription_duration_ms(
+ uint32_t last_subscription_duration_ms) {
+ fbb_.AddElement<uint32_t>(ScanStats::VT_LAST_SUBSCRIPTION_DURATION_MS,
+ last_subscription_duration_ms, 0);
+ }
+ void add_channel_scan_count(
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> channel_scan_count) {
+ fbb_.AddOffset(ScanStats::VT_CHANNEL_SCAN_COUNT, channel_scan_count);
+ }
+ void add_scan_records(
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScanRecord>>>
+ scan_records) {
+ fbb_.AddOffset(ScanStats::VT_SCAN_RECORDS, scan_records);
+ }
+ void add_rpc_log_records(
+ flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<RpcLogRecord>>>
+ rpc_log_records) {
+ fbb_.AddOffset(ScanStats::VT_RPC_LOG_RECORDS, rpc_log_records);
+ }
+ ScanStatsBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
+ start_ = fbb_.StartTable();
+ }
+ ScanStatsBuilder &operator=(const ScanStatsBuilder &);
+ flatbuffers::Offset<ScanStats> Finish() {
+ const auto end = fbb_.EndTable(start_, 9);
+ auto o = flatbuffers::Offset<ScanStats>(end);
+ return o;
+ }
+};
+
+inline flatbuffers::Offset<ScanStats> CreateScanStats(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ uint32_t num_scans_requested_by_nanoapp = 0,
+ uint32_t num_scans_serviced_by_hardware = 0,
+ uint32_t num_scans_serviced_by_cache = 0, uint32_t updated_at_chre_ms = 0,
+ uint32_t sent_at_chre_ms = 0, uint32_t last_subscription_duration_ms = 0,
+ flatbuffers::Offset<flatbuffers::Vector<uint8_t>> channel_scan_count = 0,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<ScanRecord>>>
+ scan_records = 0,
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<RpcLogRecord>>>
+ rpc_log_records = 0) {
+ ScanStatsBuilder builder_(_fbb);
+ builder_.add_rpc_log_records(rpc_log_records);
+ builder_.add_scan_records(scan_records);
+ builder_.add_channel_scan_count(channel_scan_count);
+ builder_.add_last_subscription_duration_ms(last_subscription_duration_ms);
+ builder_.add_sent_at_chre_ms(sent_at_chre_ms);
+ builder_.add_updated_at_chre_ms(updated_at_chre_ms);
+ builder_.add_num_scans_serviced_by_cache(num_scans_serviced_by_cache);
+ builder_.add_num_scans_serviced_by_hardware(num_scans_serviced_by_hardware);
+ builder_.add_num_scans_requested_by_nanoapp(num_scans_requested_by_nanoapp);
+ return builder_.Finish();
+}
+
+inline flatbuffers::Offset<ScanStats> CreateScanStatsDirect(
+ flatbuffers::FlatBufferBuilder &_fbb,
+ uint32_t num_scans_requested_by_nanoapp = 0,
+ uint32_t num_scans_serviced_by_hardware = 0,
+ uint32_t num_scans_serviced_by_cache = 0, uint32_t updated_at_chre_ms = 0,
+ uint32_t sent_at_chre_ms = 0, uint32_t last_subscription_duration_ms = 0,
+ const std::vector<uint8_t> *channel_scan_count = nullptr,
+ const std::vector<flatbuffers::Offset<ScanRecord>> *scan_records = nullptr,
+ const std::vector<flatbuffers::Offset<RpcLogRecord>> *rpc_log_records =
+ nullptr) {
+ return wifi_offload::fbs::CreateScanStats(
+ _fbb, num_scans_requested_by_nanoapp, num_scans_serviced_by_hardware,
+ num_scans_serviced_by_cache, updated_at_chre_ms, sent_at_chre_ms,
+ last_subscription_duration_ms,
+ channel_scan_count ? _fbb.CreateVector<uint8_t>(*channel_scan_count) : 0,
+ scan_records
+ ? _fbb.CreateVector<flatbuffers::Offset<ScanRecord>>(*scan_records)
+ : 0,
+ rpc_log_records
+ ? _fbb.CreateVector<flatbuffers::Offset<RpcLogRecord>>(
+ *rpc_log_records)
+ : 0);
+}
+
+} // namespace fbs
+} // namespace wifi_offload
+
+#endif // FLATBUFFERS_GENERATED_FLATBUFFERSTYPES_WIFI_OFFLOAD_FBS_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/generate_code.sh b/apps/wifi_offload/include/chre/apps/wifi_offload/generate_code.sh
new file mode 100755
index 0000000..09ab1a7
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/generate_code.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -e
+
+# Path to flatc compiler
+FLATC_PATH="$ANDROID_BUILD_TOP/external/flatbuffers/flatc"
+
+# Flat buffer schema files
+FLATC_SRCS="flatbuffers_types.fbs"
+
+# Flatc arguments
+FLATC_ARGS="--cpp --no-includes"
+
+# Generate c++ code
+$FLATC_PATH $FLATC_ARGS $FLATC_SRCS
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/host_message_types.h b/apps/wifi_offload/include/chre/apps/wifi_offload/host_message_types.h
new file mode 100644
index 0000000..67ac25a
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/host_message_types.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_HOST_MESSAGE_TYPES_H_
+#define CHRE_WIFI_OFFLOAD_HOST_MESSAGE_TYPES_H_
+
+namespace wifi_offload {
+
+enum HostMessageType : uint32_t {
+ HOST_CMD_BASE = 0x00001000,
+
+ HOST_CMD_CONFIG_SCANS,
+ HOST_CMD_SUBSCRIBE_SCAN_RESULTS,
+ HOST_CMD_UNSUBSCRIBE_SCAN_RESULTS,
+ HOST_CMD_GET_SCAN_STATS,
+ HOST_CMD_RESET,
+
+ HOST_MSG_BASE = 0x0005000,
+
+ HOST_MSG_SCAN_RESULTS,
+ HOST_MSG_SCAN_STATS,
+ HOST_MSG_ERROR,
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_HOST_MESSAGE_TYPES_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/preferred_network.h b/apps/wifi_offload/include/chre/apps/wifi_offload/preferred_network.h
new file mode 100644
index 0000000..86ab436
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/preferred_network.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_PREFERRED_NETWORK_H_
+#define CHRE_WIFI_OFFLOAD_PREFERRED_NETWORK_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/ssid.h"
+
+namespace wifi_offload {
+
+enum SecurityMode : uint8_t {
+ UNKNOWN = 0,
+ OPEN = 0x1 << 0,
+ WEP = 0x1 << 1,
+ PSK = 0x1 << 2,
+ EAP = 0x1 << 3,
+ ALL_SECURITY_MODES_MASK = OPEN | WEP | PSK | EAP,
+};
+
+class PreferredNetwork {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::PreferredNetwork;
+
+ PreferredNetwork();
+
+ PreferredNetwork(PreferredNetwork &&other) = default;
+
+ ~PreferredNetwork() = default;
+
+ bool operator==(const PreferredNetwork &other) const;
+
+ flatbuffers::Offset<FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const FbsType &fbs_network);
+
+ void Log() const;
+
+ Ssid ssid_;
+ /* SecurityMode flags that are associated with this SSID
+ * More than one security mode can be supported, see SecurityMode */
+ uint8_t security_modes_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_PREFERRED_NETWORK_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/rpc_log_record.h b/apps/wifi_offload/include/chre/apps/wifi_offload/rpc_log_record.h
new file mode 100644
index 0000000..fb7b949
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/rpc_log_record.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_RPC_LOG_RECORD_H_
+#define CHRE_WIFI_OFFLOAD_RPC_LOG_RECORD_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+
+namespace wifi_offload {
+
+class RpcLogRecord {
+ public:
+ /**
+ * Enumerates the type of log that is recorded
+ */
+ enum class RpcLogRecordType : uint8_t {
+ CMD_BASE = 0x00,
+ CMD_INIT,
+ CMD_CONFIG_SCANS,
+ CMD_SUBSCRIBE_SCAN_RESULTS,
+ CMD_UNSUBSCRIBE_SCAN_RESULTS,
+ CMD_GET_SCAN_STATS,
+ CMD_RESET,
+ CMD_LAST_ITEM,
+
+ EVENT_RECVD_BASE = 0x40,
+ EVENT_RECVD_SCAN_RESULT_ASYNC,
+ EVENT_RECVD_SCAN_RESULT,
+ EVENT_RECVD_LAST_ITEM,
+
+ EVENT_SENT_BASE = 0x80,
+ EVENT_SENT_SCAN_RESULT,
+ EVENT_SENT_ABORT,
+ EVENT_SENT_ERROR,
+ EVENT_SENT_LAST_ITEM,
+
+ REQ_BASE = 0xc0,
+ REQ_SCAN,
+ REQ_LAST_ITEM,
+ };
+
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::RpcLogRecord;
+
+ RpcLogRecord();
+ ~RpcLogRecord() = default;
+
+ bool operator==(const RpcLogRecord &other) const;
+
+ flatbuffers::Offset<RpcLogRecord::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const RpcLogRecord::FbsType &fbs_record);
+
+ RpcLogRecordType record_type_;
+ uint32_t timestamp_chre_ms_; // See chreGetTime()
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_RPC_LOG_RECORD_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_config.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_config.h
new file mode 100644
index 0000000..26c2084
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_config.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_CONFIG_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_CONFIG_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/scan_filter.h"
+#include "chre/apps/wifi_offload/scan_params.h"
+
+namespace wifi_offload {
+
+class ScanConfig {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanConfig;
+
+ ScanConfig() = default;
+ ~ScanConfig() = default;
+
+ bool operator==(const ScanConfig &other) const;
+
+ flatbuffers::Offset<ScanConfig::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanConfig::FbsType &fbs_config);
+
+ void Log() const;
+
+ ScanParams scan_params_;
+ ScanFilter scan_filter_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_CONFIG_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_filter.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_filter.h
new file mode 100644
index 0000000..c49051b
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_filter.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_FILTER_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_FILTER_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/preferred_network.h"
+
+namespace wifi_offload {
+
+/**
+ * Instruction on how to filter scan results before waking up the applications
+ * processor.
+ */
+class ScanFilter {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanFilter;
+
+ ScanFilter();
+
+ ~ScanFilter() = default;
+
+ bool operator==(const ScanFilter &other) const;
+
+ flatbuffers::Offset<ScanFilter::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanFilter::FbsType &fbs_filter);
+
+ void Log() const;
+
+ Vector<PreferredNetwork> networks_to_match_; // empty means match all
+ int8_t min_rssi_threshold_dbm_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_FILTER_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_params.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_params.h
new file mode 100644
index 0000000..e8542e3
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_params.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_PARAMS_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_PARAMS_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/ssid.h"
+
+namespace wifi_offload {
+
+/**
+ * Parameters for performing offload scans
+ */
+class ScanParams {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanParams;
+
+ ScanParams();
+
+ ~ScanParams() = default;
+
+ bool operator==(const ScanParams &other) const;
+
+ flatbuffers::Offset<ScanParams::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanParams::FbsType &fbs_params);
+
+ void Log() const;
+
+ Vector<Ssid> ssids_to_scan_;
+ Vector<uint32_t> frequencies_to_scan_mhz_;
+ uint32_t disconnected_mode_scan_interval_ms_; // 0 means disable
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_PARAMS_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_record.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_record.h
new file mode 100644
index 0000000..9e9a055
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_record.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_RECORD_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_RECORD_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+
+namespace wifi_offload {
+
+class ScanRecord {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanRecord;
+
+ ScanRecord();
+ ~ScanRecord() = default;
+
+ bool operator==(const ScanRecord &other) const;
+
+ flatbuffers::Offset<ScanRecord::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanRecord::FbsType &fbs_record);
+
+ uint32_t time_spent_scanning_ms_;
+ uint32_t num_channels_scanned_;
+ /* Number of ScanRecords aggregated into this record. Multiple ScanRecords
+ * may be combined together into a single ScanRecord by adding up values to
+ * save space if needed */
+ uint32_t num_entries_aggregated_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_RECORD_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result.h
new file mode 100644
index 0000000..c1ffbc1
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_RESULT_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_RESULT_H_
+
+// First to pickup the LOG_TAG
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/preferred_network.h"
+#include "chre/apps/wifi_offload/ssid.h"
+
+namespace wifi_offload {
+
+/**
+ * Scan Results returned by offload nanoapp to the offload HAL
+ */
+class ScanResult {
+ public:
+ /**
+ * This is a bit mask describing the capabilities of a BSS.
+ * See IEEE Std 802.11: 8.4.1.4
+ */
+ enum Capability : uint16_t {
+ UNKNOWN = 0,
+ ESS = 1 << 0,
+ IBSS = 1 << 1,
+ CF_POLLABLE = 1 << 2,
+ CF_POLL_REQ = 1 << 3,
+ PRIVACY = 1 << 4,
+ SHORT_PREAMBLE = 1 << 5,
+ PBCC = 1 << 6,
+ CHANNEL_AGILITY = 1 << 7,
+ SPECTURM_MGMT = 1 << 8,
+ QOS = 1 << 9,
+ SHORT_SLOT_TIME = 1 << 10,
+ APSD = 1 << 11,
+ RADIO_MEASUREMENT = 1 << 12,
+ DSSS_OFDM = 1 << 13,
+ DELAYED_BLOCK_ACK = 1 << 14,
+ IMMEDIATE_BLOCK_ACK = 1 << 15,
+ ALL_CAPABILITIES_MASK = 0xffff,
+ };
+
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanResult;
+
+ static constexpr int kBssidSize = CHRE_WIFI_BSSID_LEN;
+
+ ScanResult();
+
+ ScanResult(const ScanResult &other);
+
+ ScanResult(ScanResult &&other) = default;
+
+ explicit ScanResult(const chreWifiScanResult &chre_scan_result);
+
+ ~ScanResult() = default;
+
+ bool operator==(const ScanResult &other) const;
+
+ flatbuffers::Offset<ScanResult::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanResult::FbsType &fbs_result);
+
+ void Log() const;
+
+ Ssid ssid_;
+ uint8_t security_modes_; // SecurityMode flags, see SecurityMode
+ uint8_t bssid_[kBssidSize];
+ uint16_t capability_; // Can have multiple bits set, see Capability
+ uint32_t frequency_scanned_mhz_;
+ int8_t rssi_dbm_; // Signal strength
+ uint64_t tsf_; // TSF found in beacon/probe response
+
+ private:
+ void UpdateFromChreWifiScanResult(const chreWifiScanResult &chre_scan_result);
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_RESULT_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result_message.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result_message.h
new file mode 100644
index 0000000..33c64fc
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_result_message.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_RESULT_MESSAGE_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_RESULT_MESSAGE_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/scan_result.h"
+
+namespace wifi_offload {
+
+/**
+ * Container for vector of scan results that provides serialization methods
+ */
+class ScanResultMessage {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanResultMessage;
+
+ ScanResultMessage() = default;
+ ~ScanResultMessage() = default;
+
+ void SetScanResults(const Vector<ScanResult> &results);
+ void GetScanResults(Vector<ScanResult> *results);
+
+ flatbuffers::Offset<ScanResultMessage::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanResultMessage::FbsType &fbs_result_message);
+
+ private:
+ Vector<ScanResult> scan_results_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_RESULT_MESSAGE_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/scan_stats.h b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_stats.h
new file mode 100644
index 0000000..9b61823
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/scan_stats.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SCAN_STATS_H_
+#define CHRE_WIFI_OFFLOAD_SCAN_STATS_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+#include "chre/apps/wifi_offload/rpc_log_record.h"
+#include "chre/apps/wifi_offload/scan_record.h"
+
+namespace wifi_offload {
+
+/**
+ * Defines the scan statistics to be returned to the framework
+ */
+class ScanStats {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::ScanStats;
+
+ ScanStats();
+ ~ScanStats() = default;
+
+ bool operator==(const ScanStats &other) const;
+
+ flatbuffers::Offset<ScanStats::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const ScanStats::FbsType &fbs_stats);
+
+ uint32_t num_scans_requested_by_nanoapp_;
+ uint32_t num_scans_serviced_by_hardware_;
+ uint32_t num_scans_serviced_by_cache_;
+ uint32_t updated_at_chre_ms_;
+ uint32_t sent_at_chre_ms_;
+ /* The duration between when the framework subscribed for scan results to min
+ * of time when the framework unsubscribed vs. current time */
+ uint32_t last_subscription_duration_ms_;
+
+ ChannelHistogram channel_histogram_;
+
+ Vector<ScanRecord> scan_records_;
+ Vector<RpcLogRecord> rpc_log_records_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SCAN_STATS_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/ssid.h b/apps/wifi_offload/include/chre/apps/wifi_offload/ssid.h
new file mode 100644
index 0000000..cbfd461
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/ssid.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_SSID_H_
+#define CHRE_WIFI_OFFLOAD_SSID_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+
+namespace wifi_offload {
+
+/* SSID of the Access Point, maximum 32 characters */
+class Ssid {
+ public:
+ /* Corresponding flatbuffers-generated data-type used to serialize and
+ * deserialize instances of this class */
+ using FbsType = fbs::Ssid;
+
+ static constexpr size_t kMaxSsidLen = CHRE_WIFI_SSID_MAX_LEN; // = 32
+
+ Ssid() = default;
+
+ Ssid(const Ssid &other);
+
+ Ssid(Ssid &&other) = default;
+
+ ~Ssid() = default;
+
+ void SetData(const uint8_t *buff, size_t len);
+
+ bool operator==(const Ssid &other) const;
+
+ flatbuffers::Offset<Ssid::FbsType> Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const;
+
+ bool Deserialize(const Ssid::FbsType &fbs_ssid);
+
+ void Log() const;
+
+ void ToChreWifiSsidListItem(chreWifiSsidListItem *chre_ssid) const;
+
+ private:
+ Vector<uint8_t> ssid_vec_;
+};
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_SSID_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/utility.h b/apps/wifi_offload/include/chre/apps/wifi_offload/utility.h
new file mode 100644
index 0000000..f48b075
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/utility.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_UTILITY_H_
+#define CHRE_WIFI_OFFLOAD_UTILITY_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/error_codes.h"
+#include "chre/apps/wifi_offload/scan_config.h"
+#include "chre/apps/wifi_offload/scan_filter.h"
+#include "chre/apps/wifi_offload/scan_params.h"
+#include "chre/apps/wifi_offload/scan_result.h"
+#include "chre/apps/wifi_offload/ssid.h"
+
+namespace wifi_offload {
+namespace utility {
+
+/**
+ * Maps channel frequency to channel number based in IEEE 802.11.
+ *
+ * @param freq Frequency in MHz we want to map to a channel number
+ *
+ * @return Channel number for a given frequency, 0 for unknown frequencies
+ */
+int Ieee80211FrequencyToChannel(int freq);
+
+void LogSsid(const uint8_t *ssid, uint8_t ssid_len);
+
+void LogBssid(const uint8_t *bssid);
+
+void LogChreScanResult(const chreWifiScanResult &result);
+
+const char *GetErrorCodeName(ErrorCode error_code);
+
+} // namespace utility
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_UTILITY_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/vector_serialization.h b/apps/wifi_offload/include/chre/apps/wifi_offload/vector_serialization.h
new file mode 100644
index 0000000..2f576b1
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/vector_serialization.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_WIFI_VECTOR_SERIALIZATION_H_
+#define CHRE_WIFI_OFFLOAD_WIFI_VECTOR_SERIALIZATION_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_types_generated.h"
+
+namespace wifi_offload {
+
+template <typename T>
+flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<typename T::FbsType>>>
+SerializeVector(const Vector<T> &native_vector,
+ flatbuffers::FlatBufferBuilder *builder) {
+ wifi_offload::Vector<flatbuffers::Offset<typename T::FbsType>> offset_vector;
+ offset_vector.reserve(native_vector.size());
+ for (const auto &elem : native_vector) {
+ offset_vector.push_back(elem.Serialize(builder));
+ }
+ return builder->CreateVector(offset_vector);
+}
+
+template <typename T>
+bool DeserializeVector(
+ const flatbuffers::Vector<flatbuffers::Offset<typename T::FbsType>>
+ &flatbuffer_vector,
+ Vector<T> *native_vector) {
+ if (native_vector == nullptr) {
+ return false;
+ }
+
+ native_vector->clear();
+ native_vector->reserve(flatbuffer_vector.size());
+ for (const auto &flatbuffer_elem : flatbuffer_vector) {
+ T native_elem;
+ if (flatbuffer_elem && native_elem.Deserialize(*flatbuffer_elem)) {
+ native_vector->push_back(std::move(native_elem));
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace wifi_offload
+
+#endif // CHRE_WIFI_OFFLOAD_WIFI_VECTOR_SERIALIZATION_H_
diff --git a/apps/wifi_offload/include/chre/apps/wifi_offload/wifi_offload.h b/apps/wifi_offload/include/chre/apps/wifi_offload/wifi_offload.h
new file mode 100644
index 0000000..fef0c8d
--- /dev/null
+++ b/apps/wifi_offload/include/chre/apps/wifi_offload/wifi_offload.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_WIFI_OFFLOAD_H_
+#define CHRE_WIFI_OFFLOAD_WIFI_OFFLOAD_H_
+
+#include <cinttypes>
+#include <chre/wifi.h>
+
+#ifdef BUILD_FOR_CHRE_WIFI_OFFLOAD
+#include <chre/event.h>
+#include <chre/re.h>
+#include <chre/version.h>
+
+#define LOG_TAG "[WifiOffload]"
+
+#include "chre/util/dynamic_vector.h"
+#include "chre/util/nanoapp/log.h"
+#include "chre/util/unique_ptr.h"
+
+#undef LOGE
+#undef LOGW
+#undef LOGI
+#undef LOGD
+// Define custom logging macros to support prefixing a LOG_TAG
+#define LOGE(fmt, ...) chreLog(CHRE_LOG_ERROR, LOG_TAG " " fmt, ##__VA_ARGS__)
+#define LOGW(fmt, ...) chreLog(CHRE_LOG_WARN, LOG_TAG " " fmt, ##__VA_ARGS__)
+#define LOGI(fmt, ...) chreLog(CHRE_LOG_INFO, LOG_TAG " " fmt, ##__VA_ARGS__)
+#define LOGD(fmt, ...) chreLog(CHRE_LOG_DEBUG, LOG_TAG " " fmt, ##__VA_ARGS__)
+
+namespace wifi_offload {
+template <typename T>
+using Vector = chre::DynamicVector<T>;
+} // namespace wifi_offload
+
+#else // BUILD_FOR_CHRE_WIFI_OFFLOAD
+#include <android/log.h>
+#include <vector>
+
+#ifndef LOG_TAG
+#define LOG_TAG "[Offload HAL]"
+#endif
+
+// Define these to logging functions that are available for offload HAL
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+namespace wifi_offload {
+template <typename T>
+using Vector = std::vector<T>;
+} // namespace wifi_offload
+
+#endif // BUILD_FOR_CHRE_WIFI_OFFLOAD
+
+#endif // CHRE_WIFI_OFFLOAD_WIFI_OFFLOAD_H_
diff --git a/apps/wifi_offload/preferred_network.cc b/apps/wifi_offload/preferred_network.cc
new file mode 100644
index 0000000..43a7a63
--- /dev/null
+++ b/apps/wifi_offload/preferred_network.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/preferred_network.h"
+
+namespace wifi_offload {
+
+PreferredNetwork::PreferredNetwork() : security_modes_(SecurityMode::UNKNOWN) {}
+
+bool PreferredNetwork::operator==(const PreferredNetwork &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return ssid_ == other.ssid_ && security_modes_ == other.security_modes_;
+}
+
+flatbuffers::Offset<PreferredNetwork::FbsType> PreferredNetwork::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ return fbs::CreatePreferredNetwork(*builder, ssid_.Serialize(builder),
+ security_modes_);
+}
+
+bool PreferredNetwork::Deserialize(
+ const PreferredNetwork::FbsType &fbs_network) {
+ const auto &fbs_ssid = fbs_network.ssid();
+ if (fbs_ssid == nullptr || !ssid_.Deserialize(*fbs_ssid)) {
+ LOGE("Failed to deserialize PreferredNetwork. Null or incomplete members.");
+ return false;
+ }
+
+ security_modes_ = fbs_network.security_modes();
+ if (security_modes_ & ~SecurityMode::ALL_SECURITY_MODES_MASK) {
+ LOGE("Failed to deserialize PreferredNetwork. Invalid security mode.");
+ return false;
+ }
+
+ return true;
+}
+
+void PreferredNetwork::Log() const {
+ ssid_.Log();
+ LOGI(" security modes: 0x%" PRIx8, security_modes_);
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/rpc_log_record.cc b/apps/wifi_offload/rpc_log_record.cc
new file mode 100644
index 0000000..79075ac
--- /dev/null
+++ b/apps/wifi_offload/rpc_log_record.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/rpc_log_record.h"
+
+namespace wifi_offload {
+
+RpcLogRecord::RpcLogRecord()
+ : record_type_(RpcLogRecordType::CMD_BASE), timestamp_chre_ms_(0) {}
+
+bool RpcLogRecord::operator==(const RpcLogRecord &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return record_type_ == other.record_type_ &&
+ timestamp_chre_ms_ == other.timestamp_chre_ms_;
+}
+
+flatbuffers::Offset<RpcLogRecord::FbsType> RpcLogRecord::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ return fbs::CreateRpcLogRecord(*builder, static_cast<uint8_t>(record_type_),
+ timestamp_chre_ms_);
+}
+
+bool RpcLogRecord::Deserialize(const RpcLogRecord::FbsType &fbs_record) {
+ uint8_t rec = fbs_record.record_type();
+ if ((rec > static_cast<uint8_t>(RpcLogRecordType::CMD_BASE) &&
+ rec < static_cast<uint8_t>(RpcLogRecordType::CMD_LAST_ITEM)) ||
+ (rec > static_cast<uint8_t>(RpcLogRecordType::EVENT_RECVD_BASE) &&
+ rec < static_cast<uint8_t>(RpcLogRecordType::EVENT_RECVD_LAST_ITEM)) ||
+ (rec > static_cast<uint8_t>(RpcLogRecordType::EVENT_SENT_BASE) &&
+ rec < static_cast<uint8_t>(RpcLogRecordType::EVENT_SENT_LAST_ITEM)) ||
+ (rec > static_cast<uint8_t>(RpcLogRecordType::REQ_BASE) &&
+ rec < static_cast<uint8_t>(RpcLogRecordType::REQ_LAST_ITEM))) {
+ record_type_ = static_cast<RpcLogRecordType>(fbs_record.record_type());
+ } else {
+ LOGE("Failed to deserialize RpcLogRecord. Invalid record type %" PRIu8,
+ rec);
+ return false;
+ }
+
+ timestamp_chre_ms_ = fbs_record.timestamp_chre_ms();
+ return true;
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_config.cc b/apps/wifi_offload/scan_config.cc
new file mode 100644
index 0000000..c5621d7
--- /dev/null
+++ b/apps/wifi_offload/scan_config.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_config.h"
+
+namespace wifi_offload {
+
+bool ScanConfig::operator==(const ScanConfig &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return scan_params_ == other.scan_params_ &&
+ scan_filter_ == other.scan_filter_;
+}
+
+flatbuffers::Offset<ScanConfig::FbsType> ScanConfig::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto params_offset = scan_params_.Serialize(builder);
+ auto filter_offset = scan_filter_.Serialize(builder);
+ return fbs::CreateScanConfig(*builder, params_offset, filter_offset);
+}
+
+bool ScanConfig::Deserialize(const ScanConfig::FbsType &fbs_config) {
+ if (fbs_config.scan_params() == nullptr ||
+ fbs_config.scan_filter() == nullptr) {
+ LOGE("Failed to deserialize ScanConfig. Null or incomplete members.");
+ return false;
+ }
+
+ return scan_params_.Deserialize(*fbs_config.scan_params()) &&
+ scan_filter_.Deserialize(*fbs_config.scan_filter());
+}
+
+void ScanConfig::Log() const {
+ scan_params_.Log();
+ scan_filter_.Log();
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_filter.cc b/apps/wifi_offload/scan_filter.cc
new file mode 100644
index 0000000..cca04be
--- /dev/null
+++ b/apps/wifi_offload/scan_filter.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_filter.h"
+#include "chre/apps/wifi_offload/vector_serialization.h"
+
+namespace wifi_offload {
+
+ScanFilter::ScanFilter() : min_rssi_threshold_dbm_(0) {}
+
+bool ScanFilter::operator==(const ScanFilter &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return networks_to_match_ == other.networks_to_match_ &&
+ min_rssi_threshold_dbm_ == other.min_rssi_threshold_dbm_;
+}
+
+flatbuffers::Offset<ScanFilter::FbsType> ScanFilter::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto netList = SerializeVector(networks_to_match_, builder);
+ return fbs::CreateScanFilter(*builder, netList, min_rssi_threshold_dbm_);
+}
+
+bool ScanFilter::Deserialize(const ScanFilter::FbsType &fbs_filter) {
+ const auto &fbsNetList = fbs_filter.networks_to_match();
+ if (fbsNetList == nullptr) {
+ LOGE("Failed to deserialize ScanFilter. Null or incomplete members.");
+ return false;
+ }
+
+ if (!DeserializeVector<PreferredNetwork>(*fbsNetList, &networks_to_match_)) {
+ LOGE("Failed to deserialize ScanFilter. Null or incomplete members.");
+ return false;
+ }
+
+ min_rssi_threshold_dbm_ = fbs_filter.min_rssi_threshold_dbm();
+ return true;
+}
+
+void ScanFilter::Log() const {
+ LOGI("ScanFilter:");
+ LOGI(" min rssi threshold: %" PRId8 "dBm", min_rssi_threshold_dbm_);
+ LOGI(" number of networks to match: %zu", networks_to_match_.size());
+ for (auto &net : networks_to_match_) {
+ net.Log();
+ }
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_params.cc b/apps/wifi_offload/scan_params.cc
new file mode 100644
index 0000000..67e0318
--- /dev/null
+++ b/apps/wifi_offload/scan_params.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_params.h"
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "chre/apps/wifi_offload/vector_serialization.h"
+
+namespace wifi_offload {
+
+ScanParams::ScanParams() : disconnected_mode_scan_interval_ms_(0) {}
+
+bool ScanParams::operator==(const ScanParams &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return ssids_to_scan_ == other.ssids_to_scan_ &&
+ frequencies_to_scan_mhz_ == other.frequencies_to_scan_mhz_ &&
+ disconnected_mode_scan_interval_ms_ ==
+ other.disconnected_mode_scan_interval_ms_;
+}
+
+flatbuffers::Offset<ScanParams::FbsType> ScanParams::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto ssid_vec = SerializeVector(ssids_to_scan_, builder);
+ auto freq_vec = builder->CreateVector(frequencies_to_scan_mhz_);
+ return fbs::CreateScanParams(*builder, ssid_vec, freq_vec,
+ disconnected_mode_scan_interval_ms_);
+}
+
+bool ScanParams::Deserialize(const ScanParams::FbsType &fbs_params) {
+ const auto &ssid_vec = fbs_params.ssids_to_scan();
+ if (ssid_vec == nullptr ||
+ !DeserializeVector<Ssid>(*ssid_vec, &ssids_to_scan_)) {
+ LOGE("Failed to deserialize ScanParams. Null or incomplete members.");
+ return false;
+ }
+
+ const auto &freq_vec = fbs_params.frequencies_to_scan_mhz();
+ if (freq_vec == nullptr) {
+ LOGE("Failed to deserialize ScanParams. Null or incomplete members.");
+ return false;
+ }
+ frequencies_to_scan_mhz_.clear();
+ frequencies_to_scan_mhz_.reserve(freq_vec->size());
+ for (const auto &freq : *freq_vec) {
+ if (!ChannelHistogram::IsSupportedFrequency(freq)) {
+ LOGE("Failed to deserialize ScanParams. Invalid frequency to scan.");
+ return false;
+ }
+ frequencies_to_scan_mhz_.push_back(freq);
+ }
+ disconnected_mode_scan_interval_ms_ =
+ fbs_params.disconnected_mode_scan_interval_ms();
+
+ return true;
+}
+
+void ScanParams::Log() const {
+ LOGI("ScanParams:");
+ LOGI(" disconnected mode scan interval (ms): %" PRIu32,
+ disconnected_mode_scan_interval_ms_);
+ LOGI(" number of ssids to scan: %zu", ssids_to_scan_.size());
+ for (const auto &ssid : ssids_to_scan_) {
+ ssid.Log();
+ }
+ LOGI(" number of frequencies to scan: %zu", frequencies_to_scan_mhz_.size());
+ for (const auto &freq : frequencies_to_scan_mhz_) {
+ LOGI(" frequency: %" PRIu32, freq);
+ }
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_record.cc b/apps/wifi_offload/scan_record.cc
new file mode 100644
index 0000000..9bbc5db
--- /dev/null
+++ b/apps/wifi_offload/scan_record.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_record.h"
+
+namespace wifi_offload {
+
+ScanRecord::ScanRecord()
+ : time_spent_scanning_ms_(0),
+ num_channels_scanned_(0),
+ num_entries_aggregated_(0) {}
+
+bool ScanRecord::operator==(const ScanRecord &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return time_spent_scanning_ms_ == other.time_spent_scanning_ms_ &&
+ num_channels_scanned_ == other.num_channels_scanned_ &&
+ num_entries_aggregated_ == other.num_entries_aggregated_;
+}
+
+flatbuffers::Offset<ScanRecord::FbsType> ScanRecord::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ return fbs::CreateScanRecord(*builder, time_spent_scanning_ms_,
+ num_channels_scanned_, num_entries_aggregated_);
+}
+
+bool ScanRecord::Deserialize(const ScanRecord::FbsType &fbs_record) {
+ time_spent_scanning_ms_ = fbs_record.time_spent_scanning_ms();
+ num_channels_scanned_ = fbs_record.num_channels_scanned();
+ num_entries_aggregated_ = fbs_record.num_entries_aggregated();
+ return true;
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_result.cc b/apps/wifi_offload/scan_result.cc
new file mode 100644
index 0000000..af3731d
--- /dev/null
+++ b/apps/wifi_offload/scan_result.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_result.h"
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "chre/apps/wifi_offload/utility.h"
+
+namespace wifi_offload {
+namespace {
+
+SecurityMode ConvertSecurityModeChreToOffload(int chre_security_mode) {
+ switch (chre_security_mode) {
+ case CHRE_WIFI_SECURITY_MODE_OPEN:
+ return SecurityMode::OPEN;
+ case CHRE_WIFI_SECURITY_MODE_WEP:
+ return SecurityMode::WEP;
+ case CHRE_WIFI_SECURITY_MODE_PSK:
+ return SecurityMode::PSK;
+ case CHRE_WIFI_SECURITY_MODE_EAP:
+ return SecurityMode::EAP;
+ default:
+ return SecurityMode::UNKNOWN;
+ }
+}
+
+} // namespace
+
+ScanResult::ScanResult()
+ : security_modes_(SecurityMode::UNKNOWN),
+ capability_(Capability::UNKNOWN),
+ frequency_scanned_mhz_(0),
+ rssi_dbm_(-128),
+ tsf_(0) {
+ std::memset(bssid_, 0, sizeof(bssid_));
+}
+
+ScanResult::ScanResult(const ScanResult &other)
+ : ssid_(other.ssid_),
+ security_modes_(other.security_modes_),
+ capability_(other.capability_),
+ frequency_scanned_mhz_(other.frequency_scanned_mhz_),
+ rssi_dbm_(other.rssi_dbm_),
+ tsf_(other.tsf_) {
+ std::memcpy(bssid_, other.bssid_, sizeof(bssid_));
+}
+
+ScanResult::ScanResult(const chreWifiScanResult &chre_scan_result) {
+ UpdateFromChreWifiScanResult(chre_scan_result);
+}
+
+bool ScanResult::operator==(const ScanResult &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return std::memcmp(bssid_, other.bssid_, sizeof(bssid_)) == 0 &&
+ ssid_ == other.ssid_ && security_modes_ == other.security_modes_ &&
+ capability_ == other.capability_ &&
+ frequency_scanned_mhz_ == other.frequency_scanned_mhz_ &&
+ rssi_dbm_ == other.rssi_dbm_ && tsf_ == other.tsf_;
+}
+
+flatbuffers::Offset<ScanResult::FbsType> ScanResult::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto ssid_offset = ssid_.Serialize(builder);
+ auto bssid_offset = builder->CreateVector(bssid_, kBssidSize);
+ return fbs::CreateScanResult(*builder, ssid_offset, security_modes_,
+ bssid_offset, capability_,
+ frequency_scanned_mhz_, rssi_dbm_, tsf_);
+}
+
+bool ScanResult::Deserialize(const ScanResult::FbsType &fbs_result) {
+ if (fbs_result.ssid() == nullptr || !ssid_.Deserialize(*fbs_result.ssid())) {
+ LOGE("Failed to deserialize ScanResult. Null or incomplete members.");
+ return false;
+ }
+
+ security_modes_ = fbs_result.security_modes();
+ if (security_modes_ & ~SecurityMode::ALL_SECURITY_MODES_MASK) {
+ LOGE("Failed to deserialize ScanResult. Invalid security mode.");
+ return false;
+ }
+
+ if (fbs_result.bssid() == nullptr ||
+ fbs_result.bssid()->size() != kBssidSize) {
+ LOGE("Failed to deserialize ScanResult. Null or incomplete members.");
+ return false;
+ }
+ for (uint8_t i = 0; i < kBssidSize; i++) {
+ bssid_[i] = fbs_result.bssid()->Get(i);
+ }
+
+ capability_ = fbs_result.capability();
+ if ((capability_ == Capability::UNKNOWN) ||
+ (capability_ & ~Capability::ALL_CAPABILITIES_MASK)) {
+ LOGE("Failed to deserialize ScanResult. Invalid network capability.");
+ return false;
+ }
+
+ frequency_scanned_mhz_ = fbs_result.frequency_scanned_mhz();
+ if (!ChannelHistogram::IsSupportedFrequency(frequency_scanned_mhz_)) {
+ LOGE("Failed to deserialize ScanResult. Invalid channel frequency.");
+ return false;
+ }
+
+ rssi_dbm_ = fbs_result.rssi_dbm();
+ if (rssi_dbm_ > 0) {
+ LOGE("Failed to deserialize ScanResult. Positive rssi value.");
+ return false;
+ }
+
+ tsf_ = fbs_result.tsf();
+ return true;
+}
+
+void ScanResult::Log() const {
+ LOGI("ScanResult:");
+ ssid_.Log();
+ LOGI(" security modes: 0x%" PRIx8, security_modes_);
+ utility::LogBssid(bssid_);
+ LOGI(" capability: 0x%" PRIx16, capability_);
+ LOGI(" scanned frequency: %" PRIu32, frequency_scanned_mhz_);
+ LOGI(" rssi: %" PRId8 "dBm", rssi_dbm_);
+ LOGI(" tsf: %" PRIu64, tsf_);
+}
+
+void ScanResult::UpdateFromChreWifiScanResult(
+ const chreWifiScanResult &chre_scan_result) {
+ ssid_.SetData(chre_scan_result.ssid, chre_scan_result.ssidLen);
+
+ security_modes_ = 0;
+ for (const auto chre_security_mode :
+ {CHRE_WIFI_SECURITY_MODE_OPEN, CHRE_WIFI_SECURITY_MODE_WEP,
+ CHRE_WIFI_SECURITY_MODE_PSK, CHRE_WIFI_SECURITY_MODE_EAP}) {
+ if (chre_scan_result.securityMode & chre_security_mode) {
+ security_modes_ |= ConvertSecurityModeChreToOffload(chre_security_mode);
+ }
+ }
+
+ std::memcpy(bssid_, chre_scan_result.bssid, CHRE_WIFI_BSSID_LEN);
+ // TODO: make sure capability definition between two versions is the same
+ // (802.11:7.3.1.4 vs. 802.11:8.4.1.4)
+ capability_ = chre_scan_result.capabilityInfo;
+ if (chre_scan_result.channelWidth == CHRE_WIFI_CHANNEL_WIDTH_20_MHZ) {
+ frequency_scanned_mhz_ = chre_scan_result.primaryChannel;
+ } else {
+ // TODO: (b/62870147) Support other possible channel widths
+ LOGW("Scan result channel width not supported %" PRIu8,
+ chre_scan_result.channelWidth);
+ }
+
+ rssi_dbm_ = chre_scan_result.rssi;
+ tsf_ = 0; // tsf value not available
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_result_message.cc b/apps/wifi_offload/scan_result_message.cc
new file mode 100644
index 0000000..e82ff0b
--- /dev/null
+++ b/apps/wifi_offload/scan_result_message.cc
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_result_message.h"
+#include "chre/apps/wifi_offload/vector_serialization.h"
+
+namespace wifi_offload {
+
+void ScanResultMessage::SetScanResults(const Vector<ScanResult> &results) {
+ scan_results_.clear();
+ scan_results_.reserve(results.size());
+ for (const auto &result : results) {
+ scan_results_.emplace_back(result);
+ }
+}
+
+void ScanResultMessage::GetScanResults(Vector<ScanResult> *results) {
+ if (results == nullptr) {
+ LOGE("ScanResultsMessage output pointer is null in GetScanResults.");
+ return;
+ }
+
+ results->clear();
+ results->reserve(scan_results_.size());
+ for (const auto &result : scan_results_) {
+ results->emplace_back(result);
+ }
+}
+
+flatbuffers::Offset<ScanResultMessage::FbsType> ScanResultMessage::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto results = SerializeVector(scan_results_, builder);
+ return fbs::CreateScanResultMessage(*builder, results);
+}
+
+bool ScanResultMessage::Deserialize(
+ const ScanResultMessage::FbsType &fbs_result_message) {
+ const auto &fbs_results = fbs_result_message.scan_results();
+ if (fbs_results == nullptr || fbs_results->size() == 0) {
+ LOGE(
+ "Failed to deserialize ScanResultsMessage. Null or incomplete "
+ "members.");
+ return false;
+ }
+
+ if (!DeserializeVector<ScanResult>(*fbs_results, &scan_results_)) {
+ LOGE(
+ "Failed to deserialize ScanResultMessage. Null or incomplete members.");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/scan_stats.cc b/apps/wifi_offload/scan_stats.cc
new file mode 100644
index 0000000..eb2aa0b
--- /dev/null
+++ b/apps/wifi_offload/scan_stats.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/scan_stats.h"
+#include "chre/apps/wifi_offload/vector_serialization.h"
+
+namespace wifi_offload {
+
+ScanStats::ScanStats()
+ : num_scans_requested_by_nanoapp_(0),
+ num_scans_serviced_by_hardware_(0),
+ num_scans_serviced_by_cache_(0),
+ updated_at_chre_ms_(0),
+ sent_at_chre_ms_(0),
+ last_subscription_duration_ms_(0) {}
+
+bool ScanStats::operator==(const ScanStats &other) const {
+ if (this == &other) {
+ return true;
+ }
+ return num_scans_requested_by_nanoapp_ ==
+ other.num_scans_requested_by_nanoapp_ &&
+ num_scans_serviced_by_hardware_ ==
+ other.num_scans_serviced_by_hardware_ &&
+ num_scans_serviced_by_cache_ == other.num_scans_serviced_by_cache_ &&
+ updated_at_chre_ms_ == other.updated_at_chre_ms_ &&
+ sent_at_chre_ms_ == other.sent_at_chre_ms_ &&
+ last_subscription_duration_ms_ ==
+ other.last_subscription_duration_ms_ &&
+ channel_histogram_ == other.channel_histogram_ &&
+ scan_records_ == other.scan_records_ &&
+ rpc_log_records_ == other.rpc_log_records_;
+}
+
+flatbuffers::Offset<ScanStats::FbsType> ScanStats::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ auto histo = channel_histogram_.Serialize(builder);
+ auto scan_recs = SerializeVector(scan_records_, builder);
+ auto log_recs = SerializeVector(rpc_log_records_, builder);
+ return fbs::CreateScanStats(*builder, num_scans_requested_by_nanoapp_,
+ num_scans_serviced_by_hardware_,
+ num_scans_serviced_by_cache_, updated_at_chre_ms_,
+ sent_at_chre_ms_, last_subscription_duration_ms_,
+ histo, scan_recs, log_recs);
+}
+
+bool ScanStats::Deserialize(const ScanStats::FbsType &fbs_stats) {
+ const auto &histo = fbs_stats.channel_scan_count();
+ if (histo == nullptr || !channel_histogram_.Deserialize(*histo)) {
+ LOGE("Failed to deserialize ScanStats. Null or incomplete members.");
+ return false;
+ }
+
+ const auto &scan_recs = fbs_stats.scan_records();
+ if (scan_recs == nullptr ||
+ !DeserializeVector<ScanRecord>(*scan_recs, &scan_records_)) {
+ LOGE("Failed to deserialize ScanStats. Null or incomplete members.");
+ return false;
+ }
+
+ const auto &log_recs = fbs_stats.rpc_log_records();
+ if (log_recs == nullptr ||
+ !DeserializeVector<RpcLogRecord>(*log_recs, &rpc_log_records_)) {
+ LOGE("Failed to deserialize ScanStats. Null or incomplete members.");
+ return false;
+ }
+
+ num_scans_requested_by_nanoapp_ = fbs_stats.num_scans_requested_by_nanoapp();
+ num_scans_serviced_by_hardware_ = fbs_stats.num_scans_serviced_by_hardware();
+ num_scans_serviced_by_cache_ = fbs_stats.num_scans_serviced_by_cache();
+ updated_at_chre_ms_ = fbs_stats.updated_at_chre_ms();
+ sent_at_chre_ms_ = fbs_stats.sent_at_chre_ms();
+ last_subscription_duration_ms_ = fbs_stats.last_subscription_duration_ms();
+ return true;
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/ssid.cc b/apps/wifi_offload/ssid.cc
new file mode 100644
index 0000000..17dfc65
--- /dev/null
+++ b/apps/wifi_offload/ssid.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/apps/wifi_offload/ssid.h"
+#include "chre/apps/wifi_offload/utility.h"
+
+namespace wifi_offload {
+
+Ssid::Ssid(const Ssid &other) {
+ SetData(other.ssid_vec_.data(), other.ssid_vec_.size());
+}
+
+void Ssid::SetData(const uint8_t *buff, size_t len) {
+ if (len > kMaxSsidLen) {
+ LOGE("Ssid buffer len %zu larger than max ssid len %zu. Truncating.", len,
+ kMaxSsidLen);
+ len = kMaxSsidLen;
+ }
+
+ ssid_vec_.clear();
+ ssid_vec_.reserve(len);
+ for (size_t i = 0; i < len; i++) {
+ ssid_vec_.push_back(buff[i]);
+ }
+}
+
+bool Ssid::operator==(const Ssid &other) const {
+ return ssid_vec_ == other.ssid_vec_;
+}
+
+flatbuffers::Offset<Ssid::FbsType> Ssid::Serialize(
+ flatbuffers::FlatBufferBuilder *builder) const {
+ return fbs::CreateSsid(*builder, builder->CreateVector(ssid_vec_));
+}
+
+bool Ssid::Deserialize(const Ssid::FbsType &fbs_ssid) {
+ const auto &ssid_vec = fbs_ssid.ssid();
+ if (ssid_vec == nullptr) {
+ LOGE("Failed to deserialize Ssid. Null or incomplete members.");
+ return false;
+ }
+
+ if (ssid_vec->size() > kMaxSsidLen) {
+ LOGE("Failed to deserialize Ssid. Ssid size is larger than max len.");
+ return false;
+ }
+
+ SetData(ssid_vec->data(), ssid_vec->size());
+ return true;
+}
+
+void Ssid::Log() const {
+ utility::LogSsid(ssid_vec_.data(), static_cast<uint8_t>(ssid_vec_.size()));
+}
+
+void Ssid::ToChreWifiSsidListItem(chreWifiSsidListItem *chre_ssid) const {
+ if (chre_ssid == nullptr) {
+ LOGW("Failed to convert to chreWifiSsidListItem. Output pointer is null");
+ return;
+ }
+
+ std::memcpy(chre_ssid->ssid, ssid_vec_.data(), ssid_vec_.size());
+ chre_ssid->ssidLen = static_cast<uint8_t>(ssid_vec_.size());
+}
+
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/test/channelhistogram_test.cc b/apps/wifi_offload/test/channelhistogram_test.cc
new file mode 100644
index 0000000..9ec5e94
--- /dev/null
+++ b/apps/wifi_offload/test/channelhistogram_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "include/random_generator.h"
+#include "include/utility.h"
+
+using wifi_offload_test::kNumFrequencies_Test;
+using wifi_offload_test::kAllFrequencies_Test;
+using wifi_offload_test::kAllChannels_Test;
+
+/**
+ * This file includes all the unit tests for ChannelHistogram class, except ==
+ * operator and serialize/deserialize functions which have already been covered
+ * in offloadtypes_test.cc.
+ */
+
+class ChannelHistogramTest : public testing::Test {
+ public:
+ wifi_offload_test::RandomGenerator random_gen_;
+ wifi_offload::ChannelHistogram channel_histo_;
+};
+
+TEST_F(ChannelHistogramTest, UnsupportedFrequencyAndchannel_numbersAreHandled) {
+ // some unsupported frequencies
+ channel_histo_.IncrementScanCountForFrequency(2000);
+ channel_histo_.IncrementScanCountForFrequency(42000);
+
+ // verify no count have been increased
+ for (size_t i = 0; i < kNumFrequencies_Test; i++) {
+ ASSERT_EQ(0, channel_histo_.GetChannelScanCount(kAllChannels_Test[i]));
+ }
+
+ // some unsupported channel numbers
+ EXPECT_EQ(0, channel_histo_.GetChannelScanCount(0));
+ EXPECT_EQ(0, channel_histo_.GetChannelScanCount(15));
+ EXPECT_EQ(0, channel_histo_.GetChannelScanCount(200));
+}
+
+TEST_F(ChannelHistogramTest, FrequenciesMapCorrectlyTochannel_numbers) {
+ for (size_t i = 0; i < kNumFrequencies_Test; i++) {
+ wifi_offload::ChannelHistogram histo;
+ histo.IncrementScanCountForFrequency(kAllFrequencies_Test[i]);
+
+ // verify only the increase channel is non-zero
+ for (size_t j = 0; j < kNumFrequencies_Test; j++) {
+ EXPECT_EQ(kAllChannels_Test[i] == kAllChannels_Test[j] ? 255 : 0,
+ histo.GetChannelScanCount(kAllChannels_Test[j]));
+ }
+ }
+}
+
+TEST_F(ChannelHistogramTest, IncreaseFrequencyScanCountAndGetChannelScanCount) {
+ uint32_t increase = 1;
+ for (size_t i = 0; i < 12; i++) {
+ increase *= 2;
+ channel_histo_.IncrementScanCountForFrequencyForTest(
+ kAllFrequencies_Test[i], increase);
+ }
+
+ uint32_t expected = 1;
+ for (size_t i = 0; i < 12; i++) {
+ uint8_t scaled_value =
+ channel_histo_.GetChannelScanCount(kAllChannels_Test[i]);
+
+ expected *= 2;
+ EXPECT_EQ(expected * 254 / increase + 1, scaled_value);
+ }
+
+ for (size_t i = 12; i < 14; i++) {
+ EXPECT_EQ(0, channel_histo_.GetChannelScanCount(kAllChannels_Test[i]));
+ }
+}
+
+TEST_F(ChannelHistogramTest, SetScanCountToMaxUInt32) {
+ // add some nice scan counts
+ uint32_t increase = 1;
+ for (size_t i = 0; i < 12; i++) {
+ increase *= 2;
+ channel_histo_.IncrementScanCountForFrequencyForTest(
+ kAllFrequencies_Test[i], increase);
+ }
+ /* set the next scan count to be 2 ^ 32 - 1, this will force all other counts
+ * to get mapped to 1 */
+ channel_histo_.IncrementScanCountForFrequencyForTest(kAllFrequencies_Test[12],
+ 0xffffffff);
+
+ for (size_t i = 0; i < 12; i++) {
+ EXPECT_EQ(1, channel_histo_.GetChannelScanCount(kAllChannels_Test[i]));
+ }
+ EXPECT_EQ(255, channel_histo_.GetChannelScanCount(kAllChannels_Test[12]));
+}
diff --git a/apps/wifi_offload/test/chrescanparamssafe_test.cc b/apps/wifi_offload/test/chrescanparamssafe_test.cc
new file mode 100644
index 0000000..ac5c89b
--- /dev/null
+++ b/apps/wifi_offload/test/chrescanparamssafe_test.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "chre/apps/wifi_offload/chre_scan_params_safe.h"
+#include "include/utility.h"
+
+class ChreScanParamsSafeTest : public testing::Test {
+ public:
+ wifi_offload_test::RandomGenerator random_gen_;
+ wifi_offload::ScanParams nanoapp_scan_params_;
+
+ void ConstructChreScanParamsSafeAndCompareWithOrigScanParams() {
+ wifi_offload::ChreScanParamsSafe chre_scan_params_safe(
+ nanoapp_scan_params_);
+ const chreWifiScanParams *chre_scan_params =
+ chre_scan_params_safe.GetChreWifiScanParams();
+
+ EXPECT_EQ(CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS,
+ chre_scan_params->scanType);
+ EXPECT_EQ(0, chre_scan_params->maxScanAgeMs);
+
+ ASSERT_EQ(nanoapp_scan_params_.ssids_to_scan_.size(),
+ chre_scan_params->ssidListLen);
+ for (size_t i = 0; i < chre_scan_params->ssidListLen; i++) {
+ chreWifiSsidListItem ssid_item;
+ nanoapp_scan_params_.ssids_to_scan_[i].ToChreWifiSsidListItem(&ssid_item);
+ ASSERT_EQ(ssid_item.ssidLen, chre_scan_params->ssidList[i].ssidLen);
+ EXPECT_EQ(0,
+ std::memcmp(ssid_item.ssid, chre_scan_params->ssidList[i].ssid,
+ chre_scan_params->ssidList[i].ssidLen));
+ }
+ ASSERT_EQ(nanoapp_scan_params_.frequencies_to_scan_mhz_.size(),
+ chre_scan_params->frequencyListLen);
+ EXPECT_EQ(
+ 0, std::memcmp(nanoapp_scan_params_.frequencies_to_scan_mhz_.data(),
+ chre_scan_params->frequencyList,
+ chre_scan_params->frequencyListLen * sizeof(uint32_t)));
+ }
+};
+
+TEST_F(ChreScanParamsSafeTest,
+ ConstructsChreScanParamsSafeAndComparesWithOriginalScanParams) {
+ init(nanoapp_scan_params_.frequencies_to_scan_mhz_, random_gen_,
+ CHRE_WIFI_FREQUENCY_LIST_MAX_LEN - 5);
+ init(nanoapp_scan_params_.ssids_to_scan_, random_gen_,
+ CHRE_WIFI_SSID_LIST_MAX_LEN - 2);
+ ConstructChreScanParamsSafeAndCompareWithOrigScanParams();
+}
+
+TEST_F(ChreScanParamsSafeTest, ConstructsChreScanParamsSafeWithEmptyFreqList) {
+ init(nanoapp_scan_params_.ssids_to_scan_, random_gen_,
+ CHRE_WIFI_SSID_LIST_MAX_LEN - 2);
+ ConstructChreScanParamsSafeAndCompareWithOrigScanParams();
+}
+
+TEST_F(ChreScanParamsSafeTest, ConstructsChreScanParamsSafeWithEmptySsidList) {
+ init(nanoapp_scan_params_.frequencies_to_scan_mhz_, random_gen_,
+ CHRE_WIFI_FREQUENCY_LIST_MAX_LEN - 5);
+ ConstructChreScanParamsSafeAndCompareWithOrigScanParams();
+}
+
+TEST_F(ChreScanParamsSafeTest, ChreScanParamsSafeTruncatesLongLists) {
+ // initialize frequency and ssid lists to exceed limit size
+ init(nanoapp_scan_params_.frequencies_to_scan_mhz_, random_gen_,
+ CHRE_WIFI_FREQUENCY_LIST_MAX_LEN + 5);
+ init(nanoapp_scan_params_.ssids_to_scan_, random_gen_,
+ CHRE_WIFI_SSID_LIST_MAX_LEN + 2);
+
+ wifi_offload::ChreScanParamsSafe chre_scan_params_safe(nanoapp_scan_params_);
+ const chreWifiScanParams *chre_scan_params =
+ chre_scan_params_safe.GetChreWifiScanParams();
+
+ EXPECT_EQ(CHRE_WIFI_SSID_LIST_MAX_LEN, chre_scan_params->ssidListLen);
+ EXPECT_EQ(CHRE_WIFI_FREQUENCY_LIST_MAX_LEN,
+ chre_scan_params->frequencyListLen);
+}
diff --git a/apps/wifi_offload/test/flatbuffersserialization_test.cc b/apps/wifi_offload/test/flatbuffersserialization_test.cc
new file mode 100644
index 0000000..7e061ca
--- /dev/null
+++ b/apps/wifi_offload/test/flatbuffersserialization_test.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstring>
+#include "gtest/gtest.h"
+
+#include "chre/apps/wifi_offload/flatbuffers_serialization.h"
+
+#include "include/utility.h"
+
+using wifi_offload::fbs::Serialize;
+using wifi_offload::fbs::Deserialize;
+
+template <typename TestType>
+class FlatbuffersSerializationTest : public testing::Test {
+ public:
+ // RandomGenerator used to initialize data-types with random values
+ wifi_offload_test::RandomGenerator random_gen_;
+
+ static const size_t kBufferLen = CHRE_MESSAGE_TO_HOST_MAX_SIZE;
+ uint8_t buffer[kBufferLen];
+};
+
+typedef testing::Types<wifi_offload::ScanStats, wifi_offload::ScanConfig,
+ wifi_offload::Vector<wifi_offload::ScanResult>>
+ Implementations;
+
+TYPED_TEST_CASE(FlatbuffersSerializationTest, Implementations);
+
+TYPED_TEST(FlatbuffersSerializationTest,
+ SerializationWithNullBufferReturnsRequiredBufferSize) {
+ TypeParam test_obj;
+ init(test_obj, this->random_gen_);
+
+ size_t required_buff_size = Serialize(test_obj, nullptr, 0);
+ EXPECT_NE(0, required_buff_size);
+
+ size_t serialized_size = Serialize(test_obj, this->buffer, this->kBufferLen);
+ ASSERT_NE(0, serialized_size);
+
+ EXPECT_EQ(serialized_size, required_buff_size);
+}
+
+TYPED_TEST(FlatbuffersSerializationTest,
+ SerializationThenDeserializationCreatesEqualValue) {
+ TypeParam test_obj;
+ init(test_obj, this->random_gen_);
+
+ size_t serialized_size = Serialize(test_obj, this->buffer, this->kBufferLen);
+ ASSERT_NE(0, serialized_size);
+
+ TypeParam deserialized_obj;
+ ASSERT_TRUE(Deserialize(this->buffer, serialized_size, &deserialized_obj));
+ EXPECT_EQ(test_obj, deserialized_obj);
+}
+
+TYPED_TEST(FlatbuffersSerializationTest, NegativeTestsForSerialization) {
+ TypeParam test_obj;
+ init(test_obj, this->random_gen_);
+
+ EXPECT_EQ(0, Serialize(test_obj, this->buffer, 0)); // zero buffer size
+ EXPECT_EQ(0, Serialize(test_obj, this->buffer, 10)); // buffer too small
+}
+
+TYPED_TEST(FlatbuffersSerializationTest, NegativeTestsForDeserialization) {
+ TypeParam test_obj;
+ init(test_obj, this->random_gen_);
+
+ // The first 4 bytes in the buffer represent the position of the root
+ // table, so corrupting it should force the deserialization to fail.
+ constexpr size_t kRootTableOffsetSize = 4;
+ size_t serialized_size = Serialize(test_obj, this->buffer, this->kBufferLen);
+ ASSERT_GE(serialized_size, kRootTableOffsetSize);
+
+ TypeParam new_obj;
+ EXPECT_FALSE(Deserialize(nullptr, serialized_size, &new_obj));
+ EXPECT_FALSE(Deserialize(this->buffer, 0, &new_obj));
+
+ // Corrupt the root table's offset
+ std::memset(this->buffer, 0xff, kRootTableOffsetSize);
+ EXPECT_FALSE(Deserialize(this->buffer, serialized_size, &new_obj));
+}
diff --git a/apps/wifi_offload/test/include/random_generator.h b/apps/wifi_offload/test/include/random_generator.h
new file mode 100644
index 0000000..8ecbcf9
--- /dev/null
+++ b/apps/wifi_offload/test/include/random_generator.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_TEST_RANDOM_GENERATOR_H_
+#define CHRE_WIFI_OFFLOAD_TEST_RANDOM_GENERATOR_H_
+
+#include <cinttypes>
+#include <random>
+
+namespace wifi_offload_test {
+
+/**
+ * @class RandomGenerator A class to generate random values for any uint type:
+ * (uint8_t, ..., uint64_t). Also supports resetting to its initial
+ * state to be able to reproduce the same random sequence.
+ */
+class RandomGenerator {
+ public:
+ /**
+ * Default constructs a RandomGenerator object
+ */
+ RandomGenerator();
+
+ /**
+ * Resets the object to its initial state. Useful if we want to reproduce the
+ * exact same sequenct again.
+ */
+ void Reset();
+
+ /**
+ * Generates a random number of type IntType
+ *
+ * @return A random number of type IntType
+ */
+ template <typename IntType>
+ IntType get() {
+ return static_cast<IntType>(uniform_distribution_(random_engine_));
+ }
+
+ private:
+ /* The initial seed kept to use again when need to reset */
+ std::random_device::result_type initial_seed_;
+
+ /* Standard mersenne_twister_engine */
+ std::mt19937 random_engine_;
+
+ /**
+ * Use uniform_distribution_ to transform the random unsigned int generated by
+ * random_engine_ into an uint64_t
+ */
+ std::uniform_int_distribution<uint64_t> uniform_distribution_;
+};
+
+} // wifi_offload_test namespace
+
+#endif // CHRE_WIFI_OFFLOAD_TEST_RANDOM_GENERATOR_H_
diff --git a/apps/wifi_offload/test/include/utility.h b/apps/wifi_offload/test/include/utility.h
new file mode 100644
index 0000000..7546b0d
--- /dev/null
+++ b/apps/wifi_offload/test/include/utility.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_WIFI_OFFLOAD_TEST_UTILITY_H_
+#define CHRE_WIFI_OFFLOAD_TEST_UTILITY_H_
+
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+#include "chre/apps/wifi_offload/channel_histogram.h"
+#include "chre/apps/wifi_offload/preferred_network.h"
+#include "chre/apps/wifi_offload/rpc_log_record.h"
+#include "chre/apps/wifi_offload/scan_config.h"
+#include "chre/apps/wifi_offload/scan_filter.h"
+#include "chre/apps/wifi_offload/scan_params.h"
+#include "chre/apps/wifi_offload/scan_record.h"
+#include "chre/apps/wifi_offload/scan_result.h"
+#include "chre/apps/wifi_offload/scan_stats.h"
+
+#include "../include/random_generator.h"
+
+namespace wifi_offload_test {
+
+constexpr uint8_t kNumFrequencies_Test = 74;
+/* Supported frequencies in 2.4GHz (802.11b/g/n) and 5GHz (802.11a/h/j/n/ac) */
+constexpr uint16_t kAllFrequencies_Test[kNumFrequencies_Test] = {
+ 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462,
+ 2467, 2472, 2484, 5035, 5040, 5045, 5055, 5060, 5080, 5170, 5180,
+ 5190, 5200, 5210, 5220, 5230, 5240, 5250, 5260, 5270, 5280, 5290,
+ 5300, 5310, 5320, 5500, 5510, 5520, 5530, 5540, 5550, 5560, 5570,
+ 5580, 5590, 5600, 5610, 5620, 5630, 5640, 5660, 5670, 5680, 5690,
+ 5700, 5710, 5720, 5745, 5755, 5765, 5775, 5785, 5795, 5805, 5825,
+ 4915, 4920, 4925, 4935, 4940, 4945, 4960, 4980,
+};
+
+/* Supported channel numbers that matches frequencies in
+ * kAllFrequencies_Test. 2.4GHz (802.11b/g/n) and 5GHz (802.11a/h/j/n/ac) */
+constexpr uint8_t kAllChannels_Test[kNumFrequencies_Test] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 7,
+ 8, 9, 11, 12, 16, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52,
+ 54, 56, 58, 60, 62, 64, 100, 102, 104, 106, 108, 110, 112, 114, 116,
+ 118, 120, 122, 124, 126, 128, 132, 134, 136, 138, 140, 142, 144, 149, 151,
+ 153, 155, 157, 159, 161, 165, 183, 184, 185, 187, 188, 189, 192, 196,
+};
+
+void init(uint8_t &val, RandomGenerator &rand_gen);
+void init(uint16_t &val, RandomGenerator &rand_gen);
+void init(uint32_t &val, RandomGenerator &rand_gen);
+void init(uint64_t &val, RandomGenerator &rand_gen);
+void init(uint8_t *arr, size_t len, RandomGenerator &rand_gen);
+void init(wifi_offload::Ssid &ssid, RandomGenerator &rand_gen);
+void init(wifi_offload::PreferredNetwork &net_info, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanRecord &record, RandomGenerator &rand_gen);
+void init(wifi_offload::RpcLogRecord &record, RandomGenerator &rand_gen);
+void init(wifi_offload::ChannelHistogram &histo, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanStats &stats, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanParams ¶ms, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanFilter &filter, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanConfig &config, RandomGenerator &rand_gen);
+void init(wifi_offload::ScanResult &result, RandomGenerator &rand_gen);
+void init(chreWifiScanResult &result, RandomGenerator &rand_gen);
+
+template <typename T>
+void init(wifi_offload::Vector<T> &vec, RandomGenerator &rand_gen,
+ size_t vec_len) {
+ vec.clear();
+ for (size_t i = 0; i < vec_len; i++) {
+ T new_elem;
+ init(new_elem, rand_gen);
+ vec.push_back(std::move(new_elem));
+ }
+}
+
+template <typename T>
+void init(wifi_offload::Vector<T> &vec, RandomGenerator &rand_gen) {
+ size_t vec_len = (rand_gen.get<uint8_t>() % 10) + 1;
+ init(vec, rand_gen, vec_len);
+}
+
+} // wifioffloadtesthelper namespace
+
+#endif // CHRE_WIFI_OFFLOAD_TEST_UTILITY_H_
diff --git a/apps/wifi_offload/test/offloadtypes_test.cc b/apps/wifi_offload/test/offloadtypes_test.cc
new file mode 100644
index 0000000..350e284
--- /dev/null
+++ b/apps/wifi_offload/test/offloadtypes_test.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "include/utility.h"
+
+template <typename TestType>
+class OffloadTypesTest : public testing::Test {
+ public:
+ // RandomGenerator used to initialize data-types with random values
+ wifi_offload_test::RandomGenerator random_gen_;
+
+ flatbuffers::FlatBufferBuilder builder;
+
+ void EqualOperatorReturnsTrueForEqualValues() {
+ TestType lhs;
+ init(lhs, random_gen_);
+
+ random_gen_.Reset();
+ TestType rhs;
+ init(rhs, random_gen_);
+
+ EXPECT_EQ(lhs, rhs);
+ }
+
+ void EqualOperatorReturnsFalseForDifferentValues() {
+ TestType lhs, rhs;
+ init(lhs, random_gen_);
+ init(rhs, random_gen_);
+
+ ASSERT_FALSE(lhs == rhs);
+ }
+
+ void SerializationThenDeserializationCreatesEqualValue() {
+ TestType test_obj;
+ init(test_obj, random_gen_);
+ builder.Finish(test_obj.Serialize(&builder));
+
+ const uint8_t *serialized_buff = builder.GetBufferPointer();
+ const size_t serialized_size = builder.GetSize();
+ ASSERT_NE(nullptr, serialized_buff);
+ ASSERT_NE(0, serialized_size);
+
+ flatbuffers::Verifier verifier(serialized_buff, serialized_size);
+ ASSERT_TRUE(verifier.VerifyBuffer<typename TestType::FbsType>(nullptr));
+
+ const auto fbs_obj =
+ flatbuffers::GetRoot<typename TestType::FbsType>(serialized_buff);
+ ASSERT_NE(nullptr, fbs_obj);
+
+ TestType deserialized_obj;
+ ASSERT_TRUE(deserialized_obj.Deserialize(*fbs_obj));
+ EXPECT_EQ(test_obj, deserialized_obj);
+ }
+};
+
+typedef testing::Types<wifi_offload::PreferredNetwork, wifi_offload::ScanResult,
+ wifi_offload::ScanParams, wifi_offload::ScanFilter,
+ wifi_offload::ScanConfig, wifi_offload::ScanRecord,
+ wifi_offload::RpcLogRecord, wifi_offload::ScanStats>
+ Implementations;
+
+TYPED_TEST_CASE(OffloadTypesTest, Implementations);
+
+TYPED_TEST(OffloadTypesTest, EqualOperatorReturnsTrueForEqualValues) {
+ this->EqualOperatorReturnsTrueForEqualValues();
+}
+
+TYPED_TEST(OffloadTypesTest, EqualOperatorReturnsFalseForDifferentValues) {
+ this->EqualOperatorReturnsFalseForDifferentValues();
+}
+
+TYPED_TEST(OffloadTypesTest,
+ SerializationThenDeserializationCreatesEqualValue) {
+ this->SerializationThenDeserializationCreatesEqualValue();
+}
diff --git a/apps/wifi_offload/test/random_generator.cc b/apps/wifi_offload/test/random_generator.cc
new file mode 100644
index 0000000..9a57db0
--- /dev/null
+++ b/apps/wifi_offload/test/random_generator.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/random_generator.h"
+
+namespace wifi_offload_test {
+namespace {
+/* use default seed (vs. random seed) to reproduce the same sequence on
+ * different runs */
+constexpr bool kUseDefaultSeed = true;
+} // namespace
+
+RandomGenerator::RandomGenerator() {
+ if (kUseDefaultSeed) {
+ initial_seed_ = random_engine_.default_seed;
+ } else {
+ /* Used to obtain a seed for the random number engine */
+ std::random_device random_device;
+ // save the seed for future resets
+ initial_seed_ = random_device();
+ }
+
+ Reset();
+}
+
+void RandomGenerator::Reset() {
+ random_engine_.seed(initial_seed_);
+ uniform_distribution_.reset();
+}
+
+} // wifi_offload_test namespace
diff --git a/apps/wifi_offload/test/randomgenerator_test.cc b/apps/wifi_offload/test/randomgenerator_test.cc
new file mode 100644
index 0000000..3b0237f
--- /dev/null
+++ b/apps/wifi_offload/test/randomgenerator_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits>
+#include "gtest/gtest.h"
+
+#include "include/random_generator.h"
+
+template <typename TestType>
+class RandomGeneratorTest : public testing::Test {
+ public:
+ wifi_offload_test::RandomGenerator random_gen_;
+
+ void GeneratedNumbersAreSmallerOrEqualTypeMaxValue() {
+ uint64_t rand_val = random_gen_.get<TestType>();
+ uint64_t max_val = std::numeric_limits<TestType>::max();
+
+ EXPECT_TRUE(rand_val <= max_val);
+ }
+
+ void AllRandomGeneratorsGenerateTheSameSequence() {
+ constexpr size_t num_values = 10;
+ TestType rand_values[num_values];
+
+ for (size_t i = 0; i < num_values; i++) {
+ rand_values[i] = random_gen_.get<TestType>();
+ }
+
+ wifi_offload_test::RandomGenerator another_random_gen_;
+ for (size_t i = 0; i < num_values; i++) {
+ ASSERT_EQ(rand_values[i], another_random_gen_.get<TestType>());
+ }
+ }
+
+ void AfterResetGeneratesTheSameSequence() {
+ constexpr size_t num_values = 10;
+ TestType rand_values[num_values];
+
+ for (size_t i = 0; i < num_values; i++) {
+ rand_values[i] = random_gen_.get<TestType>();
+ }
+
+ random_gen_.Reset();
+ for (size_t i = 0; i < num_values; i++) {
+ ASSERT_EQ(rand_values[i], random_gen_.get<TestType>());
+ }
+ }
+
+ void GeneratesDifferentNumbersIn8Bytes() {
+ constexpr size_t num_values = 10;
+ uint64_t rand_values[num_values];
+
+ constexpr size_t repeats_to_fill_8_bytes =
+ sizeof(uint64_t) / sizeof(TestType);
+ constexpr size_t shift_size =
+ (sizeof(TestType) * 8) % (sizeof(uint64_t) * 8);
+
+ for (size_t i = 0; i < num_values; i++) {
+ rand_values[i] = 0;
+ for (size_t j = 0; j < repeats_to_fill_8_bytes; j++) {
+ rand_values[i] <<= shift_size;
+ rand_values[i] |= random_gen_.get<TestType>();
+ }
+ }
+
+ // The probability of choosing equal random numbers out of 2 ^ 64 values
+ // is "extremely" small. Smaller than the change of memory failure.
+ for (size_t i = 0; i < num_values - 1; i++) {
+ for (size_t j = i + 1; j < num_values; j++) {
+ ASSERT_NE(rand_values[i], rand_values[j]);
+ }
+ }
+ }
+};
+
+typedef testing::Types<uint8_t, uint16_t, uint32_t, uint64_t> Implementations;
+
+TYPED_TEST_CASE(RandomGeneratorTest, Implementations);
+
+TYPED_TEST(RandomGeneratorTest, GeneratedNumbersAreSmallerOrEqualTypeMaxValue) {
+ this->GeneratedNumbersAreSmallerOrEqualTypeMaxValue();
+}
+
+TYPED_TEST(RandomGeneratorTest, AllRandomGeneratorsGenerateTheSameSequence) {
+ this->AllRandomGeneratorsGenerateTheSameSequence();
+}
+
+TYPED_TEST(RandomGeneratorTest, AfterResetGeneratesTheSameSequence) {
+ this->AfterResetGeneratesTheSameSequence();
+}
+
+TYPED_TEST(RandomGeneratorTest, GeneratesDifferentNumbersIn8Bytes) {
+ this->GeneratesDifferentNumbersIn8Bytes();
+}
diff --git a/apps/wifi_offload/test/scanresult_test.cc b/apps/wifi_offload/test/scanresult_test.cc
new file mode 100644
index 0000000..c23f2cb
--- /dev/null
+++ b/apps/wifi_offload/test/scanresult_test.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "include/utility.h"
+
+TEST(ScanResultTest, ConstructOffloadScanResultBasedOnChreWifiScanResult) {
+ wifi_offload_test::RandomGenerator random_gen;
+ chreWifiScanResult chre_scan_result;
+ init(chre_scan_result, random_gen);
+ wifi_offload::ScanResult nanoapp_scan_result(chre_scan_result);
+
+ wifi_offload::Ssid ssid;
+ ssid.SetData(chre_scan_result.ssid, chre_scan_result.ssidLen);
+ ASSERT_EQ(ssid, nanoapp_scan_result.ssid_);
+ EXPECT_EQ(chre_scan_result.securityMode, nanoapp_scan_result.security_modes_);
+ EXPECT_EQ(0, std::memcmp(chre_scan_result.bssid, nanoapp_scan_result.bssid_,
+ CHRE_WIFI_BSSID_LEN));
+ EXPECT_EQ(chre_scan_result.capabilityInfo, nanoapp_scan_result.capability_);
+ EXPECT_EQ(chre_scan_result.primaryChannel,
+ nanoapp_scan_result.frequency_scanned_mhz_);
+ EXPECT_EQ(chre_scan_result.rssi, nanoapp_scan_result.rssi_dbm_);
+ EXPECT_EQ(0, nanoapp_scan_result.tsf_);
+}
diff --git a/apps/wifi_offload/test/utility.cc b/apps/wifi_offload/test/utility.cc
new file mode 100644
index 0000000..10a43ec
--- /dev/null
+++ b/apps/wifi_offload/test/utility.cc
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "include/utility.h"
+
+using RpcLog = wifi_offload::RpcLogRecord::RpcLogRecordType;
+
+namespace wifi_offload_test {
+
+void init(uint8_t &val, RandomGenerator &rand_gen) {
+ val = rand_gen.get<uint8_t>();
+}
+
+void init(uint16_t &val, RandomGenerator &rand_gen) {
+ val = rand_gen.get<uint16_t>();
+}
+
+void init(uint32_t &val, RandomGenerator &rand_gen) {
+ val = rand_gen.get<uint32_t>();
+}
+
+void init(uint64_t &val, RandomGenerator &rand_gen) {
+ val = rand_gen.get<uint64_t>();
+}
+
+void init(uint8_t *arr, size_t len, RandomGenerator &rand_gen) {
+ for (size_t i = 0; i < len; i++) {
+ arr[i] = rand_gen.get<uint8_t>();
+ }
+}
+
+void init_security_mode(uint8_t &sec_mode, RandomGenerator &rand_gen) {
+ init(sec_mode, rand_gen);
+ sec_mode &= 0x0f;
+}
+
+void init_rssi(int8_t &rssi, RandomGenerator &rand_gen) {
+ rssi = rand_gen.get<int8_t>();
+ if (rssi > 0) {
+ rssi = -rssi;
+ }
+}
+
+void init_rpc_log_record(RpcLog &log_record, RandomGenerator &rand_gen) {
+ log_record = static_cast<RpcLog>((rand_gen.get<uint32_t>() % 6u) +
+ static_cast<uint32_t>(RpcLog::CMD_BASE) + 1);
+}
+
+void init_frequency(uint32_t &freq, RandomGenerator &rand_gen) {
+ freq = kAllFrequencies_Test[rand_gen.get<uint8_t>() % kNumFrequencies_Test];
+}
+
+void init_capability(uint16_t &capa, RandomGenerator &rand_gen) {
+ capa = (rand_gen.get<uint16_t>() % 0xffff) + 1;
+}
+
+void init(wifi_offload::Ssid &ssid, RandomGenerator &rand_gen) {
+ uint8_t rand_ssid[wifi_offload::Ssid::kMaxSsidLen];
+ size_t len = (rand_gen.get<uint8_t>() % wifi_offload::Ssid::kMaxSsidLen) + 1;
+ init(rand_ssid, len, rand_gen);
+ ssid.SetData(rand_ssid, len);
+}
+
+void init(wifi_offload::PreferredNetwork &net_info, RandomGenerator &rand_gen) {
+ init(net_info.ssid_, rand_gen);
+ init_security_mode(net_info.security_modes_, rand_gen);
+}
+
+void init(wifi_offload::ScanRecord &record, RandomGenerator &rand_gen) {
+ init(record.time_spent_scanning_ms_, rand_gen);
+ init(record.num_channels_scanned_, rand_gen);
+ init(record.num_entries_aggregated_, rand_gen);
+}
+
+void init(wifi_offload::RpcLogRecord &record, RandomGenerator &rand_gen) {
+ init_rpc_log_record(record.record_type_, rand_gen);
+ init(record.timestamp_chre_ms_, rand_gen);
+}
+
+void init(wifi_offload::ChannelHistogram &histo, RandomGenerator &rand_gen) {
+ for (size_t i = 0; i < kNumFrequencies_Test; i++) {
+ histo.IncrementScanCountForFrequencyForTest(kAllFrequencies_Test[i],
+ rand_gen.get<uint16_t>());
+ }
+}
+
+void init(wifi_offload::ScanStats &stats, RandomGenerator &rand_gen) {
+ init(stats.num_scans_requested_by_nanoapp_, rand_gen);
+ init(stats.num_scans_serviced_by_hardware_, rand_gen);
+ init(stats.num_scans_serviced_by_cache_, rand_gen);
+ init(stats.updated_at_chre_ms_, rand_gen);
+ init(stats.sent_at_chre_ms_, rand_gen);
+ init(stats.last_subscription_duration_ms_, rand_gen);
+ init(stats.channel_histogram_, rand_gen);
+ init<wifi_offload::ScanRecord>(stats.scan_records_, rand_gen);
+ init<wifi_offload::RpcLogRecord>(stats.rpc_log_records_, rand_gen);
+}
+
+void init(wifi_offload::ScanParams ¶ms, RandomGenerator &rand_gen) {
+ init<wifi_offload::Ssid>(params.ssids_to_scan_, rand_gen);
+ // Need to init frequency vector with valid frequency values
+ // init<uint32_t>(params.frequencies_to_scan_mhz_, rand_gen);
+ params.frequencies_to_scan_mhz_.clear();
+ size_t vec_len = (rand_gen.get<uint8_t>() % 10) + 1;
+ for (size_t i = 0; i < vec_len; i++) {
+ uint32_t new_freq;
+ init_frequency(new_freq, rand_gen);
+ params.frequencies_to_scan_mhz_.push_back(new_freq);
+ }
+ init(params.disconnected_mode_scan_interval_ms_, rand_gen);
+}
+
+void init(wifi_offload::ScanFilter &filter, RandomGenerator &rand_gen) {
+ init<wifi_offload::PreferredNetwork>(filter.networks_to_match_, rand_gen);
+ init_rssi(filter.min_rssi_threshold_dbm_, rand_gen);
+}
+
+void init(wifi_offload::ScanConfig &config, RandomGenerator &rand_gen) {
+ init(config.scan_params_, rand_gen);
+ init(config.scan_filter_, rand_gen);
+}
+
+void init(wifi_offload::ScanResult &result, RandomGenerator &rand_gen) {
+ init(result.ssid_, rand_gen);
+ init_security_mode(result.security_modes_, rand_gen);
+ init(result.bssid_, wifi_offload::ScanResult::kBssidSize, rand_gen);
+ init_capability(result.capability_, rand_gen);
+ init_frequency(result.frequency_scanned_mhz_, rand_gen);
+ init_rssi(result.rssi_dbm_, rand_gen);
+ init(result.tsf_, rand_gen);
+}
+
+void init(chreWifiScanResult &result, RandomGenerator &rand_gen) {
+ init(result.ageMs, rand_gen);
+ init_capability(result.capabilityInfo, rand_gen);
+ result.ssidLen =
+ (rand_gen.get<uint8_t>() % wifi_offload::Ssid::kMaxSsidLen) + 1;
+ init(result.ssid, result.ssidLen, rand_gen);
+ init(result.bssid, CHRE_WIFI_BSSID_LEN, rand_gen);
+ init(result.flags, rand_gen);
+ init_rssi(result.rssi, rand_gen);
+ init(result.band, rand_gen);
+ init_frequency(result.primaryChannel, rand_gen);
+ init(result.centerFreqPrimary, rand_gen);
+ init(result.centerFreqSecondary, rand_gen);
+ result.channelWidth = CHRE_WIFI_CHANNEL_WIDTH_20_MHZ;
+ init_security_mode(result.securityMode, rand_gen);
+}
+
+} // wifioffloadtesthelper namespace
diff --git a/apps/wifi_offload/test/wifioffloadutility_test.cc b/apps/wifi_offload/test/wifioffloadutility_test.cc
new file mode 100644
index 0000000..58af31f
--- /dev/null
+++ b/apps/wifi_offload/test/wifioffloadutility_test.cc
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include "chre/apps/wifi_offload/utility.h"
+#include "include/utility.h"
+
+TEST(UtilityTest, MapAllSupportedFrequenciesToAllchannel_numbers) {
+ for (size_t i = 0; i < wifi_offload_test::kNumFrequencies_Test; i++) {
+ int channel = wifi_offload::utility::Ieee80211FrequencyToChannel(
+ static_cast<int>(wifi_offload_test::kAllFrequencies_Test[i]));
+ EXPECT_EQ(wifi_offload_test::kAllChannels_Test[i],
+ static_cast<uint8_t>(channel));
+ }
+}
diff --git a/apps/wifi_offload/utility.cc b/apps/wifi_offload/utility.cc
new file mode 100644
index 0000000..617c10f
--- /dev/null
+++ b/apps/wifi_offload/utility.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <cctype>
+
+#include "chre/apps/wifi_offload/utility.h"
+#include "chre/apps/wifi_offload/wifi_offload.h"
+
+namespace wifi_offload {
+namespace utility {
+namespace {
+
+// The length of a string SSID with null-terminator.
+constexpr size_t kMaxSsidStrLen = CHRE_WIFI_SSID_MAX_LEN + 1;
+// The length of a formatted BSSID string in XX:XX:XX:XX:XX:XX\0 format.
+constexpr size_t kBssidStrLen = 18;
+
+bool ParseSsidToStr(const uint8_t *ssid, size_t ssid_len, char *ssid_str,
+ size_t ssid_str_len) {
+ if (ssid_str_len < ssid_len + 1) {
+ return false;
+ }
+ // Verify that the ssid is entirely printable characters and ASCII spaces.
+ for (uint8_t i = 0; i < ssid_len; i++) {
+ if (!std::isgraph(ssid[i]) && ssid[i] != ' ') {
+ return false;
+ }
+ }
+
+ std::memcpy(ssid_str, ssid, ssid_len);
+ ssid_str[ssid_len] = '\0';
+ return true;
+}
+
+bool ParseBssidToStr(const uint8_t bssid[CHRE_WIFI_BSSID_LEN], char *bssid_str,
+ size_t bssid_str_len) {
+ if (bssid_str_len < kBssidStrLen) {
+ return false;
+ }
+
+ const char *kFormat = "%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8 ":%02" PRIx8
+ ":%02" PRIx8 ":%02" PRIx8;
+ std::snprintf(bssid_str, bssid_str_len, kFormat, bssid[0], bssid[1], bssid[2],
+ bssid[3], bssid[4], bssid[5]);
+ return true;
+}
+
+const char *ParseChreWifiBand(uint8_t band) {
+ switch (band) {
+ case CHRE_WIFI_BAND_2_4_GHZ:
+ return "2.4GHz";
+ case CHRE_WIFI_BAND_5_GHZ:
+ return "5GHz";
+ default:
+ return "<invalid>";
+ }
+}
+
+} // namespace
+
+int Ieee80211FrequencyToChannel(int freq) {
+ /* see 802.11-2007 17.3.8.3.2 and Annex J */
+ if (freq == 2484)
+ return 14;
+ else if (freq < 2484)
+ return (freq - 2407) / 5;
+ else if (freq >= 4910 && freq <= 4980)
+ return (freq - 4000) / 5;
+ else if (freq <= 45000) /* DMG band lower limit */
+ return (freq - 5000) / 5;
+ else if (freq >= 58320 && freq <= 64800)
+ return (freq - 56160) / 2160;
+ else
+ return 0;
+}
+
+void LogSsid(const uint8_t *ssid, uint8_t ssid_len) {
+ const char *ssid_str = "<non-printable>";
+ char ssid_buffer[kMaxSsidStrLen];
+ if (ssid_len == 0) {
+ ssid_str = "<empty>";
+ } else if (ParseSsidToStr(ssid, ssid_len, ssid_buffer, kMaxSsidStrLen)) {
+ ssid_str = ssid_buffer;
+ } else {
+ // ssid has non-printable ASCII chars, parse in hex format
+ char ssid_hex_buffer[CHRE_WIFI_SSID_MAX_LEN * 3];
+ char *buf_ptr = ssid_hex_buffer;
+ for (size_t i = 0; i < ssid_len; i++) {
+ buf_ptr += std::sprintf(buf_ptr, "%02" PRIx8 ":", ssid[i]);
+ }
+ buf_ptr[-1] = '\0';
+ ssid_str = ssid_hex_buffer;
+ }
+ LOGI(" ssid: %s", ssid_str);
+}
+
+void LogBssid(const uint8_t *bssid) {
+ const char *bssid_str = "<non-printable>";
+ char bssidBuffer[kBssidStrLen];
+ if (ParseBssidToStr(bssid, bssidBuffer, kBssidStrLen)) {
+ bssid_str = bssidBuffer;
+ }
+ LOGI(" bssid: %s", bssid_str);
+}
+
+void LogChreScanResult(const chreWifiScanResult &result) {
+ LOGI("chreWifiScanResult:");
+ LogSsid(result.ssid, result.ssidLen);
+ LOGI(" age (ms): %" PRIu32, result.ageMs);
+ LOGI(" capability info: 0x%" PRIx16, result.capabilityInfo);
+ LogBssid(result.bssid);
+ LOGI(" flags: 0x%" PRIx8, result.flags);
+ LOGI(" rssi: %" PRId8 "dBm", result.rssi);
+ LOGI(" band: %s (%" PRIu8 ")", ParseChreWifiBand(result.band), result.band);
+ LOGI(" primary channel: %" PRIu32, result.primaryChannel);
+ LOGI(" center frequency primary: %" PRIu32, result.centerFreqPrimary);
+ LOGI(" center frequency secondary: %" PRIu32, result.centerFreqSecondary);
+ LOGI(" channel width: %" PRIu8, result.channelWidth);
+ LOGI(" security mode: %" PRIu8, result.securityMode);
+}
+
+const char *GetErrorCodeName(ErrorCode error_code) {
+ switch (error_code) {
+ case SUCCESS:
+ return "SUCCESS";
+ case FAILED_TO_ALLOCATE_MESSAGE_BUFFER:
+ return "FAILED_TO_ALLOCATE_MESSAGE_BUFFER";
+ case FAILED_TO_SERIALIZE_MESSAGE:
+ return "FAILED_TO_SERIALIZE_MESSAGE";
+ case FAILED_TO_SEND_MESSAGE:
+ return "FAILED_TO_SEND_MESSAGE";
+ case FAILED_TO_DESERIALIZE_SCAN_CONFIG:
+ return "FAILED_TO_DESERIALIZE_SCAN_CONFIG";
+ case INVALID_SUBSCRIBE_MESSAGE_SIZE:
+ return "INVALID_SUBSCRIBE_MESSAGE_SIZE";
+ case SCAN_CONFIG_NOT_INITIALIZED:
+ return "SCAN_CONFIG_NOT_INITIALIZED";
+ case UNSPECIFIED_HOST_ENDPOINT:
+ return "UNSPECIFIED_HOST_ENDPOINT";
+ case FAILED_TO_SEND_SCAN_RESULTS:
+ return "FAILED_TO_SEND_SCAN_RESULTS";
+ case FAILED_TO_SEND_SCAN_STATS:
+ return "FAILED_TO_SEND_SCAN_STATS";
+ case SCAN_MONITORING_NOT_SUPPORTED:
+ return "SCAN_MONITORING_NOT_SUPPORTED";
+ case FAILED_TO_START_SCAN_MONITORING:
+ return "FAILED_TO_START_SCAN_MONITORING";
+ case FAILED_TO_STOP_SCAN_MONITORING:
+ return "FAILED_TO_STOP_SCAN_MONITORING";
+ case FAILED_TO_CONFIGURE_SCAN_MONITORING_ASYNC:
+ return "FAILED_TO_CONFIGURE_SCAN_MONITORING_ASYNC";
+ case ONDEMAND_SCAN_NOT_SUPPORTED:
+ return "ONDEMAND_SCAN_NOT_SUPPORTED";
+ case FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST:
+ return "FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST";
+ case FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST_ASYNC:
+ return "FAILED_TO_SEND_ONDEMAND_SCAN_REQUEST_ASYNC";
+ case OUT_OF_ORDER_SCAN_RESULTS:
+ return "OUT_OF_ORDER_SCAN_RESULTS";
+ case INCOMPLETE_SCAN_RESULTS_BEFORE_SCAN_REQUEST:
+ return "INCOMPLETE_SCAN_RESULTS_BEFORE_SCAN_REQUEST";
+ case FAILED_TO_SET_SCAN_TIMER:
+ return "FAILED_TO_SET_SCAN_TIMER";
+ default:
+ return "UNKNOWN_ERROR";
+ }
+}
+
+} // namespace utility
+} // namespace wifi_offload
diff --git a/apps/wifi_offload/wifi_offload.mk b/apps/wifi_offload/wifi_offload.mk
new file mode 100644
index 0000000..0450680
--- /dev/null
+++ b/apps/wifi_offload/wifi_offload.mk
@@ -0,0 +1,40 @@
+#
+# Wifi Offload Makefile
+#
+
+WIFI_OFFLOAD_TYPES_PREFIX = $(CHRE_PREFIX)/apps/wifi_offload
+FLAT_BUFFERS_PREFIX = $(CHRE_PREFIX)/external/flatbuffers
+
+# Common Compiler Flags ########################################################
+
+# Include paths.
+COMMON_CFLAGS += -I$(FLAT_BUFFERS_PREFIX)/include
+
+# Common Source Files ##########################################################
+
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/channel_histogram.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/chre_scan_params_safe.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/flatbuffers_serialization.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/preferred_network.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/rpc_log_record.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_config.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_filter.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_params.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_record.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_result.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_result_message.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/scan_stats.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/ssid.cc
+COMMON_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/utility.cc
+
+# GoogleTest Source Files ######################################################
+
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/channelhistogram_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/chrescanparamssafe_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/flatbuffersserialization_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/offloadtypes_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/random_generator.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/randomgenerator_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/scanresult_test.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/utility.cc
+GOOGLETEST_SRCS += $(WIFI_OFFLOAD_TYPES_PREFIX)/test/wifioffloadutility_test.cc
diff --git a/ash/platform/slpi/ash.cc b/ash/platform/slpi/ash.cc
index 613da1d..c07b5ba 100644
--- a/ash/platform/slpi/ash.cc
+++ b/ash/platform/slpi/ash.cc
@@ -30,15 +30,16 @@
#include "chre/platform/slpi/smgr_client.h"
#include "chre_api/chre/sensor.h"
-using chre::getSensorServiceQmiClientHandle;
+using chre::getSmrHelper;
+using chre::getSensorServiceSmrClientHandle;
+using chre::MakeUnique;
+using chre::MakeUniqueZeroFill;
using chre::memoryAlloc;
using chre::memoryFree;
+using chre::UniquePtr;
namespace {
-//! The timeout for QMI messages in milliseconds.
-constexpr uint32_t kQmiTimeoutMs = 1000;
-
//! The constant to convert magnetometer readings from uT in Android to Gauss
//! in SMGR.
constexpr float kGaussPerMicroTesla = 0.01f;
@@ -135,19 +136,18 @@
LOGE("Attempting to set calibration of sensor %" PRIu8, sensorType);
} else {
// Allocate request and response for sensor calibraton.
- auto *calRequest = memoryAlloc<sns_smgr_sensor_cal_req_msg_v01>();
- auto *calResponse = memoryAlloc<sns_smgr_sensor_cal_resp_msg_v01>();
- if (calRequest == nullptr || calResponse == nullptr) {
+ auto calRequest = MakeUniqueZeroFill<sns_smgr_sensor_cal_req_msg_v01>();
+ auto calResponse = MakeUnique<sns_smgr_sensor_cal_resp_msg_v01>();
+ if (calRequest.isNull() || calResponse.isNull()) {
LOGE("Failed to allocated sensor cal memory");
} else {
- populateCalRequest(sensorType, calInfo, calRequest);
+ populateCalRequest(sensorType, calInfo, calRequest.get());
- qmi_client_error_type status = qmi_client_send_msg_sync(
- getSensorServiceQmiClientHandle(), SNS_SMGR_CAL_REQ_V01,
- calRequest, sizeof(*calRequest), calResponse, sizeof(*calResponse),
- kQmiTimeoutMs);
+ smr_err status = getSmrHelper()->sendReqSync(
+ getSensorServiceSmrClientHandle(), SNS_SMGR_CAL_REQ_V01,
+ &calRequest, &calResponse);
- if (status != QMI_NO_ERR) {
+ if (status != SMR_NO_ERR) {
LOGE("Error setting sensor calibration: status %d", status);
} else if (calResponse->Resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
LOGE("Setting sensor calibration failed with error: %" PRIu8,
@@ -156,8 +156,6 @@
success = true;
}
}
- memoryFree(calRequest);
- memoryFree(calResponse);
}
return success;
}
diff --git a/build/app_support/qcom_nanohub/app_support_uimg.cc b/build/app_support/qcom_nanohub/app_support_uimg.cc
new file mode 100755
index 0000000..483dcb1
--- /dev/null
+++ b/build/app_support/qcom_nanohub/app_support_uimg.cc
@@ -0,0 +1 @@
+#error "This file is not implemented yet"
diff --git a/build/arch/hexagon.mk b/build/arch/hexagon.mk
index 87ca25c..f63c363 100644
--- a/build/arch/hexagon.mk
+++ b/build/arch/hexagon.mk
@@ -44,6 +44,10 @@
# that any Qualcomm code needs it.
TARGET_CFLAGS += -D__V_DYNAMIC__
+# This flag is used by some QC-supplied code to differentiate things intended to
+# run on Hexagon vs. other architectures
+TARGET_CFLAGS += -DQDSP6
+
# Hexagon Shared Object Linker Flags ###########################################
TARGET_SO_LDFLAGS += --gc-sections
diff --git a/build/common.mk b/build/common.mk
index 8f99dc5..58b2fec 100644
--- a/build/common.mk
+++ b/build/common.mk
@@ -28,6 +28,10 @@
MAKECMDGOALS = all
endif
+# Variant-specific Support Source Files ########################################
+
+SYS_SUPPORT_PATH = $(CHRE_PREFIX)/build/sys_support
+
# Makefile Includes ############################################################
# Common Includes
diff --git a/build/nanoapp/app.mk b/build/nanoapp/app.mk
index b2122f5..38cb8b3 100644
--- a/build/nanoapp/app.mk
+++ b/build/nanoapp/app.mk
@@ -62,6 +62,23 @@
# Add the CHRE API to the include search path.
COMMON_CFLAGS += -I$(CHRE_PREFIX)/chre_api/include/chre_api
+# Add util and platform/shared to the include search path.
+COMMON_CFLAGS += -I$(CHRE_PREFIX)/util/include
+COMMON_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include
+
+# Allows a nanoapp to know that is compiled separately from the CHRE system.
+COMMON_CFLAGS += -DCHRE_IS_NANOAPP_BUILD
+
+# Compile FlatBuffers in a portable way.
+COMMON_CFLAGS += -DFLATBUFFERS_CHRE
+
+# Nanoapp configuration flags.
+COMMON_CFLAGS += -DNANOAPP_ID=$(NANOAPP_ID)
+COMMON_CFLAGS += -DNANOAPP_VERSION=$(NANOAPP_VERSION)
+COMMON_CFLAGS += -DNANOAPP_VENDOR_STRING=$(NANOAPP_VENDOR_STRING)
+COMMON_CFLAGS += -DNANOAPP_NAME_STRING=$(NANOAPP_NAME_STRING)
+COMMON_CFLAGS += -DNANOAPP_IS_SYSTEM_NANOAPP=$(NANOAPP_IS_SYSTEM_NANOAPP)
+
# Variant-specific Nanoapp Support Source Files ################################
APP_SUPPORT_PATH = $(CHRE_PREFIX)/build/app_support
@@ -69,7 +86,10 @@
GOOGLE_HEXAGONV60_SLPI_SRCS += $(DSO_SUPPORT_LIB_PATH)/nanoapp_support_lib_dso.c
GOOGLE_HEXAGONV62_SLPI_SRCS += $(DSO_SUPPORT_LIB_PATH)/nanoapp_support_lib_dso.c
+GOOGLE_HEXAGONV62_SLPI-UIMG_SRCS += $(DSO_SUPPORT_LIB_PATH)/nanoapp_support_lib_dso.c
+GOOGLE_X86_LINUX_SRCS += $(DSO_SUPPORT_LIB_PATH)/nanoapp_support_lib_dso.c
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
# Makefile Includes ############################################################
@@ -80,5 +100,7 @@
include $(CHRE_PREFIX)/build/variant/google_cm4_nanohub.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv60_slpi.mk
include $(CHRE_PREFIX)/build/variant/google_hexagonv62_slpi.mk
+include $(CHRE_PREFIX)/build/variant/google_hexagonv62_slpi-uimg.mk
include $(CHRE_PREFIX)/build/variant/google_x86_linux.mk
include $(CHRE_PREFIX)/build/variant/qcom_hexagonv60_nanohub.mk
+include $(CHRE_PREFIX)/build/variant/qcom_hexagonv60_nanohub-uimg.mk
diff --git a/build/nanoapp/google_slpi.mk b/build/nanoapp/google_slpi.mk
index 2e629bb..dfcb317 100644
--- a/build/nanoapp/google_slpi.mk
+++ b/build/nanoapp/google_slpi.mk
@@ -15,14 +15,6 @@
#
################################################################################
-TARGET_CFLAGS += -DNANOAPP_ID=$(NANOAPP_ID)
-TARGET_CFLAGS += -DNANOAPP_VERSION=$(NANOAPP_VERSION)
-TARGET_CFLAGS += -DNANOAPP_VENDOR_STRING=$(NANOAPP_VENDOR_STRING)
-TARGET_CFLAGS += -DNANOAPP_NAME_STRING=$(NANOAPP_NAME_STRING)
-TARGET_CFLAGS += -DNANOAPP_IS_SYSTEM_NANOAPP=$(NANOAPP_IS_SYSTEM_NANOAPP)
-TARGET_CFLAGS += -I$(CHRE_PREFIX)/platform/shared/include
-TARGET_CFLAGS += -I$(CHRE_PREFIX)/util/include
-
ifndef GOOGLE_SLPI_NANOAPP_BUILD_TEMPLATE
define GOOGLE_SLPI_NANOAPP_BUILD_TEMPLATE
diff --git a/build/sys_support/qcom/uimage.lcs.toolv80 b/build/sys_support/qcom/uimage.lcs.toolv80
new file mode 100644
index 0000000..af42a34
--- /dev/null
+++ b/build/sys_support/qcom/uimage.lcs.toolv80
@@ -0,0 +1,205 @@
+/*
+Copyright (c) 2017, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+OUTPUT_FORMAT("elf32-littlehexagon", "elf32-bighexagon",
+ "elf32-littlehexagon")
+OUTPUT_ARCH(hexagon)
+
+PHDRS {
+phdr1 PT_LOAD;
+phdr2 PT_LOAD;
+dynamic1 PT_DYNAMIC;
+note1 PT_NOTE;
+}
+
+ENTRY(start)
+SECTIONS
+{
+ .interp : { *(.interp) }
+ .note.qti.uimg.dl.ver : { *(.note.qti.uimg.dl.ver) } : phdr1 : note1
+ .dynsym : { *(.dynsym) } : phdr1
+ .dynstr : { *(.dynstr) }
+ .hash : { *(.hash) }
+ .rela.dyn :
+ {
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.sdata .rela.lit[a48] .rela.sdata.* .rela.lit[a48].* .rela.gnu.linkonce.s.* .rela.gnu.linkonce.l[a48].*)
+ *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*)
+ *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*)
+ *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rela.plt :
+ {
+ *(.rela.plt)
+ } : phdr1
+ . = ALIGN(64);
+ /* Code starts. */
+ .start :
+ {
+ KEEP (*(.start))
+ } =0x00c0007f
+ . = ALIGN(64);
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x00c0007f
+ .plt : { *(.plt) }
+ . = ALIGN (64);
+ .text :
+ {
+ *(.text.unlikely .text.*_unlikely)
+ *(.text.hot .text.hot.* .gnu.linkonce.t.hot.*)
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ } =0x00c0007f
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x00c0007f
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ . = ALIGN(64);
+ /* Constants start. */
+ .rodata :
+ {
+ *(.rodata.hot .rodata.hot.* .gnu.linkonce.r.hot.*)
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ }
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : { KEEP (*(.eh_frame)) }
+ .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
+ _DYNAMIC = .;
+ .dynamic : { *(.dynamic) } : phdr1 : dynamic1
+ .got : { *(.got) *(.igot) } : phdr1
+ .got.plt : { *(.got.plt) *(.igot.plt) }
+ . = ALIGN(64);
+ .ctors :
+ {
+ KEEP (*crtbegin.o(.ctors))
+ KEEP (*crtbegin?.o(.ctors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o fini.o) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*crtbegin?.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o fini.o) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ /*. = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+ . = ALIGN (DEFINED (DATAALIGN) ? (DATAALIGN * 1K) : CONSTANT (MAXPAGESIZE));*/
+ . = DATA_SEGMENT_RELRO_END (16, .);
+ . = ALIGN (4K);
+ .data :
+ {
+ *(.data.hot .data.hot.* .gnu.linkonce.d.hot.*)
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ } : phdr2
+ _edata = .; PROVIDE (edata = .);
+ . = ALIGN (64);
+ /* Small data start. */
+ . = ALIGN(64);
+ .sdata :
+ {
+ PROVIDE (_SDA_BASE_ = .);
+ *(.sdata.1 .sdata.1.* .gnu.linkonce.s.1.*)
+ *(.sbss.1 .sbss.1.* .gnu.linkonce.sb.1.*)
+ *(.scommon.1 .scommon.1.*)
+ *(.sdata.2 .sdata.2.* .gnu.linkonce.s.2.*)
+ *(.sbss.2 .sbss.2.* .gnu.linkonce.sb.2.*)
+ *(.scommon.2 .scommon.2.*)
+ *(.sdata.4 .sdata.4.* .gnu.linkonce.s.4.*)
+ *(.sbss.4 .sbss.4.* .gnu.linkonce.sb.4.*)
+ *(.scommon.4 .scommon.4.*)
+ *(.lit[a4] .lit[a4].* .gnu.linkonce.l[a4].*)
+ *(.sdata.8 .sdata.8.* .gnu.linkonce.s.8.*)
+ *(.sbss.8 .sbss.8.* .gnu.linkonce.sb.8.*)
+ *(.scommon.8 .scommon.8.*)
+ *(.lit8 .lit8.* .gnu.linkonce.l8.*)
+ *(.sdata.hot .sdata.hot.* .gnu.linkonce.s.hot.*)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ }
+ .sbss :
+ {
+ PROVIDE (__sbss_start = .);
+ PROVIDE (___sbss_start = .);
+ *(.dynsbss)
+ *(.sbss.hot .sbss.hot.* .gnu.linkonce.sb.hot.*)
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon .scommon.*)
+ . = ALIGN (. != 0 ? 64 : 1);
+ PROVIDE (__sbss_end = .);
+ PROVIDE (___sbss_end = .);
+ }
+ . = ALIGN (64);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss.hot .bss.hot.* .gnu.linkonce.b.hot.*)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ }
+ . = ALIGN (64);
+ _end = .;
+ PROVIDE (end = .);
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ /* DWARF 3 */
+ .debug_pubtypes 0 : { *(.debug_pubtypes) }
+ .debug_ranges 0 : { *(.debug_ranges) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) }
+}
diff --git a/build/sys_support/qcom/uimg_dl_ver.c b/build/sys_support/qcom/uimg_dl_ver.c
new file mode 100644
index 0000000..3f1ed35
--- /dev/null
+++ b/build/sys_support/qcom/uimg_dl_ver.c
@@ -0,0 +1,69 @@
+/*
+Copyright (c) 2017, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UIMG_DL_VER_MAJOR
+#define UIMG_DL_VER_MAJOR 1
+#endif
+#ifndef UIMG_DL_VER_MINOR
+#define UIMG_DL_VER_MINOR 0
+#endif
+#ifndef UIMG_DL_VER_MAINT
+#define UIMG_DL_VER_MAINT 0
+#endif
+
+#define __TOSTR(_x) #_x
+#define _TOSTR(_x) __TOSTR(_x)
+
+typedef struct note_type{
+ int sizename;
+ int sizedesc;
+ int type;
+ char name[24];
+ int desc[3];
+} note_type;
+
+#ifdef __llvm__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-variable"
+#endif
+
+const note_type uimg_dl_ver __attribute__ ((section (".note.qti.uimg.dl.ver")))
+ __attribute__ ((visibility ("default"))) = {
+ 24,
+ 12,
+ 0,
+ "uimg.dl.ver." _TOSTR(UIMG_DL_VER_MAJOR) "." _TOSTR(UIMG_DL_VER_MINOR) "." _TOSTR(UIMG_DL_VER_MAINT),
+ {UIMG_DL_VER_MAJOR, UIMG_DL_VER_MINOR, UIMG_DL_VER_MAINT}
+};
+
+#ifdef __llvm__
+#pragma clang diagnostic pop
+#endif
+
+
diff --git a/build/tools_config.mk b/build/tools_config.mk
index 04c56d8..89a67a1 100644
--- a/build/tools_config.mk
+++ b/build/tools_config.mk
@@ -32,7 +32,7 @@
COMMON_DEBUG_CFLAGS += -g
# Dependency Resolution
-DEP_CFLAGS = -MT $$@ -MM -MG -MP -MF $$(basename $$@).Td
+DEP_CFLAGS = -MM -MG -MP -MF $$(basename $$@).Td
DEP_POST_COMPILE = @mv -f $$(basename $$@).Td $$(basename $$@).d && touch $$@
# Compile with hidden visibility by default.
diff --git a/build/variant/google_hexagonv62_slpi-uimg.mk b/build/variant/google_hexagonv62_slpi-uimg.mk
new file mode 100644
index 0000000..2b09a59
--- /dev/null
+++ b/build/variant/google_hexagonv62_slpi-uimg.mk
@@ -0,0 +1,31 @@
+#
+# Google CHRE Reference Implementation for Hexagon v62 Architecture on SLPI
+#
+
+include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+
+TARGET_NAME = google_hexagonv62_slpi-uimg
+# 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 += -mno-pic-data-is-text-relative
+TARGET_CFLAGS += -DCHRE_SLPI_UIMG_ENABLED
+TARGET_CFLAGS += $(GOOGLE_HEXAGONV62_SLPI-UIMG_CFLAGS)
+TARGET_VARIANT_SRCS = $(GOOGLE_HEXAGONV62_SLPI-UIMG_SRCS)
+TARGET_SO_LATE_LIBS = $(GOOGLE_HEXAGONV62_SLPI-UIMG_LATE_LIBS)
+HEXAGON_ARCH = v62
+
+# Enable uImage support.
+TARGET_VARIANT_SRCS += $(SYS_SUPPORT_PATH)/qcom/uimg_dl_ver.c
+TARGET_SO_LDFLAGS += --script=$(SYS_SUPPORT_PATH)/qcom/uimage.lcs.toolv80
+
+ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
+ifneq ($(IS_NANOAPP_BUILD),)
+TARGET_SO_LATE_LIBS += $(CHRE_PREFIX)/build/app_support/google_slpi/libchre_slpi_skel.so
+include $(CHRE_PREFIX)/build/nanoapp/google_slpi.mk
+endif
+
+include $(CHRE_PREFIX)/build/arch/hexagon.mk
+include $(CHRE_PREFIX)/build/build_template.mk
+endif
diff --git a/build/variant/google_x86_googletest.mk b/build/variant/google_x86_googletest.mk
index 24310e6..f32da60 100644
--- a/build/variant/google_x86_googletest.mk
+++ b/build/variant/google_x86_googletest.mk
@@ -35,7 +35,7 @@
TARGET_BUILD_BIN = true
# Link in libraries for the final executable.
-TARGET_BIN_LDFLAGS += -lrt
+TARGET_BIN_LDFLAGS += -lrt -ldl
TARGET_BIN_LDFLAGS += -lpthread
include $(CHRE_PREFIX)/build/build_template.mk
diff --git a/build/variant/google_x86_linux.mk b/build/variant/google_x86_linux.mk
index bf5a484..4ab5d88 100644
--- a/build/variant/google_x86_linux.mk
+++ b/build/variant/google_x86_linux.mk
@@ -26,8 +26,9 @@
# Instruct the build to link a final executable.
TARGET_BUILD_BIN = true
-# Link in libraries for the final executable.
-TARGET_BIN_LDFLAGS += -lrt
+# Link in libraries for the final executable and export symbols to dynamically
+# loaded objects.
+TARGET_BIN_LDFLAGS += -lrt -ldl -Wl,--export-dynamic
endif
include $(CHRE_PREFIX)/build/arch/x86.mk
diff --git a/build/variant/qcom_hexagonv60_nanohub-uimg.mk b/build/variant/qcom_hexagonv60_nanohub-uimg.mk
new file mode 100644
index 0000000..f57e26a
--- /dev/null
+++ b/build/variant/qcom_hexagonv60_nanohub-uimg.mk
@@ -0,0 +1,28 @@
+#
+# Qualcomm CHRE Implementation for Hexagon v60, based on Nanohub
+#
+
+include $(CHRE_PREFIX)/build/clean_build_template_args.mk
+
+TARGET_NAME = qcom_hexagonv60_nanohub-uimg
+TARGET_CFLAGS = -DCHRE_MESSAGE_TO_HOST_MAX_SIZE=4080
+TARGET_CFLAGS += -mno-pic-data-is-text-relative
+TARGET_CFLAGS += $(QCOM_HEXAGONV60_NANOHUB-UIMG_CFLAGS)
+TARGET_VARIANT_SRCS = $(QCOM_HEXAGONV60_NANOHUB-UIMG_SRCS)
+TARGET_SO_LATE_LIBS = $(QCOM_HEXAGONV60_NANOHUB-UIMG_LATE_LIBS)
+HEXAGON_ARCH = v60
+
+# Enable uImage support.
+TARGET_VARIANT_SRCS += $(SYS_SUPPORT_PATH)/qcom/uimg_dl_ver.c
+TARGET_SO_LDFLAGS += --script=$(SYS_SUPPORT_PATH)/qcom/uimage.lcs.toolv80
+
+ifneq ($(filter $(TARGET_NAME)% all, $(MAKECMDGOALS)),)
+ifneq ($(IS_NANOAPP_BUILD),)
+TARGET_SO_LATE_LIBS += $(CHRE_PREFIX)/build/app_support/qcom_nanohub/chre.so
+TARGET_SO_LATE_LIBS += $(CHRE_PREFIX)/build/app_support/qcom_nanohub/chre_platform.so
+include $(CHRE_PREFIX)/build/nanoapp/qcom_nanohub.mk
+endif
+
+include $(CHRE_PREFIX)/build/arch/hexagon.mk
+include $(CHRE_PREFIX)/build/build_template.mk
+endif
diff --git a/chre_api/include/chre_api/chre/gnss.h b/chre_api/include/chre_api/chre/gnss.h
index b0c2e37..b61cc5d 100644
--- a/chre_api/include/chre_api/chre/gnss.h
+++ b/chre_api/include/chre_api/chre/gnss.h
@@ -72,7 +72,7 @@
* an event with the result of an asynchronous request, unless specified
* otherwise
*/
-#define CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC_U64)
+#define CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS (5 * CHRE_NSEC_PER_SEC)
/**
* Produce an event ID in the block of IDs reserved for GNSS
diff --git a/core/core.mk b/core/core.mk
index 36cbec8..4f97164 100644
--- a/core/core.mk
+++ b/core/core.mk
@@ -16,7 +16,6 @@
COMMON_SRCS += core/gnss_request_manager.cc
COMMON_SRCS += core/host_comms_manager.cc
COMMON_SRCS += core/init.cc
-COMMON_SRCS += core/memory_manager.cc
COMMON_SRCS += core/nanoapp.cc
COMMON_SRCS += core/sensor.cc
COMMON_SRCS += core/sensor_request.cc
diff --git a/core/event_loop.cc b/core/event_loop.cc
index 5ab7739..4ee27ee 100644
--- a/core/event_loop.cc
+++ b/core/event_loop.cc
@@ -109,11 +109,17 @@
// event is delivered to all interested Nanoapps, its free callback is
// invoked.
if (!havePendingEvents || !mEvents.empty()) {
+ if (mEvents.size() > mMaxEventPoolUsage) {
+ mMaxEventPoolUsage = mEvents.size();
+ }
+
// mEvents.pop() will be a blocking call if mEvents.empty()
distributeEvent(mEvents.pop());
}
havePendingEvents = deliverEvents();
+
+ mPowerControlManager.postEventLoopProcess(mEvents.size());
}
// Deliver any events sitting in Nanoapps' own queues (we could drop them to
@@ -291,6 +297,12 @@
for (const UniquePtr<Nanoapp>& app : mNanoapps) {
success &= app->logStateToBuffer(buffer, bufferPos, bufferSize);
}
+
+ success &= debugDumpPrint(buffer, bufferPos, bufferSize,
+ "\nEvent Loop:\n");
+ success &= debugDumpPrint(buffer, bufferPos, bufferSize,
+ " Max event pool usage: %zu/%zu\n",
+ mMaxEventPoolUsage, kMaxEventCount);
return success;
}
diff --git a/core/event_loop_manager.cc b/core/event_loop_manager.cc
index f0a845f..50f16e2 100644
--- a/core/event_loop_manager.cc
+++ b/core/event_loop_manager.cc
@@ -91,4 +91,7 @@
mWwanRequestManager.init();
}
+// Explicitly instantiate the EventLoopManagerSingleton to reduce codesize.
+template class Singleton<EventLoopManager>;
+
} // namespace chre
diff --git a/core/include/chre/core/event_loop.h b/core/include/chre/core/event_loop.h
index 9f549a0..3afee6b 100644
--- a/core/include/chre/core/event_loop.h
+++ b/core/include/chre/core/event_loop.h
@@ -22,6 +22,7 @@
#include "chre/core/timer_pool.h"
#include "chre/platform/mutex.h"
#include "chre/platform/platform_nanoapp.h"
+#include "chre/platform/power_control_manager.h"
#include "chre/util/dynamic_vector.h"
#include "chre/util/fixed_size_blocking_queue.h"
#include "chre/util/non_copyable.h"
@@ -231,13 +232,22 @@
bool logStateToBuffer(char *buffer, size_t *bufferPos,
size_t bufferSize) const;
+
+ /**
+ * Returns a reference to the power control manager. This allows power
+ * controls from subsystems outside the event loops.
+ */
+ PowerControlManager& getPowerControlManager() {
+ return mPowerControlManager;
+ }
+
private:
//! The maximum number of events that can be active in the system.
- static constexpr size_t kMaxEventCount = 256;
+ static constexpr size_t kMaxEventCount = 96;
//! The maximum number of events that are awaiting to be scheduled. These
//! events are in a queue to be distributed to apps.
- static constexpr size_t kMaxUnscheduledEventCount = 256;
+ static constexpr size_t kMaxUnscheduledEventCount = 96;
//! The memory pool to allocate incoming events from.
SynchronizedMemoryPool<Event, kMaxEventCount> mEventPool;
@@ -270,6 +280,12 @@
//! Set to the nanoapp we are in the process of unloading in unloadNanoapp()
Nanoapp *mStoppingNanoapp = nullptr;
+ //! The object which manages power related controls.
+ PowerControlManager mPowerControlManager;
+
+ //! The maximum number of events ever waiting in the event pool.
+ size_t mMaxEventPoolUsage = 0;
+
/**
* Do one round of Nanoapp event delivery, only considering events in
* Nanoapps' own queues (not mEvents).
diff --git a/core/include/chre/core/event_loop_manager.h b/core/include/chre/core/event_loop_manager.h
index 7d6c71b..aaa57be 100644
--- a/core/include/chre/core/event_loop_manager.h
+++ b/core/include/chre/core/event_loop_manager.h
@@ -21,10 +21,10 @@
#include "chre/core/event_loop.h"
#include "chre/core/gnss_request_manager.h"
#include "chre/core/host_comms_manager.h"
-#include "chre/core/memory_manager.h"
#include "chre/core/sensor_request_manager.h"
#include "chre/core/wifi_request_manager.h"
#include "chre/core/wwan_request_manager.h"
+#include "chre/platform/memory_manager.h"
#include "chre/platform/mutex.h"
#include "chre/util/fixed_size_vector.h"
#include "chre/util/non_copyable.h"
@@ -221,6 +221,10 @@
//! Provide an alias to the EventLoopManager singleton.
typedef Singleton<EventLoopManager> EventLoopManagerSingleton;
+//! Extern the explicit EventLoopManagerSingleton to force non-inline method
+//! calls. This reduces codesize considerably.
+extern template class Singleton<EventLoopManager>;
+
} // namespace chre
#endif // CHRE_CORE_EVENT_LOOP_MANAGER_H_
diff --git a/core/include/chre/core/wifi_request_manager.h b/core/include/chre/core/wifi_request_manager.h
index 6584e4f..e5fc4bb 100644
--- a/core/include/chre/core/wifi_request_manager.h
+++ b/core/include/chre/core/wifi_request_manager.h
@@ -20,6 +20,7 @@
#include "chre/core/nanoapp.h"
#include "chre/platform/platform_wifi.h"
#include "chre/util/non_copyable.h"
+#include "chre/util/time.h"
namespace chre {
@@ -353,6 +354,9 @@
* @param eventData a pointer to the scan event to release.
*/
static void freeWifiScanEventCallback(uint16_t eventType, void *eventData);
+
+ //! System time when last scan request was made.
+ Nanoseconds mLastScanRequestTime;
};
} // namespace chre
diff --git a/core/sensor_request_manager.cc b/core/sensor_request_manager.cc
index 67e5ad5..0a8c2be 100644
--- a/core/sensor_request_manager.cc
+++ b/core/sensor_request_manager.cc
@@ -242,7 +242,8 @@
Sensor *sensorPtr = nullptr;
if (sensorType == SensorType::Unknown
|| sensorType >= SensorType::SENSOR_TYPE_COUNT) {
- LOGW("Attempting to get Sensor of an invalid SensorType");
+ LOGW("Attempting to get Sensor of an invalid SensorType %d",
+ static_cast<int>(sensorType));
} else {
size_t sensorIndex = getSensorTypeArrayIndex(sensorType);
if (mSensorRequests[sensorIndex].sensor.has_value()) {
diff --git a/core/tests/memory_manager_test.cc b/core/tests/memory_manager_test.cc
index 2c6674e..208d508 100644
--- a/core/tests/memory_manager_test.cc
+++ b/core/tests/memory_manager_test.cc
@@ -16,7 +16,7 @@
#include "gtest/gtest.h"
-#include "chre/core/memory_manager.h"
+#include "chre/platform/memory_manager.h"
#include "chre/platform/memory.h"
#include "chre/platform/log.h"
@@ -41,14 +41,15 @@
EXPECT_NE(ptr, nullptr);
EXPECT_EQ(manager.getTotalAllocatedBytes(), 1);
EXPECT_EQ(manager.getAllocationCount(), 1);
- manager.nanoappFree(ptr);
+ manager.nanoappFree(&app, ptr);
EXPECT_EQ(manager.getTotalAllocatedBytes(), 0);
EXPECT_EQ(manager.getAllocationCount(), 0);
}
TEST(MemoryManager, NullPointerFree) {
MemoryManager manager;
- manager.nanoappFree(nullptr);
+ Nanoapp app;
+ manager.nanoappFree(&app, nullptr);
EXPECT_EQ(manager.getTotalAllocatedBytes(), 0);
EXPECT_EQ(manager.getAllocationCount(), 0);
}
@@ -89,7 +90,7 @@
curr = head;
for (size_t i = 0; i < maxCount; i++) {
node *temp = curr->next;
- manager.nanoappFree(curr);
+ manager.nanoappFree(&app, curr);
curr = temp;
}
EXPECT_EQ(manager.getTotalAllocatedBytes(), 0);
diff --git a/core/wifi_request_manager.cc b/core/wifi_request_manager.cc
index 877983d..cd79208 100644
--- a/core/wifi_request_manager.cc
+++ b/core/wifi_request_manager.cc
@@ -20,6 +20,7 @@
#include "chre/core/wifi_request_manager.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/log.h"
+#include "chre/platform/system_time.h"
#include "chre/util/system/debug_dump.h"
namespace chre {
@@ -80,15 +81,28 @@
const void *cookie) {
CHRE_ASSERT(nanoapp);
+ // TODO(b/65331248): replace with a timer to actively check response timeout
+ bool timedOut = (mScanRequestingNanoappInstanceId.has_value()
+ && mLastScanRequestTime
+ + Nanoseconds(CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS)
+ < SystemTime::getMonotonicTime());
+ if (timedOut) {
+ LOGE("Scan request async response timed out");
+ mScanRequestingNanoappInstanceId.reset();
+ }
+
bool success = false;
- if (!mScanRequestingNanoappInstanceId.has_value()) {
+ if (mScanRequestingNanoappInstanceId.has_value()) {
+ LOGE("Active wifi scan request made while a request is in flight");
+ } else {
success = mPlatformWifi.requestScan(params);
- if (success) {
+ if (!success) {
+ LOGE("Wifi scan request failed");
+ } else {
mScanRequestingNanoappInstanceId = nanoapp->getInstanceId();
mScanRequestingNanoappCookie = cookie;
+ mLastScanRequestTime = SystemTime::getMonotonicTime();
}
- } else {
- LOGE("Active wifi scan request made while a request is in flight");
}
return success;
@@ -415,10 +429,25 @@
void WifiRequestManager::handleScanResponseSync(bool pending,
uint8_t errorCode) {
- CHRE_ASSERT_LOG(mScanRequestingNanoappInstanceId.has_value(),
- "handleScanResponseSync called with no outstanding request");
+ // TODO(b/65206783): re-enable this assertion
+ //CHRE_ASSERT_LOG(mScanRequestingNanoappInstanceId.has_value(),
+ // "handleScanResponseSync called with no outstanding request");
+ if (!mScanRequestingNanoappInstanceId.has_value()) {
+ LOGE("handleScanResponseSync called with no outstanding request");
+ }
+
+ // TODO: raise this to CHRE_ASSERT_LOG
+ if (!pending && errorCode == CHRE_ERROR_NONE) {
+ LOGE("Invalid wifi scan response");
+ errorCode = CHRE_ERROR;
+ }
+
if (mScanRequestingNanoappInstanceId.has_value()) {
bool success = (pending && errorCode == CHRE_ERROR_NONE);
+ if (!success) {
+ LOGW("Wifi scan request failed: pending %d, errorCode %" PRIu8,
+ pending, errorCode);
+ }
postScanRequestAsyncResultEventFatal(*mScanRequestingNanoappInstanceId,
success, errorCode,
mScanRequestingNanoappCookie);
@@ -445,35 +474,36 @@
}
void WifiRequestManager::handleScanEventSync(chreWifiScanEvent *event) {
- if (mScanRequestResultsArePending) {
- // Reset the event distribution logic once an entire scan event has been
- // received.
- mScanEventResultCountAccumulator += event->resultCount;
- if (mScanEventResultCountAccumulator >= event->resultTotal) {
- mScanEventResultCountAccumulator = 0;
- mScanRequestResultsArePending = false;
- }
- }
-
postScanEventFatal(event);
}
void WifiRequestManager::handleFreeWifiScanEvent(chreWifiScanEvent *scanEvent) {
- mPlatformWifi.releaseScanEvent(scanEvent);
-
- if (!mScanRequestResultsArePending
- && mScanRequestingNanoappInstanceId.has_value()) {
- Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop()
- .findNanoappByInstanceId(*mScanRequestingNanoappInstanceId);
- if (nanoapp == nullptr) {
- CHRE_ASSERT_LOG(false, "Attempted to unsubscribe unknown nanoapp from "
- "WiFi scan events");
- } else if (!nanoappHasScanMonitorRequest(*mScanRequestingNanoappInstanceId)) {
- nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
+ if (mScanRequestResultsArePending) {
+ // Reset the event distribution logic once an entire scan event has been
+ // received and processed by the nanoapp requesting the scan event.
+ mScanEventResultCountAccumulator += scanEvent->resultCount;
+ if (mScanEventResultCountAccumulator >= scanEvent->resultTotal) {
+ mScanEventResultCountAccumulator = 0;
+ mScanRequestResultsArePending = false;
}
- mScanRequestingNanoappInstanceId.reset();
+ if (!mScanRequestResultsArePending
+ && mScanRequestingNanoappInstanceId.has_value()) {
+ Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop()
+ .findNanoappByInstanceId(*mScanRequestingNanoappInstanceId);
+ if (nanoapp == nullptr) {
+ CHRE_ASSERT_LOG(false, "Attempted to unsubscribe unknown nanoapp from "
+ "WiFi scan events");
+ } else if (!nanoappHasScanMonitorRequest(
+ *mScanRequestingNanoappInstanceId)) {
+ nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
+ }
+
+ mScanRequestingNanoappInstanceId.reset();
+ }
}
+
+ mPlatformWifi.releaseScanEvent(scanEvent);
}
void WifiRequestManager::freeWifiScanEventCallback(uint16_t eventType,
diff --git a/external/flatbuffers/include/flatbuffers/flatbuffers.h b/external/flatbuffers/include/flatbuffers/flatbuffers.h
index 0d87eee..6f14b23 100644
--- a/external/flatbuffers/include/flatbuffers/flatbuffers.h
+++ b/external/flatbuffers/include/flatbuffers/flatbuffers.h
@@ -49,7 +49,7 @@
// std::function) aren't strictly required, so setting it for now.
#define FLATBUFFERS_CPP98_STL
- #include "chre/platform/assert.h"
+ #include "chre/util/container_support.h"
#include "chre/util/dynamic_vector.h"
#include "chre/util/unique_ptr.h"
@@ -593,7 +593,7 @@
uoffset_t size() const {
assert(cur_ != nullptr && buf_ != nullptr);
- return static_cast<uoffset_t>(reserved_ - (cur_ - buf_));
+ return static_cast<uoffset_t>(reserved_ - static_cast<size_t>(cur_ - buf_));
}
uint8_t *data() const {
diff --git a/host/common/socket_client.cc b/host/common/socket_client.cc
index f6e8047..ac2394c 100644
--- a/host/common/socket_client.cc
+++ b/host/common/socket_client.cc
@@ -19,10 +19,12 @@
#include <inttypes.h>
#include <string.h>
+#include <unistd.h>
#include <chrono>
#include <cutils/sockets.h>
+#include <sys/socket.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
@@ -220,16 +222,38 @@
}
bool SocketClient::tryConnect(bool suppressErrorLogs) {
+ bool success = false;
+
errno = 0;
- mSockFd = socket_local_client(mSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_SEQPACKET);
- if (mSockFd == INVALID_SOCKET && !suppressErrorLogs) {
- LOGE("Couldn't create/connect client socket to '%s': %s",
- mSocketName, strerror(errno));
+ int sockFd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+ if (sockFd >= 0) {
+ // Set the send buffer size to 2MB to allow plenty of room for nanoapp
+ // loading
+ int sndbuf = 2 * 1024 * 1024;
+ int ret = setsockopt(
+ sockFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
+ if (ret == 0) {
+ mSockFd = socket_local_client_connect(
+ sockFd, mSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+ if (mSockFd != INVALID_SOCKET) {
+ success = true;
+ } else if (!suppressErrorLogs) {
+ LOGE("Couldn't connect client socket to '%s': %s",
+ mSocketName, strerror(errno));
+ }
+ } else if (!suppressErrorLogs) {
+ LOGE("Failed to set SO_SNDBUF to %d: %s", sndbuf, strerror(errno));
+ }
+
+ if (!success) {
+ close(sockFd);
+ }
+ } else if (!suppressErrorLogs) {
+ LOGE("Couldn't create local socket: %s", strerror(errno));
}
- return (mSockFd != INVALID_SOCKET);
+ return success;
}
} // namespace chre
diff --git a/host/common/test/chre_test_client.cc b/host/common/test/chre_test_client.cc
index 602a6e3..cde7af3 100644
--- a/host/common/test/chre_test_client.cc
+++ b/host/common/test/chre_test_client.cc
@@ -147,7 +147,7 @@
uint8_t messageData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
HostProtocolHost::encodeNanoappMessage(
builder, chre::kMessageWorldAppId, kHostEndpoint,
- 1234 /* transactionId */, messageData, sizeof(messageData));
+ 1234 /* messageType */, messageData, sizeof(messageData));
LOGI("Sending message to nanoapp (%" PRIu32 " bytes w/%zu bytes of payload)",
builder.GetSize(), sizeof(messageData));
diff --git a/host/hal_generic/generic_context_hub.cc b/host/hal_generic/generic_context_hub.cc
index 28c5b3e..b8962da 100644
--- a/host/hal_generic/generic_context_hub.cc
+++ b/host/hal_generic/generic_context_hub.cc
@@ -69,6 +69,15 @@
} // anonymous namespace
+GenericContextHub::DeathRecipient::DeathRecipient(
+ sp<GenericContextHub> contexthub) : mGenericContextHub(contexthub){}
+
+void GenericContextHub::DeathRecipient::serviceDied(
+ uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
+ uint32_t hubId = static_cast<uint32_t>(cookie);
+ mGenericContextHub->handleServiceDeath(hubId);
+}
+
GenericContextHub::GenericContextHub() {
constexpr char kChreSocketName[] = "chre";
@@ -76,6 +85,8 @@
if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
ALOGE("Couldn't start socket client");
}
+
+ mDeathRecipient = new DeathRecipient(this);
}
Return<void> GenericContextHub::debug(
@@ -162,6 +173,18 @@
// TODO: currently we only support 1 hub behind this HAL implementation
if (hubId == kDefaultHubId) {
std::lock_guard<std::mutex> lock(mCallbacksLock);
+
+ if (cb != nullptr) {
+ if (mCallbacks != nullptr) {
+ ALOGD("Modifying callback for hubId %" PRIu32, hubId);
+ mCallbacks->unlinkToDeath(mDeathRecipient);
+ }
+ Return<bool> linkReturn = cb->linkToDeath(mDeathRecipient, hubId);
+ if (!linkReturn.withDefault(false)) {
+ ALOGW("Could not link death recipient to hubId %" PRIu32, hubId);
+ }
+ }
+
mCallbacks = cb;
result = Result::OK;
} else {
@@ -462,6 +485,11 @@
}
}
+void GenericContextHub::handleServiceDeath(uint32_t hubId) {
+ std::lock_guard<std::mutex> lock(mCallbacksLock);
+ ALOGI("Context hub service died for hubId %" PRIu32, hubId);
+ mCallbacks.clear();
+}
IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) {
return new GenericContextHub();
diff --git a/host/hal_generic/generic_context_hub.h b/host/hal_generic/generic_context_hub.h
index a09f0b9..5409d65 100644
--- a/host/hal_generic/generic_context_hub.h
+++ b/host/hal_generic/generic_context_hub.h
@@ -112,7 +112,19 @@
void invokeClientCallback(std::function<void()> callback);
};
+ class DeathRecipient : public hidl_death_recipient {
+ public:
+ DeathRecipient(const sp<GenericContextHub> contexthub);
+ void serviceDied(uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& who)
+ override;
+
+ private:
+ sp<GenericContextHub> mGenericContextHub;
+ };
+
sp<SocketCallbacks> mSocketCallbacks;
+ sp<DeathRecipient> mDeathRecipient;
// Cached hub info used for getHubs(), and synchronization primitives to make
// that function call synchronous if we need to query it
@@ -130,6 +142,9 @@
// Write a string to mDebugFd
void writeToDebugFile(const char *str);
void writeToDebugFile(const char *str, size_t len);
+
+ // Unregisters callback when context hub service dies
+ void handleServiceDeath(uint32_t hubId);
};
extern "C" IContexthub* HIDL_FETCH_IContexthub(const char* name);
diff --git a/host/msm/daemon/chre_daemon.cc b/host/msm/daemon/chre_daemon.cc
index b6563b5..3c13f04 100644
--- a/host/msm/daemon/chre_daemon.cc
+++ b/host/msm/daemon/chre_daemon.cc
@@ -179,7 +179,7 @@
}
}
-static int64_t getTimeOffset() {
+static int64_t getTimeOffset(bool *success) {
int64_t timeOffset = 0;
#if defined(__aarch64__)
@@ -191,18 +191,34 @@
// Use uint64_t to store since the MRS instruction uses 64 bit (X) registers
// (http://infocenter.arm.com/help/topic/
// com.arm.doc.den0024a/ch06s05s02.html)
- uint64_t qTimerCount = 0, qTimerFreqKHz = 0;
+ uint64_t qTimerCount = 0, qTimerFreq = 0;
uint64_t hostTimeNano = elapsedRealtimeNano();
asm volatile("mrs %0, cntpct_el0" : "=r"(qTimerCount));
- asm volatile("mrs %0, cntfrq_el0" : "=r"(qTimerFreqKHz));
- qTimerFreqKHz /= 1000;
+ asm volatile("mrs %0, cntfrq_el0" : "=r"(qTimerFreq));
- if (qTimerFreqKHz != 0) {
- uint64_t qTimerNanos = (qTimerCount < UINT64_MAX / 1000000) ?
- (qTimerCount * 1000000) : UINT64_MAX;
- qTimerNanos /= qTimerFreqKHz;
+ constexpr uint64_t kOneSecondInNanoseconds = 1000000000;
+ if (qTimerFreq != 0) {
+ // Get the seconds part first, then convert the remainder to prevent
+ // overflow
+ uint64_t qTimerNanos = (qTimerCount / qTimerFreq);
+ if (qTimerNanos > UINT64_MAX / kOneSecondInNanoseconds) {
+ LOGE("CNTPCT_EL0 conversion to nanoseconds overflowed during time sync."
+ " Aborting time sync.");
+ *success = false;
+ } else {
+ qTimerNanos *= kOneSecondInNanoseconds;
- timeOffset = hostTimeNano - qTimerNanos;
+ // Round the remainder portion to the nearest nanosecond
+ uint64_t remainder = (qTimerCount % qTimerFreq);
+ qTimerNanos +=
+ (remainder * kOneSecondInNanoseconds + qTimerFreq / 2) / qTimerFreq;
+
+ timeOffset = hostTimeNano - qTimerNanos;
+ *success = true;
+ }
+ } else {
+ LOGE("CNTFRQ_EL0 had 0 value. Aborting time sync.");
+ *success = false;
}
#else
#error "Unsupported CPU architecture type"
@@ -212,16 +228,19 @@
}
static void sendTimeSyncMessage() {
- int64_t timeOffset = getTimeOffset();
+ bool timeSyncSuccess = true;
+ int64_t timeOffset = getTimeOffset(&timeSyncSuccess);
- flatbuffers::FlatBufferBuilder builder(64);
- HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
- int success = chre_slpi_deliver_message_from_host(
- static_cast<const unsigned char *>(builder.GetBufferPointer()),
- static_cast<int>(builder.GetSize()));
+ if (timeSyncSuccess) {
+ flatbuffers::FlatBufferBuilder builder(64);
+ HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
+ int success = chre_slpi_deliver_message_from_host(
+ static_cast<const unsigned char *>(builder.GetBufferPointer()),
+ static_cast<int>(builder.GetSize()));
- if (success != 0) {
- LOGE("Failed to deliver timestamp message from host to CHRE: %d", success);
+ if (success != 0) {
+ LOGE("Failed to deliver timestamp message from host to CHRE: %d", success);
+ }
}
}
diff --git a/platform/include/chre/platform/condition_variable.h b/platform/include/chre/platform/condition_variable.h
index af638ff..83cd29e 100644
--- a/platform/include/chre/platform/condition_variable.h
+++ b/platform/include/chre/platform/condition_variable.h
@@ -20,6 +20,7 @@
#include "chre/platform/mutex.h"
#include "chre/target_platform/condition_variable_base.h"
#include "chre/util/non_copyable.h"
+#include "chre/util/time.h"
namespace chre {
@@ -57,6 +58,17 @@
* @param The currently locked mutex.
*/
void wait(Mutex& mutex);
+
+ /**
+ * Same behavior as the wait function, but with a timeout to unblock the
+ * calling thread if not notified within the timeout period.
+ *
+ * @param mutex The currently locked mutex.
+ * @param timeout The timeout duration in nanoseconds.
+ *
+ * @return False if timed out, true if notified.
+ */
+ bool wait_for(Mutex& mutex, Nanoseconds timeout);
};
} // namespace chre
diff --git a/platform/include/chre/platform/memory.h b/platform/include/chre/platform/memory.h
index f44b4b3..205ecdd 100644
--- a/platform/include/chre/platform/memory.h
+++ b/platform/include/chre/platform/memory.h
@@ -32,15 +32,6 @@
*/
void memoryFree(void *pointer);
-/**
- * Allocates memory 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>
-T *memoryAlloc(Args&&... args);
-
} // namespace chre
-#include "chre/platform/memory_impl.h"
-
#endif // CHRE_PLATFORM_MEMORY_H_
diff --git a/platform/include/chre/platform/memory_impl.h b/platform/include/chre/platform/memory_impl.h
deleted file mode 100644
index 719082d..0000000
--- a/platform/include/chre/platform/memory_impl.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef CHRE_PLATFORM_MEMORY_IMPL_H_
-#define CHRE_PLATFORM_MEMORY_IMPL_H_
-
-#include <new>
-#include <utility>
-
-namespace chre {
-
-template<typename T, typename... Args>
-T *memoryAlloc(Args&&... args) {
- auto *storage = static_cast<T *>(memoryAlloc(sizeof(T)));
- if (storage != nullptr) {
- new(storage) T(std::forward<Args>(args)...);
- }
-
- return storage;
-}
-
-} // namespace chre
-
-#endif // CHRE_PLATFORM_MEMORY_IMPL_H_
diff --git a/core/include/chre/core/memory_manager.h b/platform/include/chre/platform/memory_manager.h
similarity index 81%
rename from core/include/chre/core/memory_manager.h
rename to platform/include/chre/platform/memory_manager.h
index 5db51e0..3c566ed 100644
--- a/core/include/chre/core/memory_manager.h
+++ b/platform/include/chre/platform/memory_manager.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef CHRE_CORE_MEMORY_MANAGER_H_
-#define CHRE_CORE_MEMORY_MANAGER_H_
+#ifndef CHRE_PLATFORM_MEMORY_MANAGER_H_
+#define CHRE_PLATFORM_MEMORY_MANAGER_H_
#include <cstddef>
#include <cstdint>
@@ -28,30 +28,27 @@
/**
* The MemoryManager keeps track of heap memory allocated/deallocated by all
* nanoapps.
- * TODO: Free memory space when nanoapps are unloaded.
- * TODO: Move this implementation to platform-specific code area.
*
+ * TODO: Free memory space when nanoapps are unloaded.
*/
class MemoryManager : public NonCopyable {
public:
/**
- * Initializes a MemoryManager.
- */
- MemoryManager();
-
- /**
* Allocate heap memory in CHRE.
+ *
+ * @param app The pointer to the nanoapp requesting memory.
* @param bytes The size in bytes to allocate.
- * @param app Pointer to the nanoapp requesting memory.
* @return the allocated memory pointer. nullptr if the allocation fails.
*/
void *nanoappAlloc(Nanoapp *app, uint32_t bytes);
/**
* Free heap memory in CHRE.
+ *
+ * @param app The pointer to the nanoapp requesting memory free.
* @param ptr The pointer to the memory to deallocate.
*/
- void nanoappFree(void *ptr);
+ void nanoappFree(Nanoapp *app, void *ptr);
/**
* @return current total allocated memory in bytes.
@@ -113,18 +110,32 @@
};
//! Stores total allocated memory in bytes (not including header).
- size_t mTotalAllocatedBytes;
+ size_t mTotalAllocatedBytes = 0;
//! Stores total number of allocated memory spaces.
- size_t mAllocationCount;
+ size_t mAllocationCount = 0;
//! The maximum allowable total allocated memory in bytes for all nanoapps.
static constexpr size_t kMaxAllocationBytes = (128 * 1024);
//! The maximum allowable count of memory allocations for all nanoapps.
static constexpr size_t kMaxAllocationCount = (8 * 1024);
+
+ /**
+ * Called by nanoappAlloc to perform the appropriate call to memory alloc.
+ *
+ * The semantics are the same as nanoappAlloc.
+ */
+ void *doAlloc(Nanoapp *app, uint32_t size);
+
+ /**
+ * Called by nanoappFree to perform the appropriate call to memory free.
+ *
+ * The sematics are the same as nanoappFree.
+ */
+ void doFree(Nanoapp *app, void *ptr);
};
} // namespace chre
-#endif // CHRE_CORE_MEMORY_MANAGER_H_
+#endif // CHRE_PLATFORM_MEMORY_MANAGER_H_
diff --git a/platform/include/chre/platform/power_control_manager.h b/platform/include/chre/platform/power_control_manager.h
new file mode 100644
index 0000000..79eb24a
--- /dev/null
+++ b/platform/include/chre/platform/power_control_manager.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_POWER_CONTROL_MANAGER_H
+#define CHRE_PLATFORM_POWER_CONTROL_MANAGER_H
+
+#include <cstddef>
+
+#include "chre/target_platform/power_control_manager_base.h"
+#include "chre/util/non_copyable.h"
+
+namespace chre {
+
+/**
+ * An abstraction for an entity that performs power-related controls for the
+ * underlying platform.
+ */
+class PowerControlManager : public PowerControlManagerBase,
+ public NonCopyable {
+ public:
+ /**
+ * Perform power-related control after a single process of the event loop.
+ *
+ * @param numPendingEvents The current size of the event queue.
+ */
+ void postEventLoopProcess(size_t numPendingEvents);
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_POWER_CONTROL_MANAGER_H
diff --git a/platform/linux/chre_api_re.cc b/platform/linux/chre_api_re.cc
index c6e2063..0e79f87 100644
--- a/platform/linux/chre_api_re.cc
+++ b/platform/linux/chre_api_re.cc
@@ -18,8 +18,28 @@
#include "chre_api/chre/re.h"
#include "chre/platform/log.h"
+#include "chre/util/macros.h"
-void chreAbort(uint32_t abortCode) {
- LOGE("Aborting with error code %" PRIu32, abortCode);
- abort();
+DLL_EXPORT void chreLog(enum chreLogLevel level, const char *formatStr, ...) {
+ char logBuf[512];
+ va_list args;
+
+ va_start(args, formatStr);
+ vsnprintf(logBuf, sizeof(logBuf), formatStr, args);
+ va_end(args);
+
+ switch (level) {
+ case CHRE_LOG_ERROR:
+ LOGE("%s", logBuf);
+ break;
+ case CHRE_LOG_WARN:
+ LOGW("%s", logBuf);
+ break;
+ case CHRE_LOG_INFO:
+ LOGI("%s", logBuf);
+ break;
+ case CHRE_LOG_DEBUG:
+ default:
+ LOGD("%s", logBuf);
+ }
}
diff --git a/platform/linux/include/chre/target_platform/condition_variable_impl.h b/platform/linux/include/chre/target_platform/condition_variable_impl.h
index 2ec02bc..41aae9c 100644
--- a/platform/linux/include/chre/target_platform/condition_variable_impl.h
+++ b/platform/linux/include/chre/target_platform/condition_variable_impl.h
@@ -31,6 +31,12 @@
mConditionVariable.wait(mutex);
}
+inline bool ConditionVariable::wait_for(Mutex& mutex, Nanoseconds timeout) {
+ std::cv_status result = mConditionVariable.wait_for(
+ mutex, std::chrono::nanoseconds(timeout.toRawNanoseconds()));
+ return (result != std::cv_status::timeout);
+}
+
} // namespace chre
#endif // CHRE_PLATFORM_LINUX_CONDITION_VARIABLE_IMPL_H_
diff --git a/platform/linux/include/chre/target_platform/platform_nanoapp_base.h b/platform/linux/include/chre/target_platform/platform_nanoapp_base.h
index 1dd1543..4454596 100644
--- a/platform/linux/include/chre/target_platform/platform_nanoapp_base.h
+++ b/platform/linux/include/chre/target_platform/platform_nanoapp_base.h
@@ -18,6 +18,9 @@
#define CHRE_PLATFORM_LINUX_PLATFORM_NANOAPP_BASE_H_
#include <cstdint>
+#include <string>
+
+#include "chre/platform/shared/nanoapp_support_lib_dso.h"
#include "chre/util/entry_points.h"
@@ -26,18 +29,71 @@
/**
* Linux-specific nanoapp functionality.
*/
-struct PlatformNanoappBase {
- //! The function pointer of the nanoapp start entry point.
- chreNanoappStartFunction *mStart;
+class PlatformNanoappBase {
+ public:
+ /**
+ * Associate this Nanoapp with a nanoapp included in a .so that is pre-loaded
+ * onto the filesystem. Actually loading the .so into memory is done when
+ * start() is called.
+ *
+ * @param filename The name of the .so file in /vendor/lib/dsp that holds this
+ * nanoapp. This string is not deep-copied, so the memory must remain
+ * valid for the lifetime of this Nanoapp instance.
+ */
+ void loadFromFile(const std::string& filename);
- //! The function pointer of the nanoapp handle event entry point.
- chreNanoappHandleEventFunction *mHandleEvent;
+ /**
+ * 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);
- //! The function pointer of the nanoapp end entry point.
- chreNanoappEndFunction *mEnd;
+ /**
+ * @return true if the app's binary data is resident in memory, i.e. a
+ * previous call to loadFromBuffer() or loadStatic() was successful
+ */
+ bool isLoaded() const;
- uint64_t mAppId;
- uint32_t mAppVersion;
+ 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 dynamic shared object (DSO) handle returned by dlopen.
+ void *mDsoHandle = nullptr;
+
+ //! Pointer to the app info structure within this nanoapp
+ const struct chreNslNanoappInfo *mAppInfo = nullptr;
+
+ bool mIsStatic = false;
+
+ //! If this is a pre-loaded, but non-static nanoapp (i.e. loaded from
+ //! loadFromFile), this will be set to the filename string to pass to dlopen()
+ std::string mFilename;
+
+ /**
+ * Calls through to openNanoappFromFile if the nanoapp was loaded from a
+ * shared object or returns true if the nanoapp is static.
+ *
+ * @return true if the nanoapp was loaded successfully.
+ */
+ bool openNanoapp();
+
+ /**
+ * Calls dlopen on the app filename, and fetches and validates the app info
+ * pointer. This will result in execution of any on-load handlers (e.g.
+ * static global constructors) in the nanoapp.
+ *
+ * @return true if the app was opened successfully and the app info
+ * structure passed validation
+ */
+ bool openNanoappFromFile();
+
+ /**
+ * 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();
};
} // namespace chre
diff --git a/platform/shared/memory.cc b/platform/linux/include/chre/target_platform/power_control_manager_base.h
similarity index 68%
copy from platform/shared/memory.cc
copy to platform/linux/include/chre/target_platform/power_control_manager_base.h
index c137eb8..908e361 100644
--- a/platform/shared/memory.cc
+++ b/platform/linux/include/chre/target_platform/power_control_manager_base.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,13 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
-
-#include <stdlib.h>
+#ifndef CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
+#define CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
-}
+class PowerControlManagerBase {};
-void memoryFree(void *pointer) {
- free(pointer);
-}
+} // namespace chre
-} // namespace chre
+#endif // CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
diff --git a/platform/linux/include/chre/target_platform/static_nanoapp_init.h b/platform/linux/include/chre/target_platform/static_nanoapp_init.h
index c53a122..5d92fdf 100644
--- a/platform/linux/include/chre/target_platform/static_nanoapp_init.h
+++ b/platform/linux/include/chre/target_platform/static_nanoapp_init.h
@@ -29,23 +29,33 @@
* when creating the global instance of the nanoapp.
* @param appId the app's unique 64-bit ID
*/
-#define CHRE_STATIC_NANOAPP_INIT(appName, appId, appVersion) \
-namespace chre { \
- \
-UniquePtr<Nanoapp> initializeStaticNanoapp##appName() { \
- UniquePtr<Nanoapp> nanoapp = MakeUnique<Nanoapp>(); \
- if (nanoapp.isNull()) { \
- FATAL_ERROR("Failed to allocate nanoapp " #appName); \
- } else { \
- nanoapp->mStart = nanoappStart; \
- nanoapp->mHandleEvent = nanoappHandleEvent; \
- nanoapp->mEnd = nanoappEnd; \
- nanoapp->mAppId = appId; \
- nanoapp->mAppVersion = appVersion; \
- } \
- \
- return nanoapp; \
-} \
+#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; \
+ if (nanoapp.isNull()) { \
+ FATAL_ERROR("Failed to allocate nanoapp " #appName); \
+ } else { \
+ nanoapp->loadStatic(&appInfo); \
+ } \
+ \
+ return nanoapp; \
+} \
} /* namespace chre */
#endif // CHRE_PLATFORM_LINUX_STATIC_NANOAPP_INIT_H_
diff --git a/platform/linux/init.cc b/platform/linux/init.cc
index b1fa069..d7cb749 100644
--- a/platform/linux/init.cc
+++ b/platform/linux/init.cc
@@ -27,11 +27,20 @@
#include "chre/util/time.h"
#include <csignal>
+#include <tclap/CmdLine.h>
#include <thread>
using chre::EventLoopManagerSingleton;
using chre::Milliseconds;
+//! A description of the simulator.
+constexpr char kSimDescription[] = "A simulation environment for the Context "
+ "Hub Runtime Environment (CHRE)";
+
+//! The version of the simulator. This is not super important but is assigned by
+//! rules of semantic versioning.
+constexpr char kSimVersion[] = "0.1.0";
+
namespace {
extern "C" void signalHandler(int sig) {
@@ -42,21 +51,46 @@
}
-int main() {
- chre::PlatformLogSingleton::init();
- chre::init();
+int main(int argc, char **argv) {
+ try {
+ // Parse command-line arguments.
+ TCLAP::CmdLine cmd(kSimDescription, ' ', kSimVersion);
+ TCLAP::SwitchArg no_static_nanoapps_arg("", "no_static_nanoapps",
+ "disable running static nanoapps", cmd, false);
+ TCLAP::MultiArg<std::string> nanoapps_arg("", "nanoapp",
+ "a nanoapp shared object to load and execute", false, "path", cmd);
+ cmd.parse(argc, argv);
- // Register a signal handler.
- std::signal(SIGINT, signalHandler);
+ // Initialize the system.
+ chre::PlatformLogSingleton::init();
+ chre::init();
- // Load any static nanoapps and start the event loop.
- std::thread chreThread([&]() {
- chre::loadStaticNanoapps();
- EventLoopManagerSingleton::get()->getEventLoop().run();
- });
- chreThread.join();
+ // Register a signal handler.
+ std::signal(SIGINT, signalHandler);
- chre::deinit();
- chre::PlatformLogSingleton::deinit();
+ // Load any static nanoapps and start the event loop.
+ std::thread chreThread([&]() {
+ // Load static nanoapps unless they are disabled by a command-line flag.
+ if (!no_static_nanoapps_arg.getValue()) {
+ chre::loadStaticNanoapps();
+ }
+
+ // Load dynamic nanoapps specified on the command-line.
+ chre::DynamicVector<chre::UniquePtr<chre::Nanoapp>> dynamicNanoapps;
+ for (const auto& nanoapp : nanoapps_arg.getValue()) {
+ dynamicNanoapps.push_back(chre::MakeUnique<chre::Nanoapp>());
+ dynamicNanoapps.back()->loadFromFile(nanoapp);
+ EventLoopManagerSingleton::get()->getEventLoop()
+ .startNanoapp(dynamicNanoapps.back());
+ }
+
+ EventLoopManagerSingleton::get()->getEventLoop().run();
+ });
+ chreThread.join();
+
+ chre::deinit();
+ chre::PlatformLogSingleton::deinit();
+ } catch (TCLAP::ExitException) {}
+
return 0;
}
diff --git a/platform/shared/memory.cc b/platform/linux/memory.cc
similarity index 80%
rename from platform/shared/memory.cc
rename to platform/linux/memory.cc
index c137eb8..ad92c85 100644
--- a/platform/shared/memory.cc
+++ b/platform/linux/memory.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,16 @@
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/shared/memory.cc b/platform/linux/memory_manager.cc
similarity index 66%
copy from platform/shared/memory.cc
copy to platform/linux/memory_manager.cc
index c137eb8..6d55a64 100644
--- a/platform/shared/memory.cc
+++ b/platform/linux/memory_manager.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
+#include "chre/platform/memory_manager.h"
-#include <stdlib.h>
+#include "chre/util/memory.h"
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
+void *MemoryManager::doAlloc(Nanoapp *app, uint32_t bytes) {
+ return chre::memoryAlloc(bytes);
}
-void memoryFree(void *pointer) {
- free(pointer);
+void MemoryManager::doFree(Nanoapp *app, void *ptr) {
+ chre::memoryFree(ptr);
}
} // namespace chre
diff --git a/platform/linux/platform_nanoapp.cc b/platform/linux/platform_nanoapp.cc
index 7b9eb7b..28be2a8 100644
--- a/platform/linux/platform_nanoapp.cc
+++ b/platform/linux/platform_nanoapp.cc
@@ -14,33 +14,43 @@
* limitations under the License.
*/
-#include "chre_api/chre/version.h"
#include "chre/platform/platform_nanoapp.h"
+#include <cinttypes>
+#include <dlfcn.h>
+
+#include "chre_api/chre/version.h"
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/shared/nanoapp_dso_util.h"
+
namespace chre {
-PlatformNanoapp::~PlatformNanoapp() {}
+PlatformNanoapp::~PlatformNanoapp() {
+ closeNanoapp();
+}
bool PlatformNanoapp::start() {
- return mStart();
+ return openNanoapp() && mAppInfo->entryPoints.start();
}
void PlatformNanoapp::handleEvent(uint32_t senderInstanceId,
uint16_t eventType,
const void *eventData) {
- mHandleEvent(senderInstanceId, eventType, eventData);
+ mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
}
void PlatformNanoapp::end() {
- mEnd();
+ mAppInfo->entryPoints.end();
+ closeNanoapp();
}
uint64_t PlatformNanoapp::getAppId() const {
- return mAppId;
+ return (mAppInfo == nullptr ? 0 : mAppInfo->appId);
}
uint32_t PlatformNanoapp::getAppVersion() const {
- return mAppVersion;
+ return mAppInfo->appVersion;
}
uint32_t PlatformNanoapp::getTargetApiVersion() const {
@@ -56,4 +66,74 @@
return true;
}
+void PlatformNanoappBase::loadFromFile(const std::string& filename) {
+ CHRE_ASSERT(!isLoaded());
+ mFilename = filename;
+}
+
+void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
+ CHRE_ASSERT(!isLoaded());
+ mIsStatic = true;
+ mAppInfo = appInfo;
+}
+
+bool PlatformNanoappBase::isLoaded() const {
+ return (mIsStatic || mDsoHandle != nullptr);
+}
+
+bool PlatformNanoappBase::openNanoapp() {
+ bool success = false;
+
+ if (mIsStatic) {
+ success = true;
+ } else if (!mFilename.empty()) {
+ success = openNanoappFromFile();
+ } else {
+ CHRE_ASSERT(false);
+ }
+
+ return success;
+}
+
+bool PlatformNanoappBase::openNanoappFromFile() {
+ CHRE_ASSERT(!mFilename.empty());
+ CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
+ bool success = false;
+
+ mDsoHandle = dlopen(mFilename.c_str(), RTLD_NOW | RTLD_GLOBAL);
+ if (mDsoHandle == nullptr) {
+ LOGE("Failed to load nanoapp from file %s: %s",
+ mFilename.c_str(), dlerror());
+ } else {
+ mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
+ dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
+ if (mAppInfo == nullptr) {
+ LOGE("Failed to find app info symbol in %s: %s",
+ mFilename.c_str(), dlerror());
+ } else {
+ success = validateAppInfo(0 /* skip ID validation */, 0, mAppInfo,
+ true /* ignoreAppVersion */);
+ if (!success) {
+ mAppInfo = nullptr;
+ } else {
+ LOGI("Successfully loaded nanoapp %s (0x%016" PRIx64 ") version 0x%"
+ PRIx32 " uimg %d system %d from file %s", mAppInfo->name,
+ mAppInfo->appId, mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
+ mAppInfo->isSystemNanoapp, mFilename.c_str());
+ }
+ }
+ }
+
+ return success;
+}
+
+void PlatformNanoappBase::closeNanoapp() {
+ if (mDsoHandle != nullptr) {
+ if (dlclose(mDsoHandle) != 0) {
+ LOGE("dlclose failed: %s", dlerror());
+ }
+ mDsoHandle = nullptr;
+ }
+}
+
} // namespace chre
diff --git a/platform/shared/memory.cc b/platform/linux/platform_pal.cc
similarity index 72%
copy from platform/shared/memory.cc
copy to platform/linux/platform_pal.cc
index c137eb8..1ca25c9 100644
--- a/platform/shared/memory.cc
+++ b/platform/linux/platform_pal.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
-
-#include <stdlib.h>
+#include "chre/platform/shared/platform_pal.h"
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
-}
-
-void memoryFree(void *pointer) {
- free(pointer);
-}
+void PlatformPal::prePalApiCall() {}
} // namespace chre
diff --git a/platform/shared/memory.cc b/platform/linux/power_control_manager.cc
similarity index 70%
copy from platform/shared/memory.cc
copy to platform/linux/power_control_manager.cc
index c137eb8..2b06b61 100644
--- a/platform/shared/memory.cc
+++ b/platform/linux/power_control_manager.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,10 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
-
-#include <stdlib.h>
+#include "chre/platform/power_control_manager.h"
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
-}
+void PowerControlManager::postEventLoopProcess(size_t numPendingEvents) {}
-void memoryFree(void *pointer) {
- free(pointer);
-}
-
-} // namespace chre
+} // namespace chre
diff --git a/platform/platform.mk b/platform/platform.mk
index 9002c86..c3d1ec1 100644
--- a/platform/platform.mk
+++ b/platform/platform.mk
@@ -31,20 +31,27 @@
HEXAGON_SRCS += platform/shared/chre_api_wwan.cc
HEXAGON_SRCS += platform/shared/host_protocol_chre.cc
HEXAGON_SRCS += platform/shared/host_protocol_common.cc
-HEXAGON_SRCS += platform/shared/memory.cc
+HEXAGON_SRCS += platform/shared/memory_manager.cc
+HEXAGON_SRCS += platform/shared/nanoapp/nanoapp_dso_util.cc
HEXAGON_SRCS += platform/shared/pal_system_api.cc
HEXAGON_SRCS += platform/shared/platform_gnss.cc
HEXAGON_SRCS += platform/shared/platform_wifi.cc
HEXAGON_SRCS += platform/shared/platform_wwan.cc
HEXAGON_SRCS += platform/shared/system_time.cc
+HEXAGON_SRCS += platform/slpi/chre_api_re.cc
HEXAGON_SRCS += platform/slpi/fatal_error.cc
HEXAGON_SRCS += platform/slpi/host_link.cc
HEXAGON_SRCS += platform/slpi/init.cc
+HEXAGON_SRCS += platform/slpi/memory.cc
+HEXAGON_SRCS += platform/slpi/memory_manager.cc
HEXAGON_SRCS += platform/slpi/platform_log.cc
HEXAGON_SRCS += platform/slpi/platform_nanoapp.cc
+HEXAGON_SRCS += platform/slpi/platform_pal.cc
HEXAGON_SRCS += platform/slpi/platform_sensor.cc
HEXAGON_SRCS += platform/slpi/platform_sensor_util.cc
+HEXAGON_SRCS += platform/slpi/power_control_manager.cc
HEXAGON_SRCS += platform/slpi/preloaded_nanoapps.cc
+HEXAGON_SRCS += platform/slpi/smr_helper.cc
HEXAGON_SRCS += platform/slpi/system_time.cc
HEXAGON_SRCS += platform/slpi/system_timer.cc
@@ -55,10 +62,15 @@
# x86-specific Source Files ####################################################
+X86_SRCS += platform/linux/chre_api_re.cc
X86_SRCS += platform/linux/context.cc
X86_SRCS += platform/linux/fatal_error.cc
X86_SRCS += platform/linux/host_link.cc
+X86_SRCS += platform/linux/memory.cc
+X86_SRCS += platform/linux/memory_manager.cc
X86_SRCS += platform/linux/platform_log.cc
+X86_SRCS += platform/linux/platform_pal.cc
+X86_SRCS += platform/linux/power_control_manager.cc
X86_SRCS += platform/linux/system_time.cc
X86_SRCS += platform/linux/system_timer.cc
X86_SRCS += platform/linux/platform_nanoapp.cc
@@ -70,7 +82,8 @@
X86_SRCS += platform/shared/chre_api_version.cc
X86_SRCS += platform/shared/chre_api_wifi.cc
X86_SRCS += platform/shared/chre_api_wwan.cc
-X86_SRCS += platform/shared/memory.cc
+X86_SRCS += platform/shared/memory_manager.cc
+X86_SRCS += platform/shared/nanoapp/nanoapp_dso_util.cc
X86_SRCS += platform/shared/pal_gnss_stub.cc
X86_SRCS += platform/shared/pal_wifi_stub.cc
X86_SRCS += platform/shared/pal_wwan_stub.cc
diff --git a/platform/shared/chre_api_core.cc b/platform/shared/chre_api_core.cc
index 13a92a2..976b63b 100644
--- a/platform/shared/chre_api_core.cc
+++ b/platform/shared/chre_api_core.cc
@@ -116,27 +116,3 @@
chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
nanoapp->configureNanoappInfoEvents(enable);
}
-
-DLL_EXPORT void chreLog(enum chreLogLevel level, const char *formatStr, ...) {
- char logBuf[512];
- va_list args;
-
- va_start(args, formatStr);
- vsnprintf(logBuf, sizeof(logBuf), formatStr, args);
- va_end(args);
-
- switch (level) {
- case CHRE_LOG_ERROR:
- LOGE("%s", logBuf);
- break;
- case CHRE_LOG_WARN:
- LOGW("%s", logBuf);
- break;
- case CHRE_LOG_INFO:
- LOGI("%s", logBuf);
- break;
- case CHRE_LOG_DEBUG:
- default:
- LOGD("%s", logBuf);
- }
-}
diff --git a/platform/shared/chre_api_re.cc b/platform/shared/chre_api_re.cc
index d35e5f5..58dcfa5 100644
--- a/platform/shared/chre_api_re.cc
+++ b/platform/shared/chre_api_re.cc
@@ -63,5 +63,7 @@
}
DLL_EXPORT void chreHeapFree(void *ptr) {
- chre::EventLoopManagerSingleton::get()->getMemoryManager().nanoappFree(ptr);
+ chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+ chre::EventLoopManagerSingleton::get()->getMemoryManager().
+ nanoappFree(nanoapp, ptr);
}
diff --git a/platform/shared/include/chre/platform/shared/nanoapp_dso_util.h b/platform/shared/include/chre/platform/shared/nanoapp_dso_util.h
new file mode 100644
index 0000000..bd9d0eb
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/nanoapp_dso_util.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_NANOAPP_DSO_UTIL_H_
+#define CHRE_PLATFORM_NANOAPP_DSO_UTIL_H_
+
+#include "chre/platform/shared/nanoapp_support_lib_dso.h"
+
+namespace chre {
+
+/**
+ * Performs sanity checks on the app info structure included in a dynamically
+ * loaded nanoapp.
+ *
+ * @param expectedAppId The app ID passed alongside the binary
+ * @param expectedAppVersion The app version number passed alongside the binary
+ * @param appInfo App info structure included in the nanoapp binary
+ * @param skipVersionValidation if true, ignore the expectedAppVersion parameter
+ *
+ * @return true if validation was successful
+ */
+bool validateAppInfo(uint64_t expectedAppId, uint32_t expectedAppVersion,
+ const struct chreNslNanoappInfo *appInfo,
+ bool skipVersionValidation = false);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_NANOAPP_DSO_UTIL_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 e824927..6617879 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
@@ -40,7 +40,7 @@
#define CHRE_NSL_NANOAPP_INFO_MAGIC UINT32_C(0x50e69977)
//! The minor version in the nanoapp info structure helps identify whether
-#define CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION UINT8_C(0)
+#define CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION UINT8_C(1)
//! The symbol name expected from the nanoapp's definition of its info struct
#define CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME "_chreNslDsoNanoappInfo"
@@ -65,9 +65,16 @@
//! functionality beneath the HAL.
uint8_t isSystemNanoapp:1;
+ //! Set to 1 if this nanoapp runs in tightly coupled memory. This flag is only
+ //! relevant to platforms that have the ability to run nanoapps within tightly
+ //! coupled memory.
+ //!
+ //! @since minor version 1
+ uint8_t isTcmNanoapp:1;
+
//! Reserved for future use, set to 0. Assignment of this field to some use
//! must be accompanied by an increase of the struct minor version.
- uint8_t reservedFlags:7;
+ uint8_t reservedFlags:6;
uint8_t reserved;
//! The CHRE API version that the nanoapp was compiled against
diff --git a/platform/shared/include/chre/platform/shared/pal_system_api.h b/platform/shared/include/chre/platform/shared/pal_system_api.h
index a5217e8..0413d83 100644
--- a/platform/shared/include/chre/platform/shared/pal_system_api.h
+++ b/platform/shared/include/chre/platform/shared/pal_system_api.h
@@ -18,6 +18,20 @@
namespace chre {
+/**
+ * Memory allocation coming from the PAL. The semantics are the same as malloc.
+ * This function needs to be implemented by the platform, and is not provided by
+ * the shared code.
+ */
+void *palSystemApiMemoryAlloc(size_t size);
+
+/**
+ * Memory free coming from the PAL. The semantics are the same as free.
+ * This function needs to be implemented by the platform, and is not provided by
+ * the shared code.
+ */
+void palSystemApiMemoryFree(void *pointer);
+
//! Provides a global instance of the PAL system API for all PAL subsystems to
//! leverage.
extern const chrePalSystemApi gChrePalSystemApi;
diff --git a/platform/shared/include/chre/platform/shared/platform_pal.h b/platform/shared/include/chre/platform/shared/platform_pal.h
new file mode 100644
index 0000000..d3f004b
--- /dev/null
+++ b/platform/shared/include/chre/platform/shared/platform_pal.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_SHARED_PLATFORM_PAL_H_
+#define CHRE_PLATFORM_SHARED_PLATFORM_PAL_H_
+
+namespace chre {
+
+/**
+ * Provides an instance of the PlatformPal class that uses the CHRE PAL.
+ */
+class PlatformPal {
+ protected:
+ /**
+ * Routine to be performed before any call to a platform PAL API.
+ */
+ void prePalApiCall();
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SHARED_PLATFORM_PAL_H_
diff --git a/platform/shared/include/chre/target_platform/platform_gnss_base.h b/platform/shared/include/chre/target_platform/platform_gnss_base.h
index 9031694..e5267d9 100644
--- a/platform/shared/include/chre/target_platform/platform_gnss_base.h
+++ b/platform/shared/include/chre/target_platform/platform_gnss_base.h
@@ -17,6 +17,7 @@
#ifndef CHRE_PLATFORM_SHARED_PLATFORM_GNSS_BASE_H_
#define CHRE_PLATFORM_SHARED_PLATFORM_GNSS_BASE_H_
+#include "chre/platform/shared/platform_pal.h"
#include "chre/pal/gnss.h"
namespace chre {
@@ -25,7 +26,7 @@
* Provides an instance of the PlatformGnssBase class that uses the CHRE PAL to
* access the wifi subsystem.
*/
-class PlatformGnssBase {
+class PlatformGnssBase : public PlatformPal {
protected:
//! The instance of the CHRE PAL API. This will be set to nullptr if the
//! platform does not supply an implementation.
diff --git a/platform/shared/include/chre/target_platform/platform_wifi_base.h b/platform/shared/include/chre/target_platform/platform_wifi_base.h
index a2e919c..09b166d 100644
--- a/platform/shared/include/chre/target_platform/platform_wifi_base.h
+++ b/platform/shared/include/chre/target_platform/platform_wifi_base.h
@@ -17,6 +17,7 @@
#ifndef CHRE_PLATFORM_SHARED_PLATFORM_WIFI_BASE_H_
#define CHRE_PLATFORM_SHARED_PLATFORM_WIFI_BASE_H_
+#include "chre/platform/shared/platform_pal.h"
#include "chre/pal/wifi.h"
namespace chre {
@@ -25,7 +26,7 @@
* Provides an instance of the PlatformWifiBase class that uses the CHRE PAL to
* access the wifi subsystem.
*/
-class PlatformWifiBase {
+class PlatformWifiBase : public PlatformPal {
protected:
//! The instance of the CHRE PAL API. This will be set to nullptr if the
//! platform does not supply an implementation.
diff --git a/platform/shared/include/chre/target_platform/platform_wwan_base.h b/platform/shared/include/chre/target_platform/platform_wwan_base.h
index a6c0648..f45684b 100644
--- a/platform/shared/include/chre/target_platform/platform_wwan_base.h
+++ b/platform/shared/include/chre/target_platform/platform_wwan_base.h
@@ -17,6 +17,7 @@
#ifndef CHRE_PLATFORM_SHARED_PLATFORM_WWAN_BASE_H_
#define CHRE_PLATFORM_SHARED_PLATFORM_WWAN_BASE_H_
+#include "chre/platform/shared/platform_pal.h"
#include "chre/pal/wwan.h"
namespace chre {
@@ -25,7 +26,7 @@
* Provides an instance of the PlatformWwanBase class that uses the CHRE PAL to
* access the WWAN subsystem.
*/
-class PlatformWwanBase {
+class PlatformWwanBase : public PlatformPal {
protected:
//! The instance of the CHRE PAL API. This will be set to nullptr if the
//! platform does not supply an implementation.
diff --git a/core/memory_manager.cc b/platform/shared/memory_manager.cc
similarity index 89%
rename from core/memory_manager.cc
rename to platform/shared/memory_manager.cc
index ed18539..810ae10 100644
--- a/core/memory_manager.cc
+++ b/platform/shared/memory_manager.cc
@@ -14,14 +14,12 @@
* limitations under the License.
*/
-#include "chre/core/memory_manager.h"
+#include "chre/platform/memory_manager.h"
+
#include "chre/util/system/debug_dump.h"
namespace chre {
-MemoryManager::MemoryManager()
- : mTotalAllocatedBytes(0), mAllocationCount(0) {}
-
void *MemoryManager::nanoappAlloc(Nanoapp *app, uint32_t bytes) {
AllocHeader *header = nullptr;
if (bytes > 0) {
@@ -33,7 +31,7 @@
": not enough space.", app->getInstanceId());
} else {
header = static_cast<AllocHeader*>(
- chre::memoryAlloc(sizeof(AllocHeader) + bytes));
+ doAlloc(app, sizeof(AllocHeader) + bytes));
if (header != nullptr) {
mTotalAllocatedBytes += bytes;
@@ -47,7 +45,7 @@
return header;
}
-void MemoryManager::nanoappFree(void *ptr) {
+void MemoryManager::nanoappFree(Nanoapp *app, void *ptr) {
if (ptr != nullptr) {
AllocHeader *header = static_cast<AllocHeader*>(ptr);
header--;
@@ -61,7 +59,7 @@
mAllocationCount--;
}
- memoryFree(header);
+ doFree(app, header);
}
}
diff --git a/platform/shared/nanoapp/nanoapp_dso_util.cc b/platform/shared/nanoapp/nanoapp_dso_util.cc
new file mode 100644
index 0000000..5b48976
--- /dev/null
+++ b/platform/shared/nanoapp/nanoapp_dso_util.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/platform/shared/nanoapp_dso_util.h"
+
+#include <cinttypes>
+#include <cstring>
+#include <chre/version.h>
+
+#include "chre/platform/log.h"
+
+namespace chre {
+
+bool validateAppInfo(uint64_t expectedAppId, uint32_t expectedAppVersion,
+ const struct chreNslNanoappInfo *appInfo,
+ bool skipVersionValidation) {
+ uint32_t ourApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(chreGetApiVersion());
+ uint32_t targetApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(
+ appInfo->targetApiVersion);
+
+ bool success = false;
+ if (appInfo->magic != CHRE_NSL_NANOAPP_INFO_MAGIC) {
+ LOGE("Invalid app info magic: got 0x%08" PRIx32 " expected 0x%08" PRIx32,
+ appInfo->magic, static_cast<uint32_t>(CHRE_NSL_NANOAPP_INFO_MAGIC));
+ } else if (appInfo->appId == 0) {
+ LOGE("Rejecting invalid app ID 0");
+ } else if (expectedAppId != 0 && expectedAppId != appInfo->appId) {
+ LOGE("Expected app ID (0x%016" PRIx64 ") doesn't match internal one (0x%016"
+ PRIx64 ")", expectedAppId, appInfo->appId);
+ } else if (!skipVersionValidation
+ && expectedAppVersion != appInfo->appVersion) {
+ LOGE("Expected app version (0x%" PRIx32 ") doesn't match internal one (0x%"
+ PRIx32 ")", expectedAppVersion, appInfo->appVersion);
+ } else if (targetApiMajorVersion != ourApiMajorVersion) {
+ LOGE("App targets a different major API version (%" PRIu32 ") than what we "
+ "provide (%" PRIu32 ")", targetApiMajorVersion, ourApiMajorVersion);
+ } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
+ LOGE("App name is too long");
+ } else if (strlen(appInfo->vendor) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
+ LOGE("App vendor is too long");
+ } else {
+ success = true;
+ }
+
+ return success;
+}
+
+} // namespace chre
diff --git a/platform/shared/nanoapp/nanoapp_support_lib_dso.c b/platform/shared/nanoapp/nanoapp_support_lib_dso.c
index 00b0331..d35c2ab 100644
--- a/platform/shared/nanoapp/nanoapp_support_lib_dso.c
+++ b/platform/shared/nanoapp/nanoapp_support_lib_dso.c
@@ -28,6 +28,12 @@
* implement cross-version compatibility features as needed.
*/
+#ifdef CHRE_SLPI_UIMG_ENABLED
+static const int kIsTcmNanoapp = 1;
+#else
+static const int kIsTcmNanoapp = 0;
+#endif // CHRE_SLPI_UIMG_ENABLED
+
DLL_EXPORT const struct chreNslNanoappInfo _chreNslDsoNanoappInfo = {
.magic = CHRE_NSL_NANOAPP_INFO_MAGIC,
.structMinorVersion = CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION,
@@ -37,6 +43,7 @@
.vendor = NANOAPP_VENDOR_STRING,
.name = NANOAPP_NAME_STRING,
.isSystemNanoapp = NANOAPP_IS_SYSTEM_NANOAPP,
+ .isTcmNanoapp = kIsTcmNanoapp,
.appId = NANOAPP_ID,
.appVersion = NANOAPP_VERSION,
diff --git a/platform/shared/pal_system_api.cc b/platform/shared/pal_system_api.cc
index e80a5e9..f633264 100644
--- a/platform/shared/pal_system_api.cc
+++ b/platform/shared/pal_system_api.cc
@@ -59,14 +59,6 @@
}
}
-void *palSystemApiMemoryAlloc(size_t size) {
- return memoryAlloc(size);
-}
-
-void palSystemApiMemoryFree(void *pointer) {
- memoryFree(pointer);
-}
-
// Initialize the CHRE System API with function implementations provided above.
const chrePalSystemApi gChrePalSystemApi = {
CHRE_PAL_SYSTEM_API_CURRENT_VERSION, /* version */
diff --git a/platform/shared/platform_gnss.cc b/platform/shared/platform_gnss.cc
index 36298ad..3c0a561 100644
--- a/platform/shared/platform_gnss.cc
+++ b/platform/shared/platform_gnss.cc
@@ -27,12 +27,14 @@
PlatformGnss::~PlatformGnss() {
if (mGnssApi != nullptr) {
LOGD("Platform GNSS closing");
+ prePalApiCall();
mGnssApi->close();
LOGD("Platform GNSS closed");
}
}
void PlatformGnss::init() {
+ prePalApiCall();
mGnssApi = chrePalGnssGetApi(CHRE_PAL_GNSS_API_CURRENT_VERSION);
if (mGnssApi != nullptr) {
mGnssCallbacks.requestStateResync =
@@ -57,6 +59,7 @@
uint32_t PlatformGnss::getCapabilities() {
if (mGnssApi != nullptr) {
+ prePalApiCall();
return mGnssApi->getCapabilities();
} else {
return CHRE_GNSS_CAPABILITIES_NONE;
@@ -66,6 +69,7 @@
bool PlatformGnss::controlLocationSession(bool enable, Milliseconds minInterval,
Milliseconds minTimeToNextFix) {
if (mGnssApi != nullptr) {
+ prePalApiCall();
return mGnssApi->controlLocationSession(enable,
static_cast<uint32_t>(minInterval.getMilliseconds()),
static_cast<uint32_t>(minTimeToNextFix.getMilliseconds()));
@@ -76,6 +80,7 @@
void PlatformGnss::releaseLocationEvent(chreGnssLocationEvent *event) {
if (mGnssApi != nullptr) {
+ prePalApiCall();
mGnssApi->releaseLocationEvent(event);
}
}
diff --git a/platform/shared/platform_wifi.cc b/platform/shared/platform_wifi.cc
index e366a78..398f26c 100644
--- a/platform/shared/platform_wifi.cc
+++ b/platform/shared/platform_wifi.cc
@@ -27,12 +27,14 @@
PlatformWifi::~PlatformWifi() {
if (mWifiApi != nullptr) {
LOGD("Platform WiFi closing");
+ prePalApiCall();
mWifiApi->close();
LOGD("Platform WiFi closed");
}
}
void PlatformWifi::init() {
+ prePalApiCall();
mWifiApi = chrePalWifiGetApi(CHRE_PAL_WIFI_API_CURRENT_VERSION);
if (mWifiApi != nullptr) {
mWifiCallbacks.scanMonitorStatusChangeCallback =
@@ -53,6 +55,7 @@
uint32_t PlatformWifi::getCapabilities() {
if (mWifiApi != nullptr) {
+ prePalApiCall();
return mWifiApi->getCapabilities();
} else {
return CHRE_WIFI_CAPABILITIES_NONE;
@@ -61,6 +64,7 @@
bool PlatformWifi::configureScanMonitor(bool enable) {
if (mWifiApi != nullptr) {
+ prePalApiCall();
return mWifiApi->configureScanMonitor(enable);
} else {
return false;
@@ -69,6 +73,7 @@
bool PlatformWifi::requestScan(const struct chreWifiScanParams *params) {
if (mWifiApi != nullptr) {
+ prePalApiCall();
return mWifiApi->requestScan(params);
} else {
return false;
@@ -77,6 +82,7 @@
void PlatformWifi::releaseScanEvent(struct chreWifiScanEvent *event) {
if (mWifiApi != nullptr) {
+ prePalApiCall();
mWifiApi->releaseScanEvent(event);
}
}
diff --git a/platform/shared/platform_wwan.cc b/platform/shared/platform_wwan.cc
index f107936..f958a2f 100644
--- a/platform/shared/platform_wwan.cc
+++ b/platform/shared/platform_wwan.cc
@@ -27,12 +27,14 @@
PlatformWwan::~PlatformWwan() {
if (mWwanApi != nullptr) {
LOGD("Platform WWAN closing");
+ prePalApiCall();
mWwanApi->close();
LOGD("Platform WWAN closed");
}
}
void PlatformWwan::init() {
+ prePalApiCall();
mWwanApi = chrePalWwanGetApi(CHRE_PAL_WWAN_API_CURRENT_VERSION);
if (mWwanApi != nullptr) {
mWwanCallbacks.cellInfoResultCallback =
@@ -49,6 +51,7 @@
uint32_t PlatformWwan::getCapabilities() {
if (mWwanApi != nullptr) {
+ prePalApiCall();
return mWwanApi->getCapabilities();
} else {
return CHRE_WWAN_CAPABILITIES_NONE;
@@ -57,6 +60,7 @@
bool PlatformWwan::requestCellInfo() {
if (mWwanApi != nullptr) {
+ prePalApiCall();
return mWwanApi->requestCellInfo();
} else {
return false;
@@ -65,6 +69,7 @@
void PlatformWwan::releaseCellInfoResult(chreWwanCellInfoResult *result) {
if (mWwanApi != nullptr) {
+ prePalApiCall();
mWwanApi->releaseCellInfoResult(result);
}
}
diff --git a/platform/slpi/chre_api_re.cc b/platform/slpi/chre_api_re.cc
new file mode 100644
index 0000000..b0adccc
--- /dev/null
+++ b/platform/slpi/chre_api_re.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ash/debug.h"
+#include "chre_api/chre/re.h"
+#include "chre/util/macros.h"
+
+DLL_EXPORT void chreLog(enum chreLogLevel level, const char *formatStr, ...) {
+ enum ashLogLevel ashLevel;
+ va_list args;
+
+ switch (level) {
+ case CHRE_LOG_ERROR:
+ ashLevel = ASH_LOG_ERROR;
+ break;
+ case CHRE_LOG_WARN:
+ ashLevel = ASH_LOG_WARN;
+ break;
+ case CHRE_LOG_INFO:
+ ashLevel = ASH_LOG_INFO;
+ break;
+ case CHRE_LOG_DEBUG:
+ default:
+ ashLevel = ASH_LOG_DEBUG;
+ }
+
+ va_start(args, formatStr);
+ ashVaLog(ASH_SOURCE_CHRE, ashLevel, formatStr, args);
+ va_end(args);
+}
diff --git a/platform/slpi/host_link.cc b/platform/slpi/host_link.cc
index 0b9f007..ce37382 100644
--- a/platform/slpi/host_link.cc
+++ b/platform/slpi/host_link.cc
@@ -25,6 +25,7 @@
#include "chre/platform/shared/host_protocol_chre.h"
#include "chre/platform/shared/platform_log.h"
#include "chre/platform/slpi/fastrpc.h"
+#include "chre/platform/slpi/power_control_util.h"
#include "chre/platform/slpi/system_time.h"
#include "chre/util/fixed_size_blocking_queue.h"
#include "chre/util/macros.h"
@@ -142,6 +143,28 @@
}
/**
+ * Wrapper function to enqueue a message on the outbound message queue. All
+ * outgoing message to the host must be called through this function.
+ *
+ * @param message The message to send to host.
+ *
+ * @return true if the message was successfully added to the queue.
+ */
+bool enqueueMessage(PendingMessage message) {
+ // Vote for big image temporarily when waking up the main thread waiting for
+ // the message
+ bool voteSuccess = slpiForceBigImage();
+ bool success = gOutboundQueue.push(message);
+
+ // Remove the vote only if we successfully made a big image transition
+ if (voteSuccess) {
+ slpiRemoveBigImageVote();
+ }
+
+ return success;
+}
+
+/**
* Helper function that takes care of the boilerplate for allocating a
* FlatBufferBuilder on the heap and adding it to the outbound message queue.
*
@@ -169,7 +192,7 @@
// TODO: if this fails, ideally we should block for some timeout until
// there's space in the queue
- if (!gOutboundQueue.push(PendingMessage(msgType, builder.get()))) {
+ if (!enqueueMessage(PendingMessage(msgType, builder.get()))) {
LOGE("Couldn't push message type %d to outbound queue",
static_cast<int>(msgType));
} else {
@@ -237,10 +260,13 @@
startedSuccessfully);
};
+ // Re-wrap the callback data struct, so it is destructed and freed, ensuring
+ // we don't leak the embedded UniquePtr<Nanoapp>
+ UniquePtr<LoadNanoappCallbackData> dataWrapped(
+ static_cast<LoadNanoappCallbackData *>(data));
constexpr size_t kInitialBufferSize = 48;
buildAndEnqueueMessage(PendingMessageType::LoadNanoappResponse,
kInitialBufferSize, msgBuilder, data);
- memoryFree(data);
}
void handleUnloadNanoappCallback(uint16_t /*eventType*/, void *data) {
@@ -511,7 +537,7 @@
}
bool HostLink::sendMessage(const MessageToHost *message) {
- return gOutboundQueue.push(
+ return enqueueMessage(
PendingMessage(PendingMessageType::NanoappMessageToHost, message));
}
@@ -535,7 +561,7 @@
// a state where it's not blocked in chre_slpi_get_message_to_host().
int retryCount = 5;
FARF(MEDIUM, "Shutting down host link");
- while (!gOutboundQueue.push(PendingMessage(PendingMessageType::Shutdown))
+ while (!enqueueMessage(PendingMessage(PendingMessageType::Shutdown))
&& --retryCount > 0) {
qurt_timer_sleep(kPollingIntervalUsec);
}
@@ -567,7 +593,7 @@
}
void requestHostLinkLogBufferFlush() {
- if (!gOutboundQueue.push(PendingMessage(PendingMessageType::LogMessage))) {
+ if (!enqueueMessage(PendingMessage(PendingMessageType::LogMessage))) {
// Use FARF as there is a problem sending logs to the host.
FARF(ERROR, "Failed to enqueue log flush");
CHRE_ASSERT(false);
@@ -590,7 +616,7 @@
void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) {
// We generate the response in the context of chre_slpi_get_message_to_host
LOGD("Got hub info request from client ID %" PRIu16, hostClientId);
- gOutboundQueue.push(PendingMessage(
+ enqueueMessage(PendingMessage(
PendingMessageType::HubInfoResponse, hostClientId));
}
diff --git a/platform/slpi/include/chre/platform/slpi/memory.h b/platform/slpi/include/chre/platform/slpi/memory.h
new file mode 100644
index 0000000..9a535d1
--- /dev/null
+++ b/platform/slpi/include/chre/platform/slpi/memory.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_SLPI_MEMORY_H_
+#define CHRE_PLATFORM_SLPI_MEMORY_H_
+
+#include <cstddef>
+
+namespace chre {
+
+/**
+ * Memory allocation specifically using the big image heap.
+ * The semantics are the same as malloc.
+ */
+void *memoryAllocBigImage(size_t size);
+
+/**
+ * Memory free from memory allocated using the big image heap.
+ * The semantics are the same as free.
+ */
+void memoryFreeBigImage(void *pointer);
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SLPI_MEMORY_H_
diff --git a/platform/slpi/include/chre/platform/slpi/power_control_util.h b/platform/slpi/include/chre/platform/slpi/power_control_util.h
new file mode 100644
index 0000000..870b1c8
--- /dev/null
+++ b/platform/slpi/include/chre/platform/slpi/power_control_util.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_POWER_CONTROL_UTIL_H
+#define CHRE_PLATFORM_POWER_CONTROL_UTIL_H
+
+extern "C" {
+
+#include "qurt_island.h"
+
+} // extern "C"
+
+#include "chre/core/event_loop_manager.h"
+#include "chre/platform/slpi/uimg_util.h"
+
+namespace chre {
+
+/**
+ * @return true if we're currently running in micro-image, aka island mode.
+ */
+inline bool slpiInUImage() {
+ return (qurt_island_get_status() == 1);
+}
+
+/**
+ * @return true if the successfully made a micro to big image transition.
+ */
+inline bool slpiForceBigImage() {
+ bool success = false;
+ if (isSlpiUimgSupported() && slpiInUImage()) {
+ success = EventLoopManagerSingleton::get()->getEventLoop().
+ getPowerControlManager().votePowerMode(SNS_IMG_MODE_BIG);
+ }
+
+ return success;
+}
+
+/**
+ * Removes a big image vote from CHRE. Should only be called when the system is
+ * idle.
+ *
+ * @return true if the vote succeeds.
+ */
+inline bool slpiRemoveBigImageVote() {
+ return EventLoopManagerSingleton::get()->getEventLoop().
+ getPowerControlManager().votePowerMode(SNS_IMG_MODE_NOCLIENT);
+}
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_POWER_CONTROL_UTIL_H
diff --git a/platform/slpi/include/chre/platform/slpi/smgr_client.h b/platform/slpi/include/chre/platform/slpi/smgr_client.h
index f0fd90e..2c3d116 100644
--- a/platform/slpi/include/chre/platform/slpi/smgr_client.h
+++ b/platform/slpi/include/chre/platform/slpi/smgr_client.h
@@ -17,18 +17,35 @@
#ifndef CHRE_PLATFORM_SLPI_SMGR_CLIENT_H_
#define CHRE_PLATFORM_SLPI_SMGR_CLIENT_H_
-extern "C" {
+#include "chre/platform/slpi/smr_helper.h"
+#include "chre/util/singleton.h"
-#include "qmi_client.h"
-
-} // extern "C"
+/**
+ * @file
+ * Exposes the SMR helper and SMGR (non-internal) client handle used by the
+ * platform sensor implementation, for use in other modules.
+ */
namespace chre {
+//! A singleton instance of SmrHelper that can be used for making synchronous
+//! sensor requests while remaining in micro-image. This must only be used from
+//! the CHRE thread.
+typedef Singleton<SmrHelper> SmrHelperSingleton;
+
/**
- * @return A QMI sensor service client handle
+ * Convenience method for fetching the SMR helper singleton instance. Must only
+ * be used from the CHRE thread.
*/
-qmi_client_type getSensorServiceQmiClientHandle();
+inline SmrHelper *getSmrHelper() {
+ return SmrHelperSingleton::get();
+}
+
+/**
+ * @return The SMR client handle to the SMGR (non-internal) API, created by the
+ * SLPI platform-specific sensors implementation
+ */
+smr_client_hndl getSensorServiceSmrClientHandle();
} // namespace chre
diff --git a/platform/slpi/include/chre/platform/slpi/smr_helper.h b/platform/slpi/include/chre/platform/slpi/smr_helper.h
new file mode 100644
index 0000000..a076501
--- /dev/null
+++ b/platform/slpi/include/chre/platform/slpi/smr_helper.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_SLPI_SMR_HELPER_H_
+#define CHRE_PLATFORM_SLPI_SMR_HELPER_H_
+
+#include <type_traits>
+
+extern "C" {
+
+#include "qurt.h"
+#include "sns_usmr.h"
+
+}
+
+#include "chre/platform/condition_variable.h"
+#include "chre/platform/mutex.h"
+#include "chre/util/non_copyable.h"
+#include "chre/util/time.h"
+#include "chre/util/unique_ptr.h"
+
+namespace chre {
+
+//! Default timeout for sendReqSync
+constexpr Nanoseconds kDefaultSmrTimeout = Seconds(1);
+
+//! Default timeout for waitForService. Have a longer timeout since there may be
+//! external dependencies blocking SMGR initialization.
+constexpr Nanoseconds kDefaultSmrWaitTimeout = Seconds(5);
+
+/**
+ * A helper class for making synchronous requests to SMR (Sensors Message
+ * Router). Not safe to use from multiple threads.
+ */
+class SmrHelper : public NonCopyable {
+ public:
+ /**
+ * Wrapper to convert the async smr_client_release() to a synchronous call.
+ *
+ * @param clientHandle SMR handle to release
+ * @param timeout How long to wait for the response before abandoning it
+ *
+ * @return Result code returned by smr_client_release(), or SMR_TIMEOUT_ERR if
+ * the timeout was reached
+ */
+ smr_err releaseSync(smr_client_hndl clientHandle,
+ Nanoseconds timeout = kDefaultSmrTimeout);
+
+ /**
+ * Wrapper to convert the async smr_client_send_req() to a synchronous call.
+ *
+ * Only one request can be pending at a time per instance of SmrHelper.
+ *
+ * @param ReqStruct QMI IDL-generated request structure
+ * @param RespStruct QMI IDL-generated response structure
+ * @param clientHandle SMR handle previously given by smr_client_init()
+ * @param msgId QMI message ID of the request to send
+ * @param req Pointer to populated request structure
+ * @param resp Pointer to structure to receive the response
+ * @param timeout How long to wait for the response before abandoning it
+ *
+ * @return Result code returned by smr_client_send_req(), or SMR_TIMEOUT_ERR
+ * if the supplied timeout was reached
+ */
+ template<typename ReqStruct, typename RespStruct>
+ smr_err sendReqSync(
+ smr_client_hndl clientHandle, unsigned int msgId,
+ UniquePtr<ReqStruct> *req, UniquePtr<RespStruct> *resp,
+ Nanoseconds timeout = kDefaultSmrTimeout) {
+ // Try to catch copy/paste errors at compile time - QMI always has a
+ // different struct definition for request and response
+ static_assert(!std::is_same<ReqStruct, RespStruct>::value,
+ "Request and response structures must be different");
+
+ smr_err result;
+ bool timedOut = !sendReqSyncUntyped(
+ clientHandle, msgId, req->get(), sizeof(ReqStruct),
+ resp->get(), sizeof(RespStruct), timeout, &result);
+
+ // Unlike QMI, SMR does not support canceling an in-flight transaction.
+ // SMR's internal request structure maintains a pointer to the client
+ // request and response buffers, so in the event of a timeout, it is unsafe
+ // for us to free the memory because the service may try to send the
+ // response later on.
+ if (timedOut) {
+ req->release();
+ resp->release();
+ }
+
+ return result;
+ }
+
+ /**
+ * Wrapper to convert the async smr_client_check_ext() to a synchronous call.
+ * Waits for an SMR service to become available.
+ *
+ * @param serviceObj The SMR service object to wait for.
+ * @param timeout The wait timeout in microseconds.
+ *
+ * @return Result code returned by smr_client_check_ext, or SMR_TIMEOUT_ERR if
+ * the timeout was reached
+ */
+ smr_err waitForService(qmi_idl_service_object_type serviceObj,
+ Microseconds timeout = kDefaultSmrWaitTimeout);
+
+ private:
+ /**
+ * Implements sendReqSync(), but with accepting untyped (void*) buffers.
+ * snake_case parameters exactly match those given to smr_client_send_req().
+ *
+ * @param timeout How long to wait for the response before abandoning it
+ * @param result If smr_client_send_req() returns an error, then that error
+ * code, otherwise the transport error code given in the SMR response
+ * callback (assuming there was no timeout)
+ * @return false on timeout, otherwise true (includes the case where
+ * smr_client_send_req() returns an immediate error)
+ *
+ * @see sendReqSync()
+ * @see smr_client_send_req()
+ */
+ bool sendReqSyncUntyped(
+ smr_client_hndl client_handle, unsigned int msg_id,
+ void *req_c_struct, unsigned int req_c_struct_len,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ Nanoseconds timeout, smr_err *result);
+
+
+ /**
+ * Processes an SMR response callback
+ *
+ * @see smr_client_resp_cb
+ */
+ void handleResp(smr_client_hndl client_handle, unsigned int msg_id,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ smr_err transp_err);
+
+ /**
+ * SMR release complete callback used with releaseSync()
+ *
+ * @see smr_client_release_cb
+ */
+ static void smrReleaseCb(void *release_cb_data);
+
+ /**
+ * Extracts "this" from resp_cb_data and calls through to handleResp()
+ *
+ * @see smr_client_resp_cb
+ */
+ static void smrRespCb(smr_client_hndl client_handle, unsigned int msg_id,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ void *resp_cb_data, smr_err transp_err);
+
+ /**
+ * SMR wait for service callback used with waitForService()
+ *
+ * @see smr_client_init_ext_cb
+ */
+ static void smrWaitForServiceCb(qmi_idl_service_object_type service_obj,
+ qmi_service_instance instance_id,
+ bool timeout_expired,
+ void *wait_for_service_cb_data);
+
+ ConditionVariable mCond;
+ Mutex mMutex;
+
+ //! true if we are waiting on an async response
+ bool mWaiting = false;
+
+ //! While waiting on a response, set to the response buffer given to
+ //! sendReqSync()
+ void *mPendingRespBuf = nullptr;
+
+ //! true if timed out while waiting for a service to become available
+ bool mServiceTimedOut = false;
+
+ //! The (transport) error code given in the response callback
+ smr_err mTranspErr;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SLPI_SMR_HELPER_H_
diff --git a/platform/shared/memory.cc b/platform/slpi/include/chre/platform/slpi/uimg_util.h
similarity index 61%
copy from platform/shared/memory.cc
copy to platform/slpi/include/chre/platform/slpi/uimg_util.h
index c137eb8..01e0e38 100644
--- a/platform/shared/memory.cc
+++ b/platform/slpi/include/chre/platform/slpi/uimg_util.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
-
-#include <stdlib.h>
+#ifndef CHRE_PLATFORM_SLPI_UIMG_UTIL_H_
+#define CHRE_PLATFORM_SLPI_UIMG_UTIL_H_
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
-}
-
-void memoryFree(void *pointer) {
- free(pointer);
+/**
+ * @return true if CHRE on micro-image is supported
+ */
+constexpr bool isSlpiUimgSupported() {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ return true;
+#else
+ return false;
+#endif // CHRE_SLPI_UIMG_ENABLED
}
} // namespace chre
+
+#endif // CHRE_PLATFORM_SLPI_UIMG_UTIL_H_
diff --git a/platform/slpi/include/chre/target_platform/condition_variable_base.h b/platform/slpi/include/chre/target_platform/condition_variable_base.h
index bbbf514..6189cff 100644
--- a/platform/slpi/include/chre/target_platform/condition_variable_base.h
+++ b/platform/slpi/include/chre/target_platform/condition_variable_base.h
@@ -23,12 +23,20 @@
} // extern "C"
+#include "chre/platform/system_timer.h"
+
namespace chre {
class ConditionVariableBase {
protected:
//! The underlying QURT condition variable.
qurt_cond_t mConditionVariable;
+
+ //! The timer used for timed condition variable wait.
+ SystemTimer mTimeoutTimer;
+
+ //! Set to true when the timeout timer is initialized.
+ bool mTimerInitialized = false;
};
} // namespace chre
diff --git a/platform/slpi/include/chre/target_platform/condition_variable_impl.h b/platform/slpi/include/chre/target_platform/condition_variable_impl.h
index 603304b..2097233 100644
--- a/platform/slpi/include/chre/target_platform/condition_variable_impl.h
+++ b/platform/slpi/include/chre/target_platform/condition_variable_impl.h
@@ -19,6 +19,9 @@
#include "chre/platform/condition_variable.h"
+#include "chre/platform/fatal_error.h"
+#include "chre/platform/log.h"
+
namespace chre {
inline ConditionVariable::ConditionVariable() {
@@ -37,6 +40,43 @@
qurt_cond_wait(&mConditionVariable, &mutex.mMutex);
}
+// Note: The wait_for function is designed to work for a single thread waiting
+// on the condition variable.
+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;
+ }
+ }
+
+ struct TimeoutCallbackData {
+ ConditionVariable *cvPtr;
+ bool timedOut;
+ };
+ auto callback = [](void *data) {
+ auto cbData = static_cast<TimeoutCallbackData*>(data);
+ cbData->timedOut = true;
+ cbData->cvPtr->notify_one();
+ };
+
+ TimeoutCallbackData callbackData;
+ callbackData.cvPtr = this;
+ callbackData.timedOut = false;
+ if (!mTimeoutTimer.set(callback, &callbackData, timeout)) {
+ LOGE("Failed to set condition variable timer");
+ }
+
+ wait(mutex);
+ if (mTimeoutTimer.isActive()) {
+ if (!mTimeoutTimer.cancel()) {
+ LOGD("Failed to cancel condition variable timer");
+ }
+ }
+ return !callbackData.timedOut;
+}
+
} // namespace chre
#endif // CHRE_PLATFORM_SLPI_CONDITION_VARIABLE_IMPL_H_
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 9750e88..8b6ed8e 100644
--- a/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h
+++ b/platform/slpi/include/chre/target_platform/platform_nanoapp_base.h
@@ -70,6 +70,11 @@
*/
bool isLoaded() const;
+ /**
+ * @return true if the app runs in micro-image.
+ */
+ bool isUimgApp() const;
+
protected:
//! The app ID we received in the metadata alongside the nanoapp binary. This
//! is also included in (and checked against) mAppInfo.
@@ -100,6 +105,9 @@
//! applicable.
bool mIsStatic = false;
+ //! True if the nanoapp runs in micro-image.
+ bool mIsUimgApp = false;
+
/**
* Calls through to openNanoappFromBuffer or openNanoappFromFile, depending on
* how this nanoapp was loaded.
diff --git a/platform/slpi/include/chre/target_platform/power_control_manager_base.h b/platform/slpi/include/chre/target_platform/power_control_manager_base.h
new file mode 100644
index 0000000..bb27c23
--- /dev/null
+++ b/platform/slpi/include/chre/target_platform/power_control_manager_base.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
+#define CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
+
+extern "C" {
+
+#include "qurt.h"
+#include "sns_pm.h"
+
+} // extern "C"
+
+namespace chre {
+
+class PowerControlManagerBase {
+ public:
+ PowerControlManagerBase();
+
+ ~PowerControlManagerBase();
+
+ /**
+ * Votes for a power mode to the SLPI power manager.
+ *
+ * @param mode The power mode to vote for.
+ *
+ * @return true if the vote returned SNS_PM_SUCCESS.
+ */
+ bool votePowerMode(sns_pm_img_mode_e mode);
+
+ protected:
+ //! Client handle for the subscription to the power manager
+ sns_pm_handle_t mClientHandle = nullptr;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_POWER_CONTROL_MANAGER_BASE_H
diff --git a/platform/slpi/include/chre/target_platform/static_nanoapp_init.h b/platform/slpi/include/chre/target_platform/static_nanoapp_init.h
index 4567955..4b2e1f5 100644
--- a/platform/slpi/include/chre/target_platform/static_nanoapp_init.h
+++ b/platform/slpi/include/chre/target_platform/static_nanoapp_init.h
@@ -20,6 +20,7 @@
#include "chre/core/static_nanoapps.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/shared/nanoapp_support_lib_dso.h"
+#include "chre/platform/slpi/uimg_util.h"
/**
* Initializes a static nanoapp that is based on the SLPI implementation of
@@ -43,6 +44,7 @@
appInfo.vendor = "Google"; /* TODO: make this configurable */\
appInfo.name = #appName; \
appInfo.isSystemNanoapp = true; \
+ appInfo.isTcmNanoapp = isSlpiUimgSupported(); \
appInfo.appId = appId_; \
appInfo.appVersion = appVersion_; \
appInfo.entryPoints.start = nanoappStart; \
diff --git a/platform/slpi/include/chre/target_platform/system_timer_base.h b/platform/slpi/include/chre/target_platform/system_timer_base.h
index 37eca4d..fcc39a7 100644
--- a/platform/slpi/include/chre/target_platform/system_timer_base.h
+++ b/platform/slpi/include/chre/target_platform/system_timer_base.h
@@ -19,11 +19,38 @@
extern "C" {
-// TODO: Investigate switching to utimer.h. The symbols are not currently
-// exported by the static image. I have tested a static image with utimer
-// symbols exported but an SLPI crash occurs when the callback is invoked.
+#ifdef CHRE_SLPI_UIMG_ENABLED
+#include "utimer.h"
+
+typedef utimer_type SlpiTimerHandle;
+typedef utimer_cb_data_type SlpiTimerCallbackDataType;
+typedef utimer_timetick_type SlpiTimerTickType;
+typedef utimer_error_type SlpiTimerErrorType;
+
+#define SlpiTimerTickUnit UT_TICK
+#define SlpiTimerMicroUnit UT_USEC
+#define SLPI_TIMER_SUCCESS UTE_SUCCESS
+#define slpiTimerClr64 utimer_clr_64
+#define slpiTimerGet64 utimer_get_64
+#define slpiTimerSet64 utimer_set_64
+#define slpiTimerUndef utimer_undef
+#else
#include "timer.h"
+typedef timer_type SlpiTimerHandle;
+typedef timer_cb_data_type SlpiTimerCallbackDataType;
+typedef time_timetick_type SlpiTimerTickType;
+typedef timer_error_type SlpiTimerErrorType;
+
+#define SlpiTimerTickUnit T_TICK
+#define SlpiTimerMicroUnit T_USEC
+#define SLPI_TIMER_SUCCESS TE_SUCCESS
+#define slpiTimerClr64 timer_clr_64
+#define slpiTimerGet64 timer_get_64
+#define slpiTimerSet64 timer_set_64
+#define slpiTimerUndef timer_undef
+#endif // CHRE_SLPI_UIMG_ENABLED
+
} // extern "C"
namespace chre {
@@ -31,13 +58,13 @@
class SystemTimerBase {
public:
//! The underlying QURT timer.
- timer_type mTimerHandle;
+ SlpiTimerHandle mTimerHandle;
//! Tracks whether the timer has been initialized correctly.
bool mInitialized = false;
//! A static method that is invoked by the underlying QURT timer.
- static void systemTimerNotifyCallback(timer_cb_data_type data);
+ static void systemTimerNotifyCallback(SlpiTimerCallbackDataType data);
};
} // namespace chre
diff --git a/platform/slpi/init.cc b/platform/slpi/init.cc
index 5adc9ad..cd0e8ab 100644
--- a/platform/slpi/init.cc
+++ b/platform/slpi/init.cc
@@ -35,6 +35,7 @@
#include "chre/platform/shared/platform_log.h"
#include "chre/platform/slpi/fastrpc.h"
#include "chre/platform/slpi/preloaded_nanoapps.h"
+#include "chre/platform/slpi/uimg_util.h"
#include "chre/util/lock_guard.h"
using chre::EventLoop;
@@ -57,9 +58,10 @@
constexpr size_t kStackSize = (8 * 1024);
//! Memory partition where the thread control block (TCB) should be stored,
-//! which controls micro-image support (0 = big image, 1 = micro image).
+//! which controls micro-image support.
//! @see qurt_thread_attr_set_tcb_partition
-constexpr unsigned char kTcbPartition = 0;
+constexpr unsigned char kTcbPartition = chre::isSlpiUimgSupported() ?
+ QURT_THREAD_ATTR_TCB_PARTITION_TCM : QURT_THREAD_ATTR_TCB_PARTITION_RAM;
//! The priority to set for the CHRE thread (value between 1-255, with 1 being
//! the highest).
@@ -78,9 +80,7 @@
//! Protects access to thread metadata, like gThreadRunning, during critical
//! sections (starting/stopping the CHRE thread).
-Mutex *gThreadMutex;
-typename std::aligned_storage<sizeof(Mutex), alignof(Mutex)>::type
- gThreadMutexStorage;
+Mutex gThreadMutex;
//! Set to true when the CHRE thread starts, and false when it exits normally.
bool gThreadRunning;
@@ -90,22 +90,14 @@
int gTlsKey;
bool gTlsKeyValid;
-// TODO: We would prefer to just use static global C++ constructor/destructor
-// support, but currently, destructors do not seem to get called. These work as
-// a temporary workaround, though.
__attribute__((constructor))
void onLoad(void) {
// Initialize the platform logging as early as possible.
chre::PlatformLogSingleton::init();
-
- gThreadMutex = new(&gThreadMutexStorage) Mutex();
}
__attribute__((destructor))
void onUnload(void) {
- gThreadMutex->~Mutex();
- gThreadMutex = nullptr;
-
// Defer platform logging deinitialization to as late as possible.
chre::PlatformLogSingleton::deinit();
}
@@ -168,7 +160,7 @@
*/
extern "C" int chre_slpi_start_thread(void) {
// This lock ensures that we only start the thread once
- LockGuard<Mutex> lock(*gThreadMutex);
+ LockGuard<Mutex> lock(gThreadMutex);
int fastRpcResult = CHRE_FASTRPC_ERROR;
if (gThreadRunning) {
@@ -238,7 +230,7 @@
extern "C" int chre_slpi_stop_thread(void) {
// This lock ensures that we will complete shutdown before the thread can be
// started again
- LockGuard<Mutex> lock(*gThreadMutex);
+ LockGuard<Mutex> lock(gThreadMutex);
if (!gThreadRunning) {
LOGD("Tried to stop CHRE thread, but not running");
@@ -287,7 +279,7 @@
* @return 0 on success, nonzero on failure (per FastRPC requirements)
*/
extern "C" int chre_slpi_initialize_reverse_monitor(void) {
- LockGuard<Mutex> lock(*gThreadMutex);
+ LockGuard<Mutex> lock(gThreadMutex);
if (!gTlsKeyValid) {
int result = qurt_tls_create_key(&gTlsKey, onHostProcessTerminated);
diff --git a/platform/slpi/memory.cc b/platform/slpi/memory.cc
new file mode 100644
index 0000000..98a8913
--- /dev/null
+++ b/platform/slpi/memory.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/platform/memory.h"
+#include "chre/platform/slpi/memory.h"
+
+extern "C" {
+
+#include "qurt.h"
+#include "sns_memmgr.h"
+
+} // extern "C"
+
+namespace chre {
+
+void *memoryAlloc(size_t size) {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ return SNS_OS_U_MALLOC(SNS_CHRE, size);
+#else
+ return malloc(size);
+#endif // CHRE_SLPI_UIMG_ENABLED
+}
+
+void *memoryAllocBigImage(size_t size) {
+ return malloc(size);
+}
+
+void *palSystemApiMemoryAlloc(size_t size) {
+ return malloc(size);
+}
+
+void memoryFree(void *pointer) {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ SNS_OS_FREE(pointer);
+#else
+ free(pointer);
+#endif // CHRE_SLPI_UIMG_ENABLED
+}
+
+void memoryFreeBigImage(void *pointer) {
+ free(pointer);
+}
+
+void palSystemApiMemoryFree(void *pointer) {
+ free(pointer);
+}
+
+} // namespace chre
diff --git a/platform/slpi/memory_manager.cc b/platform/slpi/memory_manager.cc
new file mode 100644
index 0000000..ad5eed0
--- /dev/null
+++ b/platform/slpi/memory_manager.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/platform/memory_manager.h"
+
+#include "chre/platform/slpi/memory.h"
+#include "chre/util/memory.h"
+
+namespace chre {
+
+void *MemoryManager::doAlloc(Nanoapp *app, uint32_t bytes) {
+ if (app->isUimgApp()) {
+ return chre::memoryAlloc(bytes);
+ } else {
+ return chre::memoryAllocBigImage(bytes);
+ }
+}
+
+void MemoryManager::doFree(Nanoapp *app, void *ptr) {
+ if (app->isUimgApp()) {
+ chre::memoryFree(ptr);
+ } else {
+ chre::memoryFreeBigImage(ptr);
+ }
+}
+
+} // namespace chre
diff --git a/platform/slpi/platform_nanoapp.cc b/platform/slpi/platform_nanoapp.cc
index d8f6c87..f241840 100644
--- a/platform/slpi/platform_nanoapp.cc
+++ b/platform/slpi/platform_nanoapp.cc
@@ -16,10 +16,14 @@
#include "chre/platform/platform_nanoapp.h"
+#include "chre/core/event_loop_manager.h"
#include "chre/platform/assert.h"
#include "chre/platform/log.h"
#include "chre/platform/memory.h"
+#include "chre/platform/shared/nanoapp_dso_util.h"
#include "chre/platform/shared/nanoapp_support_lib_dso.h"
+#include "chre/platform/slpi/memory.h"
+#include "chre/platform/slpi/power_control_util.h"
#include "chre/util/system/debug_dump.h"
#include "chre_api/chre/version.h"
@@ -30,74 +34,37 @@
namespace chre {
-namespace {
-
-/**
- * Performs sanity checks on the app info structure included in a dynamically
- * loaded nanoapp.
- *
- * @param expectedAppId The app ID passed alongside the binary
- * @param expectedAppVersion The app version number passed alongside the binary
- * @param appInfo App info structure included in the nanoapp binary
- * @param skipVersionValidation if true, ignore the expectedAppVersion parameter
- *
- * @return true if validation was successful
- */
-bool validateAppInfo(uint64_t expectedAppId, uint32_t expectedAppVersion,
- const struct chreNslNanoappInfo *appInfo,
- bool skipVersionValidation = false) {
- uint32_t ourApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(chreGetApiVersion());
- uint32_t targetApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(
- appInfo->targetApiVersion);
-
- bool success = false;
- if (appInfo->magic != CHRE_NSL_NANOAPP_INFO_MAGIC) {
- LOGE("Invalid app info magic: got 0x%08" PRIx32 " expected 0x%08" PRIx32,
- appInfo->magic, static_cast<uint32_t>(CHRE_NSL_NANOAPP_INFO_MAGIC));
- } else if (appInfo->appId == 0) {
- LOGE("Rejecting invalid app ID 0");
- } else if (expectedAppId != appInfo->appId) {
- LOGE("Expected app ID (0x%016" PRIx64 ") doesn't match internal one (0x%016"
- PRIx64 ")", expectedAppId, appInfo->appId);
- } else if (!skipVersionValidation
- && expectedAppVersion != appInfo->appVersion) {
- LOGE("Expected app version (0x%" PRIx32 ") doesn't match internal one (0x%"
- PRIx32 ")", expectedAppVersion, appInfo->appVersion);
- } else if (targetApiMajorVersion != ourApiMajorVersion) {
- LOGE("App targets a different major API version (%" PRIu32 ") than what we "
- "provide (%" PRIu32 ")", targetApiMajorVersion, ourApiMajorVersion);
- } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
- LOGE("App name is too long");
- } else if (strlen(appInfo->vendor) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) {
- LOGE("App vendor is too long");
- } else {
- success = true;
- }
-
- return success;
-}
-
-} // anonymous namespace
-
PlatformNanoapp::~PlatformNanoapp() {
closeNanoapp();
if (mAppBinary != nullptr) {
- memoryFree(mAppBinary);
+ memoryFreeBigImage(mAppBinary);
}
}
bool PlatformNanoapp::start() {
// Invoke the start entry point after successfully opening the app
- return openNanoapp() ? mAppInfo->entryPoints.start() : false;
+ if (!isUimgApp()) {
+ slpiForceBigImage();
+ }
+
+ return openNanoapp() && mAppInfo->entryPoints.start();
}
void PlatformNanoapp::handleEvent(uint32_t senderInstanceId,
uint16_t eventType,
const void *eventData) {
+ if (!isUimgApp()) {
+ slpiForceBigImage();
+ }
+
mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
}
void PlatformNanoapp::end() {
+ if (!isUimgApp()) {
+ slpiForceBigImage();
+ }
+
mAppInfo->entryPoints.end();
closeNanoapp();
}
@@ -112,7 +79,7 @@
if (appBinaryLen > kMaxAppSize) {
LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
} else {
- mAppBinary = memoryAlloc(appBinaryLen);
+ mAppBinary = memoryAllocBigImage(appBinaryLen);
if (mAppBinary == nullptr) {
LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
appBinaryLen, appId);
@@ -141,15 +108,17 @@
}
bool PlatformNanoappBase::isLoaded() const {
- return (mIsStatic || mAppBinary != nullptr);
+ return (mIsStatic || mAppBinary != nullptr || mDsoHandle != nullptr);
+}
+
+bool PlatformNanoappBase::isUimgApp() const {
+ return mIsUimgApp;
}
void PlatformNanoappBase::closeNanoapp() {
if (mDsoHandle != nullptr) {
- mAppInfo = nullptr;
if (dlclose(mDsoHandle) != 0) {
- const char *name = (mAppInfo != nullptr) ? mAppInfo->name : "unknown";
- LOGE("dlclose of %s failed: %s", name, dlerror());
+ LOGE("dlclose failed: %s", dlerror());
}
mDsoHandle = nullptr;
}
@@ -168,6 +137,12 @@
CHRE_ASSERT(false);
}
+ // Save this flag locally since it may be referenced while the system is in
+ // micro-image
+ if (mAppInfo != nullptr) {
+ mIsUimgApp = mAppInfo->isTcmNanoapp;
+ }
+
return success;
}
@@ -197,7 +172,11 @@
mAppInfo = nullptr;
} else {
LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64 ") version 0x%"
- PRIx32, mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion);
+ PRIx32 " uimg %d system %d", mAppInfo->name, mAppInfo->appId,
+ mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
+ mAppInfo->isSystemNanoapp);
+ memoryFreeBigImage(mAppBinary);
+ mAppBinary = nullptr;
}
}
}
@@ -225,8 +204,9 @@
mAppInfo = nullptr;
} else {
LOGI("Successfully loaded nanoapp %s (0x%016" PRIx64 ") version 0x%"
- PRIx32 " from file %s", mAppInfo->name, mAppInfo->appId,
- mAppInfo->appVersion, mFilename);
+ PRIx32 " uimg %d system %d from file %s", mAppInfo->name,
+ mAppInfo->appId, mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
+ mAppInfo->isSystemNanoapp, mFilename);
// Save the app version field in case this app gets disabled and we
// still get a query request for the version later on. We are OK not
// knowing the version prior to the first load because we assume that
diff --git a/platform/shared/memory.cc b/platform/slpi/platform_pal.cc
similarity index 73%
copy from platform/shared/memory.cc
copy to platform/slpi/platform_pal.cc
index c137eb8..b7f41af 100644
--- a/platform/shared/memory.cc
+++ b/platform/slpi/platform_pal.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,18 +14,14 @@
* limitations under the License.
*/
-#include "chre/platform/memory.h"
+#include "chre/platform/shared/platform_pal.h"
-#include <stdlib.h>
+#include "chre/platform/slpi/power_control_util.h"
namespace chre {
-void *memoryAlloc(size_t size) {
- return malloc(size);
-}
-
-void memoryFree(void *pointer) {
- free(pointer);
+void PlatformPal::prePalApiCall() {
+ slpiForceBigImage();
}
} // namespace chre
diff --git a/platform/slpi/platform_sensor.cc b/platform/slpi/platform_sensor.cc
index d697b03..ea41757 100644
--- a/platform/slpi/platform_sensor.cc
+++ b/platform/slpi/platform_sensor.cc
@@ -22,9 +22,9 @@
extern "C" {
#include "fixed_point.h"
-#include "qmi_client.h"
#include "sns_smgr_api_v01.h"
#include "sns_smgr_internal_api_v02.h"
+#include "sns_usmr.h"
#include "timetick.h"
} // extern "C"
@@ -38,20 +38,47 @@
#include "chre/platform/system_time.h"
#include "chre/platform/slpi/platform_sensor_util.h"
#include "chre/platform/slpi/smgr_client.h"
+#include "chre/platform/slpi/smr_helper.h"
+#include "chre/platform/slpi/uimg_util.h"
#include "chre/util/macros.h"
-// TODO: [Passive] explain passive sensor design
+// As SMGR doesn't support passive sensor request, it's now implemented on the
+// client (CHRE) side using a combination of the SNS_SMGR_INTERNAL_API_V02 and a
+// modified SNS_SMGR_API_V01.
+//
+// Here's a summary of its design:
+// 1. A sensor status monitor is added in addSensorMonitor() to receive the
+// SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02 message the first time a sensor is
+// requested.
+// 2. When a request is made in PlatformSensor::applyRequest(), it checkes
+// whether it's allowed at that point and makes a corresponding QMI request.
+// 1) The request is allowed if
+// - it's an active or an off request, or
+// - it's a passive request and the merged mode (to be explained
+// shortly) is active or there exist other SMGR clients.
+// 2) If the request is allowed, a QMI request to add the sensor request is
+// made. Otherwise, a QMI request to remove the sensor request is made to
+// handle the potential active-and-allowed to passive-and-disallowed
+// transition.
+// 3) The merged mode of a sensor is the strongest mode of all sensor
+// requests of the same sensor ID, with active > passive > off.
+// 3. When SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02 from SMGR is received, a
+// SNS_SMGR_CLIENT_REQUEST_INFO_REQ_V01 message is sent to query SMGR on the
+// existence of other clients.
+// - If a transition from absence-to-presence of other clients is detected,
+// all pending passive requests are made.
+// - If a transition from presence-to-absence of other clients is deteted,
+// all passive requests are removed if the merged mode is passive.
+//
+// Note that currently the sensor status monitor indication only supports
+// primary sensor status change. So for a secondary sensor that can be requested
+// without an accompanying primary sensor (Light), this design doesn't work.
+// In PlatformSensor::applyRequest(), a passive Light sensor request is
+// overridden to be an active one.
namespace chre {
namespace {
-//! Timeout for QMI client initialization, in milliseconds. Allow more time here
-//! due to external dependencies that may block initialization of SMGR.
-constexpr uint32_t kQmiInitTimeoutMs = 5000;
-
-//! The timeout for QMI messages in milliseconds.
-constexpr uint32_t kQmiTimeoutMs = 1000;
-
//! The constant used to convert from SMGR to Android unit for magnetometer.
constexpr float kMicroTeslaPerGauss = 100.0f;
@@ -65,22 +92,13 @@
constexpr uint64_t kTickRolloverOffset =
((1ULL << 32) * Seconds(1).toRawNanoseconds()) / TIMETICK_NOMINAL_FREQ_HZ;
-//! The QMI sensor service client handle.
-qmi_client_type gPlatformSensorServiceQmiClientHandle = nullptr;
-
-//! The QMI sensor internal service client handle.
-qmi_client_type gPlatformSensorInternalServiceQmiClientHandle = nullptr;
-
-//! A sensor report indication for deserializing sensor sample indications
-//! into. This global instance is used to avoid thrashy use of the heap by
-//! allocating and freeing this on the heap for every new sensor sample. This
-//! relies on the assumption that the QMI callback is not reentrant.
-sns_smgr_buffering_ind_msg_v01 gSmgrBufferingIndMsg;
+smr_client_hndl gPlatformSensorServiceSmrClientHandle;
+smr_client_hndl gPlatformSensorInternalServiceSmrClientHandle;
//! A struct to store the number of SMGR clients of a sensor ID.
struct SensorMonitor {
uint8_t sensorId;
- uint8_t numClients;
+ bool otherClientPresent;
};
//! A vector that tracks the SensorMonitor of each supported sensor ID.
@@ -230,11 +248,12 @@
*
* @return true if it's a valid pair of indices length and report ID.
*/
-bool isValidIndicesLength() {
- return ((gSmgrBufferingIndMsg.Indices_len == 1
- && !isSecondaryTemperature(gSmgrBufferingIndMsg.ReportId))
- || (gSmgrBufferingIndMsg.Indices_len == 2
- && isSecondaryTemperature(gSmgrBufferingIndMsg.ReportId)));
+bool isValidIndicesLength(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg) {
+ return ((bufferingIndMsg.Indices_len == 1
+ && !isSecondaryTemperature(bufferingIndMsg.ReportId))
+ || (bufferingIndMsg.Indices_len == 2
+ && isSecondaryTemperature(bufferingIndMsg.ReportId)));
}
/**
@@ -345,13 +364,14 @@
}
void populateThreeAxisEvent(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
SensorType sensorType, chreSensorThreeAxisData *data,
const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
populateSensorDataHeader(sensorType, &data->header, sensorIndex);
for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
const sns_smgr_buffering_sample_s_v01& sensorData =
- gSmgrBufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
+ bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
// TimeStampOffset has max value of < 2 sec so it will not overflow here.
data->readings[i].timestampDelta =
@@ -373,13 +393,14 @@
}
void populateFloatEvent(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
SensorType sensorType, chreSensorFloatData *data,
const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
populateSensorDataHeader(sensorType, &data->header, sensorIndex);
for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
const sns_smgr_buffering_sample_s_v01& sensorData =
- gSmgrBufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
+ bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
// TimeStampOffset has max value of < 2 sec so it will not overflow.
data->readings[i].timestampDelta =
@@ -389,13 +410,14 @@
}
void populateByteEvent(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
SensorType sensorType, chreSensorByteData *data,
const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
populateSensorDataHeader(sensorType, &data->header, sensorIndex);
for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
const sns_smgr_buffering_sample_s_v01& sensorData =
- gSmgrBufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
+ bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
// TimeStampOffset has max value of < 2 sec so it will not overflow.
data->readings[i].timestampDelta =
@@ -408,13 +430,14 @@
}
void populateOccurrenceEvent(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
SensorType sensorType, chreSensorOccurrenceData *data,
const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
populateSensorDataHeader(sensorType, &data->header, sensorIndex);
for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
const sns_smgr_buffering_sample_s_v01& sensorData =
- gSmgrBufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
+ bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];
// TimeStampOffset has max value of < 2 sec so it will not overflow.
data->readings[i].timestampDelta =
@@ -426,6 +449,7 @@
* Allocate event memory according to SensorType and populate event readings.
*/
void *allocateAndPopulateEvent(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
SensorType sensorType,
const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
SensorSampleType sampleType = getSensorSampleTypeFromSensorType(sensorType);
@@ -437,7 +461,7 @@
auto *event =
static_cast<chreSensorThreeAxisData *>(memoryAlloc(memorySize));
if (event != nullptr) {
- populateThreeAxisEvent(sensorType, event, sensorIndex);
+ populateThreeAxisEvent(bufferingIndMsg, sensorType, event, sensorIndex);
}
return event;
}
@@ -448,7 +472,7 @@
auto *event =
static_cast<chreSensorFloatData *>(memoryAlloc(memorySize));
if (event != nullptr) {
- populateFloatEvent(sensorType, event, sensorIndex);
+ populateFloatEvent(bufferingIndMsg, sensorType, event, sensorIndex);
}
return event;
}
@@ -459,7 +483,7 @@
auto *event =
static_cast<chreSensorByteData *>(memoryAlloc(memorySize));
if (event != nullptr) {
- populateByteEvent(sensorType, event, sensorIndex);
+ populateByteEvent(bufferingIndMsg, sensorType, event, sensorIndex);
}
return event;
}
@@ -470,7 +494,8 @@
auto *event =
static_cast<chreSensorOccurrenceData *>(memoryAlloc(memorySize));
if (event != nullptr) {
- populateOccurrenceEvent(sensorType, event, sensorIndex);
+ populateOccurrenceEvent(
+ bufferingIndMsg, sensorType, event, sensorIndex);
}
return event;
}
@@ -553,89 +578,83 @@
/**
* Handles sensor data provided by the SMGR framework.
*
- * @param userHandle The userHandle is used by the QMI decode function.
- * @param buffer The buffer to decode sensor data from.
- * @param bufferLength The size of the buffer to decode.
+ * @param bufferingIndMsg Decoded buffering indication message
*/
-void handleSensorDataIndication(void *userHandle, void *buffer,
- unsigned int bufferLength) {
- int status = qmi_client_message_decode(
- userHandle, QMI_IDL_INDICATION, SNS_SMGR_BUFFERING_IND_V01, buffer,
- bufferLength, &gSmgrBufferingIndMsg,
- sizeof(sns_smgr_buffering_ind_msg_v01));
- if (status != QMI_NO_ERR) {
- LOGE("Error parsing sensor data indication %d", status);
- } else {
- // We only requested one sensor per request except for a secondary
- // temperature sensor.
- bool validReport = isValidIndicesLength();
- CHRE_ASSERT_LOG(validReport,
- "Got buffering indication from %" PRIu32
- " sensors with report ID %" PRIu8,
- gSmgrBufferingIndMsg.Indices_len,
- gSmgrBufferingIndMsg.ReportId);
- if (validReport) {
- // Identify the index for the desired sensor. It is always 0 except
- // possibly for a secondary temperature sensor.
- uint32_t index = 0;
- if (isSecondaryTemperature(gSmgrBufferingIndMsg.ReportId)) {
- index = (gSmgrBufferingIndMsg.Indices[0].DataType
- == SNS_SMGR_DATA_TYPE_SECONDARY_V01) ? 0 : 1;
- }
- const sns_smgr_buffering_sample_index_s_v01& sensorIndex =
- gSmgrBufferingIndMsg.Indices[index];
+void handleSensorDataIndication(
+ const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg) {
+ // We only requested one sensor per request except for a secondary
+ // temperature sensor.
+ bool validReport = isValidIndicesLength(bufferingIndMsg);
+ CHRE_ASSERT_LOG(validReport,
+ "Got buffering indication from %" PRIu32
+ " sensors with report ID %" PRIu8,
+ bufferingIndMsg.Indices_len,
+ bufferingIndMsg.ReportId);
+ if (validReport) {
+ // Identify the index for the desired sensor. It is always 0 except
+ // possibly for a secondary temperature sensor.
+ uint32_t index = 0;
+ if (isSecondaryTemperature(bufferingIndMsg.ReportId)) {
+ index = (bufferingIndMsg.Indices[0].DataType
+ == SNS_SMGR_DATA_TYPE_SECONDARY_V01) ? 0 : 1;
+ }
+ const sns_smgr_buffering_sample_index_s_v01& sensorIndex =
+ bufferingIndMsg.Indices[index];
- // Use ReportID to identify sensors as
- // gSmgrBufferingIndMsg.Samples[i].Flags are not populated.
- SensorType sensorType = getSensorTypeFromReportId(
- gSmgrBufferingIndMsg.ReportId);
- if (sensorType == SensorType::Unknown) {
- LOGW("Received sensor sample for unknown sensor %" PRIu8 " %" PRIu8,
- sensorIndex.SensorId, sensorIndex.DataType);
- } else if (sensorIndex.SampleCount == 0) {
- LOGW("Received sensorType %d event with 0 sample",
- static_cast<int>(sensorType));
+ // Use ReportID to identify sensors as
+ // bufferingIndMsg.Samples[i].Flags are not populated.
+ SensorType sensorType = getSensorTypeFromReportId(
+ bufferingIndMsg.ReportId);
+ if (sensorType == SensorType::Unknown) {
+ LOGW("Received sensor sample for unknown sensor %" PRIu8 " %" PRIu8,
+ sensorIndex.SensorId, sensorIndex.DataType);
+ } else if (sensorIndex.SampleCount == 0) {
+ LOGW("Received sensorType %d event with 0 sample",
+ static_cast<int>(sensorType));
+ } else {
+ void *eventData = allocateAndPopulateEvent(
+ bufferingIndMsg, sensorType, sensorIndex);
+ if (eventData == nullptr) {
+ LOGW("Dropping event due to allocation failure");
} else {
- void *eventData = allocateAndPopulateEvent(sensorType, sensorIndex);
- if (eventData == nullptr) {
- LOGW("Dropping event due to allocation failure");
- } else {
- // Schedule a deferred callback to update on-change sensor's last
- // event in the main thread.
- if (sensorTypeIsOnChange(sensorType)) {
- updateLastEvent(sensorType, eventData);
- }
-
- EventLoopManagerSingleton::get()->getEventLoop().postEvent(
- getSampleEventTypeForSensorType(sensorType), eventData,
- smgrSensorDataEventFree);
+ // Schedule a deferred callback to update on-change sensor's last
+ // event in the main thread.
+ if (sensorTypeIsOnChange(sensorType)) {
+ updateLastEvent(sensorType, eventData);
}
+
+ EventLoopManagerSingleton::get()->getEventLoop().postEvent(
+ getSampleEventTypeForSensorType(sensorType), eventData,
+ smgrSensorDataEventFree);
}
- } // if (validReport)
- }
+ }
+ } // if (validReport)
}
/**
- * This callback is invoked by the QMI framework when an asynchronous message is
- * delivered. Unhandled messages are logged. The signature is defined by the QMI
- * library.
+ * This callback is invoked by the SMR framework when an asynchronous message is
+ * delivered. Unhandled messages are logged.
*
- * @param userHandle The userHandle is used by the QMI library.
- * @param messageId The type of the message to decode.
- * @param buffer The buffer to decode.
- * @param bufferLength The length of the buffer to decode.
+ * @param handle Handle for the SMR client this indication was received on.
+ * @param messageId The message ID number.
+ * @param buffer Buffer containing decoded (C struct) message data.
+ * @param bufferLength Size of the decoded buffer in bytes.
* @param callbackData Data that is provided as a context to this callback. This
* is not used in this context.
+ *
+ * @see smr_client_ind_cb
*/
-void platformSensorServiceQmiIndicationCallback(void *userHandle,
- unsigned int messageId,
- void *buffer,
- unsigned int bufferLength,
- void *callbackData) {
+void platformSensorServiceIndicationCallback(
+ smr_client_hndl handle, unsigned int messageId, void *decodedInd,
+ unsigned int decodedIndLen, void *callbackData) {
switch (messageId) {
- case SNS_SMGR_BUFFERING_IND_V01:
- handleSensorDataIndication(userHandle, buffer, bufferLength);
+ case SNS_SMGR_BUFFERING_IND_V01: {
+ CHRE_ASSERT(decodedIndLen >= sizeof(sns_smgr_buffering_ind_msg_v01));
+ auto *bufferingInd =
+ static_cast<sns_smgr_buffering_ind_msg_v01 *>(decodedInd);
+ handleSensorDataIndication(*bufferingInd);
break;
+ }
default:
LOGW("Received unhandled sensor service message: 0x%x", messageId);
break;
@@ -688,30 +707,6 @@
}
/**
- * Obtains the number of SMGR clients of a sensor ID that were originated by
- * CHRE.
- *
- * @param sensorId The sensor ID as provided by the SMGR.
- * @return The number of CHRE clients.
- */
-size_t getNumChreClients(uint8_t sensorId) {
- size_t numChreClients = 0;
-
- // Identify sensor types of the specified sensor ID
- SensorType sensorTypes[kMaxNumSensorsPerSensorId];
- size_t numSensorTypes = populateSensorTypeArrayFromSensorId(
- sensorId, sensorTypes);
- for (size_t i = 0; i < numSensorTypes; i++) {
- const Sensor *sensor = EventLoopManagerSingleton::get()
- ->getSensorRequestManager().getSensor(sensorTypes[i]);
- if (sensor != nullptr && !sensor->isSensorOff) {
- numChreClients++;
- }
- }
- return numChreClients;
-}
-
-/**
* Obtains the merged SensorMode of the specified sensor ID, with sensorType's
* sensor request replaced by the supplied request.
*
@@ -742,33 +737,30 @@
}
/**
- * Makes or removes passive sensor requests when the number of SMGR clients
- * changes.
+ * Makes or removes passive sensor requests when the presence of other SMGR
+ * clients changes.
*
* @param sensorID The sensor ID being monitored.
- * @param prevNumClients The previous number of SMGR clients.
- * @param currNumclients The current number of SMGR clients.
+ * @param otherClientPresent The presence of other SMGR clients.
*/
-void onNumSmgrClientsChange(uint8_t sensorId, uint8_t prevNumClients,
- uint8_t currNumClients) {
- bool makeAllRequests = (prevNumClients == 0 && currNumClients > 0);
+void onOtherClientPresenceChange(uint8_t sensorId, bool otherClientPresent) {
+ bool makeAllRequests = otherClientPresent;
+
SensorRequest dummyRequest;
SensorMode mode = getMergedMode(sensorId, SensorType::Unknown, dummyRequest);
- bool removeAllRequests = (sensorModeIsPassive(mode)
- && currNumClients < prevNumClients
- && currNumClients == getNumChreClients(sensorId)
- && currNumClients > 0);
- bool qmiRequestMade = false;
+ bool removeAllRequests = (sensorModeIsPassive(mode) && !otherClientPresent);
+
+ bool requestMade = false;
if (makeAllRequests) {
- qmiRequestMade = makeAllPendingRequests(sensorId);
+ requestMade = makeAllPendingRequests(sensorId);
} else if (removeAllRequests) {
- qmiRequestMade = removeAllPassiveRequests(sensorId);
+ requestMade = removeAllPassiveRequests(sensorId);
}
- if (qmiRequestMade) {
- LOGD("%s: id %" PRIu8 ", prev %" PRIu8 " curr %" PRIu8 ", mode %d, chre %d",
- makeAllRequests ? "+" : "-", sensorId, prevNumClients, currNumClients,
- static_cast<size_t>(mode), getNumChreClients(sensorId));
+ if (requestMade) {
+ LOGD("%s: id %" PRIu8 ", otherClientPresent %d, mode %d",
+ makeAllRequests ? "+" : "-", sensorId, otherClientPresent,
+ static_cast<size_t>(mode));
}
}
@@ -783,10 +775,38 @@
LOGE("Sensor status monitor update of invalid sensor ID %" PRIu64,
status.sensor_id);
} else {
- uint8_t numClients = gSensorMonitors[index].numClients;
- if (numClients != status.num_clients) {
- onNumSmgrClientsChange(status.sensor_id, numClients, status.num_clients);
- gSensorMonitors[index].numClients = status.num_clients;
+ // Use asynchronous sensor status monitor indication message as a cue to
+ // query and obtain the synchronous client request info.
+ // As the info conveyed in the indication is asynchronous from current
+ // status and is insufficient to determine other clients' status, relying on
+ // it to enable/disable passive sensors may put the system in an incorrect
+ // state or lead to a request loop.
+ auto infoRequest =
+ MakeUniqueZeroFill<sns_smgr_client_request_info_req_msg_v01>();
+ auto infoResponse = MakeUnique<sns_smgr_client_request_info_resp_msg_v01>();
+
+ if (infoRequest.isNull() || infoResponse.isNull()) {
+ LOGE("Failed to allocate client request info message");
+ } else {
+ infoRequest->sensor_id = status.sensor_id;
+
+ smr_err smrStatus = getSmrHelper()->sendReqSync(
+ gPlatformSensorServiceSmrClientHandle,
+ SNS_SMGR_CLIENT_REQUEST_INFO_REQ_V01,
+ &infoRequest, &infoResponse);
+
+ if (smrStatus != SMR_NO_ERR) {
+ LOGE("Error requesting client request info: %d", smrStatus);
+ } else if (infoResponse->resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
+ LOGE("Client request info failed with error: %" PRIu8 ", id %" PRIu8,
+ infoResponse->resp.sns_err_t, infoRequest->sensor_id);
+ }
+
+ bool otherClientPresent = infoResponse->other_client_present;
+ if (gSensorMonitors[index].otherClientPresent != otherClientPresent) {
+ onOtherClientPresenceChange(status.sensor_id, otherClientPresent);
+ gSensorMonitors[index].otherClientPresent = otherClientPresent;
+ }
}
}
}
@@ -817,11 +837,11 @@
}
/**
- * Updates the sampling status after the QMI sensor request is accepted by SMGR.
+ * Updates the sampling status after the sensor request is accepted by SMGR.
*/
void updateSamplingStatus(Sensor *sensor, const SensorRequest& request) {
// With SMGR's implementation, sampling interval will be filtered to be the
- // same as requeted. Latency can be shorter if there were other SMGR clients
+ // same as requested. Latency can be shorter if there were other SMGR clients
// with proc_type also set to SNS_PROC_SSC_V01.
// If the request is passive, 'enabled' may change over time and needs to be
// updated.
@@ -864,63 +884,58 @@
/**
* Handles sensor status provided by the SMGR framework.
*
- * @param userHandle The userHandle is used by the QMI decode function.
- * @param buffer The buffer to decode sensor data from.
- * @param bufferLength The size of the buffer to decode.
+ * @param smgrMonitorIndMsg Indication message received from SMGR
*/
-void handleSensorStatusMonitorIndication(void *userHandle, void *buffer,
- unsigned int bufferLength) {
- sns_smgr_sensor_status_monitor_ind_msg_v02 smgrMonitorIndMsg;
-
- int status = qmi_client_message_decode(
- userHandle, QMI_IDL_INDICATION, SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02,
- buffer, bufferLength, &smgrMonitorIndMsg, sizeof(smgrMonitorIndMsg));
- if (status != QMI_NO_ERR) {
- LOGE("Error parsing sensor status monitor indication %d", status);
+void handleSensorStatusMonitorIndication(
+ const sns_smgr_sensor_status_monitor_ind_msg_v02& smgrMonitorIndMsg) {
+ auto *callbackData =
+ memoryAlloc<sns_smgr_sensor_status_monitor_ind_msg_v02>();
+ if (callbackData == nullptr) {
+ LOGE("Failed to allocate status update deferred callback memory");
} else {
- auto *callbackData = memoryAlloc<
- sns_smgr_sensor_status_monitor_ind_msg_v02>();
- if (callbackData == nullptr) {
- LOGE("Failed to allocate status update deferred callback memory");
- } else {
- *callbackData = smgrMonitorIndMsg;
- auto callback = [](uint16_t /* type */, void *data) {
- auto *cbData =
- static_cast<sns_smgr_sensor_status_monitor_ind_msg_v02 *>(data);
- onStatusChange(*cbData);
- memoryFree(cbData);
- };
+ *callbackData = smgrMonitorIndMsg;
+ auto callback = [](uint16_t /* type */, void *data) {
+ auto *cbData =
+ static_cast<sns_smgr_sensor_status_monitor_ind_msg_v02 *>(data);
+ onStatusChange(*cbData);
+ memoryFree(cbData);
+ };
- // Schedule a deferred callback to handle sensor status change in the main
- // thread.
- if (!EventLoopManagerSingleton::get()->deferCallback(
- SystemCallbackType::SensorStatusUpdate, callbackData, callback)) {
- LOGE("Failed to schedule a deferred callback for status update");
- memoryFree(callbackData);
- }
+ // Schedule a deferred callback to handle sensor status change in the main
+ // thread.
+ if (!EventLoopManagerSingleton::get()->deferCallback(
+ SystemCallbackType::SensorStatusUpdate, callbackData, callback)) {
+ LOGE("Failed to schedule a deferred callback for status update");
+ memoryFree(callbackData);
}
}
}
/**
- * This callback is invoked by the QMI framework when an asynchronous message is
- * delivered. Unhandled messages are logged. The signature is defined by the QMI
- * library.
+ * This callback is invoked by the SMR framework when an asynchronous message is
+ * delivered. Unhandled messages are logged.
*
- * @param userHandle The userHandle is used by the QMI library.
- * @param messageId The type of the message to decode.
- * @param buffer The buffer to decode.
- * @param bufferLength The length of the buffer to decode.
+ * @param handle Handle for the SMR client this indication was received on.
+ * @param messageId The message ID number.
+ * @param decodedInd Buffer containing decoded (C struct) message data.
+ * @param decodedIndLen Size of the decoded buffer in bytes.
* @param callbackData Data that is provided as a context to this callback. This
* is not used in this context.
+ *
+ * @see smr_client_ind_cb
*/
-void platformSensorInternalServiceQmiIndicationCallback(void *userHandle,
- unsigned int messageId, void *buffer, unsigned int bufferLength,
- void *callbackData) {
+void platformSensorInternalServiceIndicationCallback(
+ smr_client_hndl handle, unsigned int messageId, void *decodedInd,
+ unsigned int decodedIndLen, void *callbackData) {
switch (messageId) {
- case SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02:
- handleSensorStatusMonitorIndication(userHandle, buffer, bufferLength);
+ case SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02: {
+ CHRE_ASSERT(decodedIndLen >=
+ sizeof(sns_smgr_sensor_status_monitor_ind_msg_v02));
+ auto *monitorInd =
+ static_cast<sns_smgr_sensor_status_monitor_ind_msg_v02 *>(decodedInd);
+ handleSensorStatusMonitorIndication(*monitorInd);
break;
+ }
default:
LOGW("Received unhandled sensor internal service message: 0x%x",
messageId);
@@ -935,23 +950,28 @@
* @param enable true to add and false to remove the status monitor.
*/
void setSensorMonitorRequest(uint8_t sensorId, bool enable) {
- sns_smgr_sensor_status_monitor_req_msg_v02 monitorRequest;
- sns_smgr_sensor_status_monitor_resp_msg_v02 monitorResponse;
- monitorRequest.sensor_id = sensorId;
- monitorRequest.registering = enable ? TRUE : FALSE;
+ auto monitorRequest =
+ MakeUniqueZeroFill<sns_smgr_sensor_status_monitor_req_msg_v02>();
+ auto monitorResponse =
+ MakeUnique<sns_smgr_sensor_status_monitor_resp_msg_v02>();
- qmi_client_error_type status = qmi_client_send_msg_sync(
- gPlatformSensorInternalServiceQmiClientHandle,
- SNS_SMGR_SENSOR_STATUS_MONITOR_REQ_V02,
- &monitorRequest, sizeof(monitorRequest),
- &monitorResponse, sizeof(monitorResponse), kQmiTimeoutMs);
+ if (monitorRequest.isNull() || monitorResponse.isNull()) {
+ LOGE("Failed to allocate monitor request/response");
+ } else {
+ monitorRequest->sensor_id = sensorId;
+ monitorRequest->registering = enable;
- if (status != QMI_NO_ERR) {
- LOGE("Error setting sensor status monitor: %d", status);
- } else if (monitorResponse.resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
- LOGE("Sensor status monitor request failed with error: %" PRIu8
- " sensor ID %" PRIu8 " enable %d",
- monitorResponse.resp.sns_err_t, sensorId, enable);
+ smr_err status = getSmrHelper()->sendReqSync(
+ gPlatformSensorInternalServiceSmrClientHandle,
+ SNS_SMGR_SENSOR_STATUS_MONITOR_REQ_V02,
+ &monitorRequest, &monitorResponse);
+ if (status != SMR_NO_ERR) {
+ LOGE("Error setting sensor status monitor: %d", status);
+ } else if (monitorResponse->resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
+ LOGE("Sensor status monitor request failed with error: %" PRIu8
+ " sensor ID %" PRIu8 " enable %d",
+ monitorResponse->resp.sns_err_t, sensorId, enable);
+ }
}
}
@@ -966,13 +986,13 @@
if (index == gSensorMonitors.size()) {
LOGD("Adding sensor status monitor for sensor ID %" PRIu8, sensorId);
- // Initialize sensor monitor status before making a QMI request.
+ // Initialize sensor monitor status before making the request.
SensorMonitor monitor;
monitor.sensorId = sensorId;
- monitor.numClients = 0;
+ monitor.otherClientPresent = false;
gSensorMonitors.push_back(monitor);
- // Make a QMI request to add the status monitor
+ // Make a request to add the status monitor
setSensorMonitorRequest(sensorId, true);
}
}
@@ -987,51 +1007,58 @@
*/
bool getSensorsForSensorId(uint8_t sensorId,
DynamicVector<Sensor> *sensors) {
- sns_smgr_single_sensor_info_req_msg_v01 sensorInfoRequest;
- sns_smgr_single_sensor_info_resp_msg_v01 sensorInfoResponse;
-
- sensorInfoRequest.SensorID = sensorId;
-
- qmi_client_error_type status = qmi_client_send_msg_sync(
- gPlatformSensorServiceQmiClientHandle, SNS_SMGR_SINGLE_SENSOR_INFO_REQ_V01,
- &sensorInfoRequest, sizeof(sns_smgr_single_sensor_info_req_msg_v01),
- &sensorInfoResponse, sizeof(sns_smgr_single_sensor_info_resp_msg_v01),
- kQmiTimeoutMs);
-
bool success = false;
- if (status != QMI_NO_ERR) {
- LOGE("Error requesting single sensor info: %d", status);
- } else if (sensorInfoResponse.Resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
- LOGE("Single sensor info request failed with error: %d",
- sensorInfoResponse.Resp.sns_err_t);
+ auto sensorInfoRequest =
+ MakeUniqueZeroFill<sns_smgr_single_sensor_info_req_msg_v01>();
+ auto sensorInfoResponse =
+ MakeUnique<sns_smgr_single_sensor_info_resp_msg_v01>();
+
+ if (sensorInfoRequest.isNull() || sensorInfoResponse.isNull()) {
+ LOGE("Failed to allocate sensor info msg");
} else {
- const sns_smgr_sensor_info_s_v01& sensorInfoList =
- sensorInfoResponse.SensorInfo;
- for (uint32_t i = 0; i < sensorInfoList.data_type_info_len; i++) {
- const sns_smgr_sensor_datatype_info_s_v01& sensorInfo =
- sensorInfoList.data_type_info[i];
- LOGD("SensorID %" PRIu8 ", DataType %" PRIu8 ", MaxRate %" PRIu16
- "Hz, SensorName %s",
- sensorInfo.SensorID, sensorInfo.DataType,
- sensorInfo.MaxSampleRate, sensorInfo.SensorName);
+ sensorInfoRequest->SensorID = sensorId;
- SensorType sensorType = getSensorTypeFromSensorId(
- sensorInfo.SensorID, sensorInfo.DataType,
- SNS_SMGR_CAL_SEL_FULL_CAL_V01);
- if (sensorType != SensorType::Unknown) {
- addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FULL_CAL_V01, sensors);
+ smr_err status = getSmrHelper()->sendReqSync(
+ gPlatformSensorServiceSmrClientHandle,
+ SNS_SMGR_SINGLE_SENSOR_INFO_REQ_V01,
+ &sensorInfoRequest, &sensorInfoResponse);
- // Add an uncalibrated version if defined.
- SensorType uncalibratedType = getSensorTypeFromSensorId(
+ if (status != SMR_NO_ERR) {
+ LOGE("Error requesting single sensor info: %d", status);
+ } else if (sensorInfoResponse->Resp.sns_result_t !=
+ SNS_RESULT_SUCCESS_V01) {
+ LOGE("Single sensor info request failed with error: %d",
+ sensorInfoResponse->Resp.sns_err_t);
+ } else {
+ const sns_smgr_sensor_info_s_v01& sensorInfoList =
+ sensorInfoResponse->SensorInfo;
+ for (uint32_t i = 0; i < sensorInfoList.data_type_info_len; i++) {
+ const sns_smgr_sensor_datatype_info_s_v01& sensorInfo =
+ sensorInfoList.data_type_info[i];
+ LOGD("SensorID %" PRIu8 ", DataType %" PRIu8 ", MaxRate %" PRIu16
+ "Hz, SensorName %s",
+ sensorInfo.SensorID, sensorInfo.DataType,
+ sensorInfo.MaxSampleRate, sensorInfo.SensorName);
+
+ SensorType sensorType = getSensorTypeFromSensorId(
sensorInfo.SensorID, sensorInfo.DataType,
- SNS_SMGR_CAL_SEL_FACTORY_CAL_V01);
- if (sensorType != uncalibratedType) {
- addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FACTORY_CAL_V01, sensors);
+ SNS_SMGR_CAL_SEL_FULL_CAL_V01);
+ if (sensorType != SensorType::Unknown) {
+ addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FULL_CAL_V01, sensors);
+
+ // Add an uncalibrated version if defined.
+ SensorType uncalibratedType = getSensorTypeFromSensorId(
+ sensorInfo.SensorID, sensorInfo.DataType,
+ SNS_SMGR_CAL_SEL_FACTORY_CAL_V01);
+ if (sensorType != uncalibratedType) {
+ addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FACTORY_CAL_V01, sensors);
+ }
}
}
+ success = true;
}
- success = true;
}
+
return success;
}
@@ -1146,35 +1173,7 @@
}
/**
- * Obtains the number of SMGR clients that were not originated by CHRE.
- *
- * @param sensorId The sensorID as provided by the SMGR.
- * @return The number of non-CHRE clients.
- */
-size_t getNumNonChreClients(uint8_t sensorId) {
- size_t numChreClients = getNumChreClients(sensorId);
- size_t numSmgrClients = 0;
- size_t index = getSensorMonitorIndex(sensorId);
- if (index == gSensorMonitors.size()) {
- LOGE("Accessing sensor monitor with invalid sensorId %" PRIu8, sensorId);
- } else {
- numSmgrClients = gSensorMonitors[index].numClients;
- }
-
- size_t numNonChreClients = 0;
- if (numChreClients > numSmgrClients) {
- // SMGR status monitor indication may lag behind if back-to-back requests
- // are made.
- LOGW("numChreClients %zu > numSmgrClients %zu",
- numChreClients, numSmgrClients);
- } else {
- numNonChreClients = numSmgrClients - numChreClients;
- }
- return numNonChreClients;
-}
-
-/**
- * Determines whether a requet is allowed. A passive request is not always
+ * Determines whether a request is allowed. A passive request is not always
* allowed.
*
* @param sensorType The SensorType of this request
@@ -1187,22 +1186,29 @@
const Sensor *sensor = EventLoopManagerSingleton::get()
->getSensorRequestManager().getSensor(sensorType);
if (sensor != nullptr) {
- // If it's an ACTIVE or an OFF request, it's always allowed.
- allowed = true;
if (sensorModeIsPassive(request.getMode())) {
- size_t numNonChreClients = getNumNonChreClients(sensor->sensorId);
- SensorMode mode = getMergedMode(sensor->sensorId, sensorType, request);
- allowed = (numNonChreClients > 0 || sensorModeIsActive(mode));
- LOGD("sensorType %d allowed %d: mergedMode %d, numNonChreClients %zu",
- static_cast<size_t>(sensorType), allowed, static_cast<int>(mode),
- numNonChreClients);
+ size_t index = getSensorMonitorIndex(sensor->sensorId);
+ if (index == gSensorMonitors.size()) {
+ LOGE("SensorId %" PRIu8 " doesn't have a monitor", sensor->sensorId);
+ } else {
+ SensorMode mergedMode = getMergedMode(
+ sensor->sensorId, sensorType, request);
+ bool otherClientPresent = gSensorMonitors[index].otherClientPresent;
+ allowed = (sensorModeIsActive(mergedMode) || otherClientPresent);
+ LOGD("sensorType %d allowed %d: mergedMode %d, otherClientPresent %d",
+ static_cast<size_t>(sensorType), allowed,
+ static_cast<int>(mergedMode), otherClientPresent);
+ }
+ } else {
+ // If it's an ACTIVE or an OFF request, it's always allowed.
+ allowed = true;
}
}
return allowed;
}
/**
- * Makes a QMI SNS_SMGR_BUFFERING_REQ request based on the arguments provided.
+ * Makes a SNS_SMGR_BUFFERING_REQ request based on the arguments provided.
*
* @param sensorId The sensorID as provided by the SMGR.
* @param dataType The dataType for the sesnor as provided by the MSGR.
@@ -1211,27 +1217,23 @@
* @param request The sensor request
* @return true if the request has been made successfully.
*/
-bool makeQmiRequest(uint8_t sensorId, uint8_t dataType, uint8_t calType,
- uint64_t minInterval, const SensorRequest& request) {
+bool makeBufferingReq(uint8_t sensorId, uint8_t dataType, uint8_t calType,
+ uint64_t minInterval, const SensorRequest& request) {
bool success = false;
+ auto sensorRequest = MakeUniqueZeroFill<sns_smgr_buffering_req_msg_v01>();
+ auto sensorResponse = MakeUnique<sns_smgr_buffering_resp_msg_v01>();
- // Allocate request and response for the sensor request.
- auto *sensorRequest = memoryAlloc<sns_smgr_buffering_req_msg_v01>();
- auto *sensorResponse = memoryAlloc<sns_smgr_buffering_resp_msg_v01>();
-
- if (sensorRequest == nullptr || sensorResponse == nullptr) {
- LOGE("Failed to allocate sensor request/response: out of memory");
+ if (sensorRequest.isNull() || sensorResponse.isNull()) {
+ LOGE("Failed to allocate buffering msg");
} else {
populateSensorRequest(request, sensorId, dataType, calType,
- minInterval, sensorRequest);
+ minInterval, sensorRequest.get());
- qmi_client_error_type status = qmi_client_send_msg_sync(
- gPlatformSensorServiceQmiClientHandle, SNS_SMGR_BUFFERING_REQ_V01,
- sensorRequest, sizeof(*sensorRequest),
- sensorResponse, sizeof(*sensorResponse),
- kQmiTimeoutMs);
+ smr_err status = getSmrHelper()->sendReqSync(
+ gPlatformSensorServiceSmrClientHandle, SNS_SMGR_BUFFERING_REQ_V01,
+ &sensorRequest, &sensorResponse);
- if (status != QMI_NO_ERR) {
+ if (status != SMR_NO_ERR) {
LOGE("Error requesting sensor data: %d", status);
} else if (sensorResponse->Resp.sns_result_t != SNS_RESULT_SUCCESS_V01
|| (sensorResponse->AckNak != SNS_SMGR_RESPONSE_ACK_SUCCESS_V01
@@ -1242,13 +1244,12 @@
success = true;
}
}
- memoryFree(sensorRequest);
- memoryFree(sensorResponse);
+
return success;
}
/**
- * Makes a QMI SNS_SMGR_BUFFERING_REQ request if necessary.
+ * Makes a SNS_SMGR_BUFFERING_REQ request if necessary.
*
* @param sensorType The sensor type of the request.
* @param request The sensor request to be made.
@@ -1259,23 +1260,21 @@
Sensor *sensor = EventLoopManagerSingleton::get()->getSensorRequestManager()
.getSensor(sensorType);
- if (sensor == nullptr) {
- LOGE("Invalid sensorType %d", static_cast<size_t>(sensorType));
- } else {
- // Do not make a QMI off request if the sensor is off. Otherwise, SMGR
+ if (sensor != nullptr) {
+ // Do not make an off request if the sensor is already off. Otherwise, SMGR
// returns an error.
if (request.getMode() == SensorMode::Off) {
success = sensor->isSensorOff;
}
- // Make a QMI buffering request if necessary.
+ // Make a SMGR buffering request if necessary.
if (!success) {
- success = makeQmiRequest(sensor->sensorId, sensor->dataType,
- sensor->calType, sensor->minInterval, request);
+ success = makeBufferingReq(sensor->sensorId, sensor->dataType,
+ sensor->calType, sensor->minInterval, request);
}
}
- // TODO: handle makeQmiRequest failures
+ // TODO: handle makeBufferingReq failures
if (success) {
// Update internal states if request was accepted by SMGR.
sensor->isSensorOff = (request.getMode() == SensorMode::Off);
@@ -1318,7 +1317,7 @@
* Identifies and removes passive requests that have been made to the SMGR, and
* adds them to the sensor monitor.
*
- * @param sensorId The sensor ID whose passive QMI requests are to be removed.
+ * @param sensorId The sensor ID whose passive requests are to be removed.
* @return true if a DELETE request has been accepted.
*/
bool removeAllPassiveRequests(uint8_t sensorId) {
@@ -1353,81 +1352,99 @@
}
void PlatformSensor::init() {
+ // Timeout for SMR client initialization, in milliseconds.
+ constexpr uint32_t kSmrInitTimeoutMs = 10;
+
+ SmrHelperSingleton::init();
+
// sns_smgr_api_v01
- qmi_idl_service_object_type sensorServiceObject =
+ qmi_idl_service_object_type smgrSvcObj =
SNS_SMGR_SVC_get_service_object_v01();
- if (sensorServiceObject == nullptr) {
+ if (smgrSvcObj == nullptr) {
FATAL_ERROR("Failed to obtain the SNS SMGR service instance");
}
- qmi_client_os_params sensorContextOsParams;
- qmi_client_error_type status = qmi_client_init_instance(sensorServiceObject,
- QMI_CLIENT_INSTANCE_ANY, &platformSensorServiceQmiIndicationCallback,
- nullptr, &sensorContextOsParams, kQmiInitTimeoutMs,
- &gPlatformSensorServiceQmiClientHandle);
- if (status != QMI_NO_ERR) {
- FATAL_ERROR("Failed to initialize the sensor service QMI client: %d",
- status);
+ smr_err result = getSmrHelper()->waitForService(smgrSvcObj);
+ if (result != SMR_NO_ERR) {
+ FATAL_ERROR("Failed while waiting for SNS SMGR service");
+ }
+
+ // Note: giving nullptr for err_cb prevents this from degrading to a regular
+ // QMI client if the service is not found.
+ smr_err status = smr_client_init(
+ smgrSvcObj, SMR_CLIENT_INSTANCE_ANY,
+ platformSensorServiceIndicationCallback, nullptr /* ind_cb_data */,
+ kSmrInitTimeoutMs, nullptr /* err_cb */, nullptr /* err_cb_data */,
+ &gPlatformSensorServiceSmrClientHandle, isSlpiUimgSupported());
+ if (status != SMR_NO_ERR) {
+ FATAL_ERROR("Failed to initialize SMGR client: %d", status);
}
// sns_smgr_interal_api_v02
- sensorServiceObject = SNS_SMGR_INTERNAL_SVC_get_service_object_v02();
- if (sensorServiceObject == nullptr) {
+ qmi_idl_service_object_type smgrInternalSvcObj =
+ SNS_SMGR_INTERNAL_SVC_get_service_object_v02();
+ if (smgrInternalSvcObj == nullptr) {
FATAL_ERROR("Failed to obtain the SNS SMGR internal service instance");
}
- status = qmi_client_init_instance(sensorServiceObject,
- QMI_CLIENT_INSTANCE_ANY,
- &platformSensorInternalServiceQmiIndicationCallback, nullptr,
- &sensorContextOsParams, kQmiInitTimeoutMs,
- &gPlatformSensorInternalServiceQmiClientHandle);
- if (status != QMI_NO_ERR) {
- FATAL_ERROR("Failed to initialize the sensor internal service QMI client: "
- "%d", status);
+ result = getSmrHelper()->waitForService(smgrInternalSvcObj);
+ if (result != SMR_NO_ERR) {
+ FATAL_ERROR("Failed while waiting for SNS SMGR internal service");
+ }
+
+ status = smr_client_init(
+ smgrInternalSvcObj, SMR_CLIENT_INSTANCE_ANY,
+ platformSensorInternalServiceIndicationCallback,
+ nullptr /* ind_cb_data */, kSmrInitTimeoutMs, nullptr /* err_cb */,
+ nullptr /* err_cb_data */, &gPlatformSensorInternalServiceSmrClientHandle,
+ isSlpiUimgSupported());
+ if (status != SMR_NO_ERR) {
+ FATAL_ERROR("Failed to initialize SMGR internal client: %d", status);
}
}
void PlatformSensor::deinit() {
- qmi_client_error_type err = qmi_client_release(
- gPlatformSensorServiceQmiClientHandle);
- if (err != QMI_NO_ERR) {
- LOGE("Failed to release SensorService QMI client: %d", err);
+ smr_err err = getSmrHelper()->releaseSync(
+ gPlatformSensorServiceSmrClientHandle);
+ if (err != SMR_NO_ERR) {
+ LOGE("Failed to release SMGR client: %d", err);
}
- gPlatformSensorServiceQmiClientHandle = nullptr;
+ gPlatformSensorServiceSmrClientHandle = nullptr;
- err = qmi_client_release(gPlatformSensorInternalServiceQmiClientHandle);
- if (err != QMI_NO_ERR) {
- LOGE("Failed to release SensorInternalService QMI client: %d", err);
+ err = getSmrHelper()->releaseSync(
+ gPlatformSensorInternalServiceSmrClientHandle);
+ if (err != SMR_NO_ERR) {
+ LOGE("Failed to release SMGR internal client: %d", err);
}
- gPlatformSensorInternalServiceQmiClientHandle = nullptr;
+ gPlatformSensorInternalServiceSmrClientHandle = nullptr;
- // Clearing all sensor status monitors. Releaseing a QMI client also
- // releases all sensor status monitor requests.
+ // Clearing all sensor status monitors. Releasing an SMR client also releases
+ // all sensor status monitor requests.
gSensorMonitors.clear();
+ SmrHelperSingleton::deinit();
}
bool PlatformSensor::getSensors(DynamicVector<Sensor> *sensors) {
CHRE_ASSERT(sensors);
- sns_smgr_all_sensor_info_req_msg_v01 sensorListRequest;
- sns_smgr_all_sensor_info_resp_msg_v01 sensorListResponse;
+ auto sensorListRequest =
+ MakeUniqueZeroFill<sns_smgr_all_sensor_info_req_msg_v01>();
+ auto sensorListResponse = MakeUnique<sns_smgr_all_sensor_info_resp_msg_v01>();
- qmi_client_error_type status = qmi_client_send_msg_sync(
- gPlatformSensorServiceQmiClientHandle, SNS_SMGR_ALL_SENSOR_INFO_REQ_V01,
- &sensorListRequest, sizeof(sns_smgr_all_sensor_info_req_msg_v01),
- &sensorListResponse, sizeof(sns_smgr_all_sensor_info_resp_msg_v01),
- kQmiTimeoutMs);
+ smr_err status = getSmrHelper()->sendReqSync(
+ gPlatformSensorServiceSmrClientHandle, SNS_SMGR_ALL_SENSOR_INFO_REQ_V01,
+ &sensorListRequest, &sensorListResponse);
bool success = false;
- if (status != QMI_NO_ERR) {
+ if (status != SMR_NO_ERR) {
LOGE("Error requesting sensor list: %d", status);
- } else if (sensorListResponse.Resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
+ } else if (sensorListResponse->Resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
LOGE("Sensor list lequest failed with error: %d",
- sensorListResponse.Resp.sns_err_t);
+ sensorListResponse->Resp.sns_err_t);
} else {
success = true;
- for (uint32_t i = 0; i < sensorListResponse.SensorInfo_len; i++) {
- uint8_t sensorId = sensorListResponse.SensorInfo[i].SensorID;
+ for (uint32_t i = 0; i < sensorListResponse->SensorInfo_len; i++) {
+ uint8_t sensorId = sensorListResponse->SensorInfo[i].SensorID;
if (!getSensorsForSensorId(sensorId, sensors)) {
success = false;
break;
@@ -1439,16 +1456,39 @@
}
bool PlatformSensor::applyRequest(const SensorRequest& request) {
- // Adds a sensor monitor the first time this sensor is requested.
- addSensorMonitor(this->sensorId);
+ bool success;
- // Determines whether a (passive) request is allowed at this point.
- bool requestAllowed = isRequestAllowed(getSensorType(), request);
+ if (!SmrHelperSingleton::isInitialized()) {
+ // Off requests made as part of shutdown come after PlatformSensor::deinit()
+ // which releases our SMGR clients, removing all requests. Report success in
+ // this case.
+ success = (request.getMode() == SensorMode::Off) ? true : false;
+ CHRE_ASSERT_LOG(success, "Sensor request made before init/after deinit");
+ } else {
+ // Adds a sensor monitor the first time this sensor is requested.
+ addSensorMonitor(this->sensorId);
- // If request is not allowed, turn off the sensor. Otherwise, make request.
- SensorRequest offRequest;
- bool success = makeRequest(getSensorType(),
- requestAllowed ? request : offRequest);
+ // As sensor status monior indication doesn't support secondary sensor
+ // status change, Light sensor (a secondary one) is always overridden to be
+ // requested with an active mode.
+ bool passiveLight = (getSensorType() == SensorType::Light
+ && sensorModeIsPassive(request.getMode()));
+ if (passiveLight) {
+ LOGE("Passive request for Light sensor is not supported. "
+ "Overriding request to active");
+ }
+ SensorRequest localRequest(
+ passiveLight ? SensorMode::ActiveContinuous : request.getMode(),
+ request.getInterval(), request.getLatency());
+
+ // Determines whether a (passive) request is allowed at this point.
+ bool requestAllowed = isRequestAllowed(getSensorType(), localRequest);
+
+ // If request is not allowed, turn off the sensor. Otherwise, make request.
+ SensorRequest offRequest;
+ success = makeRequest(getSensorType(),
+ requestAllowed ? localRequest : offRequest);
+ }
return success;
}
@@ -1514,8 +1554,8 @@
this->lastEventValid = true;
}
-qmi_client_type getSensorServiceQmiClientHandle() {
- return gPlatformSensorServiceQmiClientHandle;
+smr_client_hndl getSensorServiceSmrClientHandle() {
+ return gPlatformSensorServiceSmrClientHandle;
}
} // namespace chre
diff --git a/platform/slpi/power_control_manager.cc b/platform/slpi/power_control_manager.cc
new file mode 100644
index 0000000..700958c
--- /dev/null
+++ b/platform/slpi/power_control_manager.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/platform/power_control_manager.h"
+
+#include "chre/platform/fatal_error.h"
+#include "chre/platform/log.h"
+#include "chre/platform/slpi/power_control_util.h"
+
+namespace chre {
+
+PowerControlManagerBase::PowerControlManagerBase() {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ char kClientName[] = "CHRE";
+ sns_pm_err_code_e result = sns_pm_client_init(
+ &mClientHandle, nullptr, kClientName, SNS_PM_CLIENT_ID_CHRE);
+ if (result != SNS_PM_SUCCESS) {
+ FATAL_ERROR("Power manager client init failed.");
+ }
+#endif // CHRE_SLPI_UIMG_ENABLED
+}
+
+PowerControlManagerBase::~PowerControlManagerBase() {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ sns_pm_client_close(mClientHandle);
+#endif // CHRE_SLPI_UIMG_ENABLED
+}
+
+bool PowerControlManagerBase::votePowerMode(
+ sns_pm_img_mode_e mode) {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ sns_pm_err_code_e result = sns_pm_vote_img_mode(mClientHandle, mode);
+ if (result != SNS_PM_SUCCESS) {
+ LOGE("Failed to vote for power mode %d with result %d", mode, result);
+ }
+
+ return (result == SNS_PM_SUCCESS);
+#else
+ return true;
+#endif // CHRE_SLPI_UIMG_ENABLED
+}
+
+void PowerControlManager::postEventLoopProcess(size_t numPendingEvents) {
+ if (numPendingEvents == 0 && !slpiInUImage()) {
+ votePowerMode(SNS_IMG_MODE_NOCLIENT);
+ }
+}
+
+} // namespace chre
diff --git a/platform/slpi/preloaded_nanoapps.cc b/platform/slpi/preloaded_nanoapps.cc
index 43ffea0..96ff5f8 100644
--- a/platform/slpi/preloaded_nanoapps.cc
+++ b/platform/slpi/preloaded_nanoapps.cc
@@ -36,12 +36,12 @@
UniquePtr<Nanoapp> nanoapp;
};
- // These nanoapps will be delivered via GMS Core in a future GMS release, but
- // are pre-loaded for now.
+ // The list of nanoapps to be loaded from the filesystem of the device.
// TODO: allow these to be overridden by target-specific build configuration
static PreloadedNanoappDescriptor preloadedNanoapps[] = {
{ 0x476f6f676c00100b, "activity.so", MakeUnique<Nanoapp>() },
{ 0x476f6f676c001004, "geofence.so", MakeUnique<Nanoapp>() },
+ { 0x476f6f676c00100c, "wifi_offload.so", MakeUnique<Nanoapp>() },
};
for (size_t i = 0; i < ARRAY_SIZE(preloadedNanoapps); i++) {
diff --git a/platform/slpi/smr_helper.cc b/platform/slpi/smr_helper.cc
new file mode 100644
index 0000000..70a876c
--- /dev/null
+++ b/platform/slpi/smr_helper.cc
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "chre/platform/slpi/smr_helper.h"
+
+#include <inttypes.h>
+
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/slpi/power_control_util.h"
+#include "chre/util/lock_guard.h"
+
+namespace chre {
+
+smr_err SmrHelper::releaseSync(smr_client_hndl clientHandle,
+ Nanoseconds timeout) {
+ // smr_client_release is synchronous for SMR services in the current
+ // implementation, so we can't hold the lock while calling it otherwise we'll
+ // deadlock in the callback.
+ {
+ LockGuard<Mutex> lock(mMutex);
+ CHRE_ASSERT(!mWaiting);
+ mWaiting = true;
+ }
+
+ smr_err result = smr_client_release(
+ clientHandle, SmrHelper::smrReleaseCb, this);
+ if (result == SMR_NO_ERR) {
+ LockGuard<Mutex> lock(mMutex);
+ bool waitSuccess = true;
+ while (mWaiting && waitSuccess) {
+ waitSuccess = mCond.wait_for(mMutex, timeout);
+ }
+
+ if (!waitSuccess) {
+ LOGE("Releasing SMR client timed out");
+ result = SMR_TIMEOUT_ERR;
+ mWaiting = false;
+ }
+ }
+
+ return result;
+}
+
+smr_err SmrHelper::waitForService(qmi_idl_service_object_type serviceObj,
+ Microseconds timeout) {
+ // smr_client_check_ext is synchronous if the service already exists,
+ // so don't hold the lock while calling to prevent deadlock in the callback.
+ {
+ LockGuard<Mutex> lock(mMutex);
+ CHRE_ASSERT(!mWaiting);
+ mWaiting = true;
+ }
+
+ smr_err result = smr_client_check_ext(serviceObj, SMR_CLIENT_INSTANCE_ANY,
+ timeout.getMicroseconds(),
+ SmrHelper::smrWaitForServiceCb, this);
+ if (result == SMR_NO_ERR) {
+ LockGuard<Mutex> lock(mMutex);
+ while (mWaiting) {
+ mCond.wait(mMutex);
+ }
+
+ if (mServiceTimedOut) {
+ LOGE("Wait for SMR service timed out");
+ result = SMR_TIMEOUT_ERR;
+ mServiceTimedOut = false;
+ }
+ }
+
+ return result;
+}
+
+bool SmrHelper::sendReqSyncUntyped(
+ smr_client_hndl client_handle, unsigned int msg_id,
+ void *req_c_struct, unsigned int req_c_struct_len,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ Nanoseconds timeout, smr_err *result) {
+ LockGuard<Mutex> lock(mMutex);
+ CHRE_ASSERT(!mWaiting);
+ bool waitSuccess = true;
+
+ // Force big image since smr_client_send_req is not supported in micro-image
+ slpiForceBigImage();
+
+ // Note that null txn_handle means we can't abandon the transaction, but it's
+ // only supported for QMI (non-SMR) services, and we don't expect that anyway.
+ // SMR itself does not support canceling transactions made to SMR services.
+ *result = smr_client_send_req(
+ client_handle, msg_id, req_c_struct, req_c_struct_len, resp_c_struct,
+ resp_c_struct_len, SmrHelper::smrRespCb, this, nullptr /* txn_handle */);
+ if (*result != SMR_NO_ERR) {
+ LOGE("Failed to send request (msg_id 0x%02x): %d", msg_id, *result);
+ } else {
+ mWaiting = true;
+ mPendingRespBuf = resp_c_struct;
+
+ while (mWaiting && waitSuccess) {
+ waitSuccess = mCond.wait_for(mMutex, timeout);
+ }
+
+ if (waitSuccess) {
+ *result = mTranspErr;
+ } else {
+ LOGE("SMR request for msg_id 0x%02x timed out after %" PRIu64 " ms",
+ msg_id, Milliseconds(timeout).getMilliseconds());
+ *result = SMR_TIMEOUT_ERR;
+ mWaiting = false;
+ }
+ mPendingRespBuf = nullptr;
+ }
+
+ return waitSuccess;
+}
+
+void SmrHelper::handleResp(smr_client_hndl client_handle, unsigned int msg_id,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ smr_err transp_err) {
+ LockGuard<Mutex> lock(mMutex);
+
+ if (!mWaiting) {
+ LOGE("Got SMR response when none pending!");
+ } else if (mPendingRespBuf != resp_c_struct) {
+ LOGE("Got SMR response with unexpected buffer, msg_id 0x%02x: %p vs. %p",
+ msg_id, mPendingRespBuf, resp_c_struct);
+ } else {
+ // SMR will handle copying the response into the buffer passed in to
+ // smr_client_send_req(), so we just need to unblock the waiting thread
+ mTranspErr = transp_err;
+ mWaiting = false;
+ mCond.notify_one();
+ }
+}
+
+void SmrHelper::smrReleaseCb(void *release_cb_data) {
+ SmrHelper *obj = static_cast<SmrHelper *>(release_cb_data);
+ LockGuard<Mutex> lock(obj->mMutex);
+ obj->mWaiting = false;
+ obj->mCond.notify_one();
+}
+
+void SmrHelper::smrRespCb(smr_client_hndl client_handle, unsigned int msg_id,
+ void *resp_c_struct, unsigned int resp_c_struct_len,
+ void *resp_cb_data, smr_err transp_err) {
+ SmrHelper *obj = static_cast<SmrHelper *>(resp_cb_data);
+ obj->handleResp(client_handle, msg_id, resp_c_struct, resp_c_struct_len,
+ transp_err);
+}
+
+void SmrHelper::smrWaitForServiceCb(qmi_idl_service_object_type /* service_obj */,
+ qmi_service_instance /* instance_id */,
+ bool timeout_expired,
+ void *wait_for_service_cb_data) {
+ SmrHelper *obj = static_cast<SmrHelper *>(wait_for_service_cb_data);
+ LockGuard<Mutex> lock(obj->mMutex);
+ obj->mServiceTimedOut = timeout_expired;
+ obj->mWaiting = false;
+ obj->mCond.notify_one();
+}
+
+} // namespace chre
diff --git a/platform/slpi/system_time.cc b/platform/slpi/system_time.cc
index 9ff957c..481535e 100644
--- a/platform/slpi/system_time.cc
+++ b/platform/slpi/system_time.cc
@@ -62,7 +62,19 @@
} // anonymous namespace
Nanoseconds SystemTime::getMonotonicTime() {
- return Microseconds(uTimetick_CvtFromTicks(uTimetick_Get(), T_USEC));
+ constexpr uint64_t kClockFreq = 19200000; // 19.2MHz QTimer clock
+
+ uint64_t ticks = uTimetick_Get();
+ uint64_t nsec = 0;
+ if (ticks >= kClockFreq) {
+ uint64_t seconds = (ticks / kClockFreq);
+ ticks %= kClockFreq;
+
+ nsec = (seconds * kOneSecondInNanoseconds);
+ }
+ nsec += (ticks * kOneSecondInNanoseconds) / kClockFreq;
+
+ return Nanoseconds(nsec);
}
int64_t SystemTime::getEstimatedHostTimeOffset() {
diff --git a/platform/slpi/system_timer.cc b/platform/slpi/system_timer.cc
index 5d58318..806909e 100644
--- a/platform/slpi/system_timer.cc
+++ b/platform/slpi/system_timer.cc
@@ -23,18 +23,28 @@
SystemTimer::SystemTimer() {}
SystemTimer::~SystemTimer() {
- timer_undef(&mTimerHandle);
+ if (mInitialized) {
+ slpiTimerUndef(&mTimerHandle);
+ }
}
bool SystemTimer::init() {
if (mInitialized) {
LOGW("Tried re-initializing timer");
} else {
- timer_error_type status = timer_def_osal(
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ SlpiTimerErrorType status = utimer_def_osal(
+ &mTimerHandle, UTIMER_FUNC1_CB_TYPE,
+ reinterpret_cast<utimer_osal_notify_obj_ptr>(systemTimerNotifyCallback),
+ reinterpret_cast<utimer_osal_notify_data>(this));
+#else
+ SlpiTimerErrorType status = timer_def_osal(
&mTimerHandle, &timer_non_defer_group, TIMER_FUNC1_CB_TYPE,
reinterpret_cast<time_osal_notify_obj_ptr>(systemTimerNotifyCallback),
reinterpret_cast<time_osal_notify_data>(this));
- if (status != TE_SUCCESS) {
+#endif // CHRE_SLPI_UIMG_ENABLED
+
+ if (status != SLPI_TIMER_SUCCESS) {
LOGE("Error initializing timer %d", status);
} else {
mInitialized = true;
@@ -50,9 +60,9 @@
if (mInitialized) {
mCallback = callback;
mData = data;
- timer_error_type status = timer_set_64(&mTimerHandle,
- Microseconds(delay).getMicroseconds(), 0, T_USEC);
- if (status != TE_SUCCESS) {
+ SlpiTimerErrorType status = slpiTimerSet64(&mTimerHandle,
+ Microseconds(delay).getMicroseconds(), 0, SlpiTimerMicroUnit);
+ if (status != SLPI_TIMER_SUCCESS) {
LOGE("Error setting timer %d", status);
} else {
wasSet = true;
@@ -65,7 +75,8 @@
bool SystemTimer::cancel() {
bool wasCancelled = false;
if (mInitialized) {
- time_timetick_type ticksRemaining = timer_clr_64(&mTimerHandle, T_TICK);
+ SlpiTimerTickType ticksRemaining = slpiTimerClr64(&mTimerHandle,
+ SlpiTimerTickUnit);
wasCancelled = (ticksRemaining > 0);
}
@@ -73,10 +84,12 @@
}
bool SystemTimer::isActive() {
- return (mInitialized && timer_get_64(&mTimerHandle, T_TICK) > 0);
+ SlpiTimerTickType ticksRemaining = slpiTimerGet64(&mTimerHandle,
+ SlpiTimerTickUnit);
+ return (mInitialized && ticksRemaining > 0);
}
-void SystemTimerBase::systemTimerNotifyCallback(timer_cb_data_type data) {
+void SystemTimerBase::systemTimerNotifyCallback(SlpiTimerCallbackDataType data) {
SystemTimer *systemTimer = reinterpret_cast<SystemTimer *>(data);
systemTimer->mCallback(systemTimer->mData);
}
diff --git a/run_sim.sh b/run_sim.sh
index d406aa4..1144121 100755
--- a/run_sim.sh
+++ b/run_sim.sh
@@ -10,4 +10,4 @@
export CHRE_VARIANT_MK_INCLUDES=variant/simulator/variant.mk
make google_x86_linux_debug -j$JOB_COUNT
-./out/google_x86_linux_debug/libchre
+./out/google_x86_linux_debug/libchre ${@:1}
diff --git a/util/include/chre/util/container_support.h b/util/include/chre/util/container_support.h
new file mode 100644
index 0000000..f70160e
--- /dev/null
+++ b/util/include/chre/util/container_support.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHRE_UTIL_CONTAINER_SUPPORT_H_
+#define CHRE_UTIL_CONTAINER_SUPPORT_H_
+
+/**
+ * @file Provides replacements for macros and functions that are normally
+ * provided by the CHRE framework implementation. These portable implementations
+ * are implemented using the CHRE API rather than private system APIs.
+ */
+
+#ifdef CHRE_IS_NANOAPP_BUILD
+
+#include <chre.h>
+
+#include "chre/util/nanoapp/log.h"
+
+/**
+ * Provides the CHRE_ASSERT macro that uses chreAbort to abort the nanoapp upon
+ * failure.
+ *
+ * @param the condition to check for non-zero.
+ */
+#define CHRE_ASSERT(condition) do { \
+ if (!(condition)) { \
+ chreAbort(UINT32_MAX); \
+ } \
+} while (0)
+
+
+/**
+ * Provides the CHRE_ASSERT_LOG macro that logs the assertion failure followed
+ * by CHRE_ASSERT if the condition is non-zero.
+ *
+ * @param condition the condition to check for non-zero.
+ * @param fmt the format string to log.
+ * @param ... arguments to format into the log message.
+ */
+#define CHRE_ASSERT_LOG(condition, fmt, ...) do { \
+ if (!(condition)) { \
+ LOGE("Assert: " fmt, ##__VA_ARGS__); \
+ CHRE_ASSERT(condition); \
+ } \
+} while (0)
+
+namespace chre {
+
+/**
+ * Provides the memoryAlloc function that is normally provided by the CHRE
+ * runtime. It maps into chreHeapAlloc.
+ *
+ * @param size the size of the allocation to make.
+ * @return a pointer to allocated memory or nullptr if allocation failed.
+ */
+inline void *memoryAlloc(size_t size) {
+ return chreHeapAlloc(static_cast<uint32_t>(size));
+}
+
+/**
+ * Provides the memoryFree function that is normally provided by the CHRE
+ * runtime. It maps into chreHeapFree.
+ *
+ * @param pointer the allocation to release.
+ */
+inline void memoryFree(void *pointer) {
+ chreHeapFree(pointer);
+}
+
+} // namespace chre
+
+#else
+#include "chre/platform/assert.h"
+#include "chre/platform/memory.h"
+#endif
+
+#endif // CHRE_UTIL_CONTAINER_SUPPORT_H_
diff --git a/util/include/chre/util/dynamic_vector_impl.h b/util/include/chre/util/dynamic_vector_impl.h
index 4d5cb26..72fca18 100644
--- a/util/include/chre/util/dynamic_vector_impl.h
+++ b/util/include/chre/util/dynamic_vector_impl.h
@@ -21,8 +21,7 @@
#include <new>
#include <utility>
-#include "chre/platform/assert.h"
-#include "chre/platform/memory.h"
+#include "chre/util/container_support.h"
#include "chre/util/memory.h"
namespace chre {
diff --git a/util/include/chre/util/fixed_size_blocking_queue.h b/util/include/chre/util/fixed_size_blocking_queue.h
index 5efbbf6..eefe089 100644
--- a/util/include/chre/util/fixed_size_blocking_queue.h
+++ b/util/include/chre/util/fixed_size_blocking_queue.h
@@ -56,6 +56,11 @@
*/
bool empty();
+ /**
+ * Determines the current size of the BlockingQueue.
+ */
+ size_t size();
+
private:
//! The mutex used to ensure thread-safety.
Mutex mMutex;
diff --git a/util/include/chre/util/fixed_size_blocking_queue_impl.h b/util/include/chre/util/fixed_size_blocking_queue_impl.h
index abfa4eb..3aa5235 100644
--- a/util/include/chre/util/fixed_size_blocking_queue_impl.h
+++ b/util/include/chre/util/fixed_size_blocking_queue_impl.h
@@ -54,6 +54,12 @@
return mQueue.empty();
}
+template<typename ElementType, size_t kSize>
+size_t FixedSizeBlockingQueue<ElementType, kSize>::size() {
+ LockGuard<Mutex> lock(mMutex);
+ return mQueue.size();
+}
+
} // namespace chre
#endif // CHRE_UTIL_BLOCKING_QUEUE_IMPL_H_
diff --git a/util/include/chre/util/memory.h b/util/include/chre/util/memory.h
index ccfb5ce..d5ad30c 100644
--- a/util/include/chre/util/memory.h
+++ b/util/include/chre/util/memory.h
@@ -50,6 +50,13 @@
void uninitializedMoveOrCopy(ElementType *source, size_t count,
ElementType *dest);
+/**
+ * Allocates memory 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>
+T *memoryAlloc(Args&&... args);
+
} // namespace chre
#include "chre/util/memory_impl.h"
diff --git a/util/include/chre/util/memory_impl.h b/util/include/chre/util/memory_impl.h
index cf9b712..9d03d39 100644
--- a/util/include/chre/util/memory_impl.h
+++ b/util/include/chre/util/memory_impl.h
@@ -22,6 +22,8 @@
#include <type_traits>
#include <utility>
+#include "chre/util/container_support.h"
+
namespace chre {
template<typename ElementType>
@@ -107,6 +109,16 @@
//typename std::is_trivially_copy_constructible<ElementType>::type());
}
+template<typename T, typename... Args>
+inline T *memoryAlloc(Args&&... args) {
+ auto *storage = static_cast<T *>(memoryAlloc(sizeof(T)));
+ if (storage != nullptr) {
+ new(storage) T(std::forward<Args>(args)...);
+ }
+
+ return storage;
+}
+
} // namespace chre
#endif // CHRE_UTIL_MEMORY_IMPL_H_
diff --git a/util/include/chre/util/unique_ptr.h b/util/include/chre/util/unique_ptr.h
index 78feb53..8af2e9a 100644
--- a/util/include/chre/util/unique_ptr.h
+++ b/util/include/chre/util/unique_ptr.h
@@ -52,6 +52,16 @@
UniquePtr(UniquePtr<ObjectType>&& other);
/**
+ * Constructs a new UniquePtr via moving the Object from another UniquePtr.
+ * This constructor allows conversion (ie: upcast) to another type if
+ * possible.
+ *
+ * @param other UniquePtr instance to move and convert into this object.
+ */
+ template<typename OtherObjectType>
+ UniquePtr(UniquePtr<OtherObjectType>&& other);
+
+ /**
* Deconstructs the object (if necessary) and releases associated memory.
*/
~UniquePtr();
@@ -105,6 +115,11 @@
UniquePtr<ObjectType>& operator=(UniquePtr<ObjectType>&& other);
private:
+ // Befriend this class to itself to allow the templated conversion constructor
+ // permission to access mObject below.
+ template<typename OtherObjectType>
+ friend class UniquePtr;
+
//! A pointer to the underlying storage for this object.
ObjectType *mObject;
};
@@ -119,6 +134,14 @@
template<typename ObjectType, typename... Args>
UniquePtr<ObjectType> MakeUnique(Args&&... args);
+/**
+ * Just like MakeUnique(), except it zeros out any allocated memory. Intended to
+ * be used for creating objects that have trivial constructors (e.g. C structs)
+ * but should start with a known state.
+ */
+template<typename ObjectType>
+UniquePtr<ObjectType> MakeUniqueZeroFill();
+
} // namespace chre
#include "chre/util/unique_ptr_impl.h"
diff --git a/util/include/chre/util/unique_ptr_impl.h b/util/include/chre/util/unique_ptr_impl.h
index aac1411..69be9d0 100644
--- a/util/include/chre/util/unique_ptr_impl.h
+++ b/util/include/chre/util/unique_ptr_impl.h
@@ -17,9 +17,11 @@
#ifndef CHRE_UTIL_UNIQUE_PTR_IMPL_H_
#define CHRE_UTIL_UNIQUE_PTR_IMPL_H_
+#include <string.h>
+#include <type_traits>
#include <utility>
-#include "chre/platform/memory.h"
+#include "chre/util/memory.h"
namespace chre {
@@ -36,6 +38,13 @@
}
template<typename ObjectType>
+template<typename OtherObjectType>
+UniquePtr<ObjectType>::UniquePtr(UniquePtr<OtherObjectType>&& other) {
+ mObject = other.mObject;
+ other.mObject = nullptr;
+}
+
+template<typename ObjectType>
UniquePtr<ObjectType>::~UniquePtr() {
if (mObject != nullptr) {
mObject->~ObjectType();
@@ -91,6 +100,22 @@
std::forward<Args>(args)...));
}
+template<typename ObjectType>
+inline UniquePtr<ObjectType> MakeUniqueZeroFill() {
+ // For simplicity, we call memset *after* memoryAlloc<ObjectType>() - this is
+ // only valid for types that have a trivial constructor. This utility function
+ // is really meant to be used with trivial types only - if there's a desire to
+ // zero things out in a non-trivial type, the right place for that is in its
+ // constructor.
+ static_assert(std::is_trivial<ObjectType>::value,
+ "MakeUniqueZeroFill is only supported for trivial types");
+ auto ptr = UniquePtr<ObjectType>(memoryAlloc<ObjectType>());
+ if (!ptr.isNull()) {
+ memset(ptr.get(), 0, sizeof(ObjectType));
+ }
+ return ptr;
+}
+
} // namespace chre
#endif // CHRE_UTIL_UNIQUE_PTR_IMPL_H_
diff --git a/util/tests/unique_ptr_test.cc b/util/tests/unique_ptr_test.cc
index 5d53901..31dc95d 100644
--- a/util/tests/unique_ptr_test.cc
+++ b/util/tests/unique_ptr_test.cc
@@ -1,9 +1,12 @@
+#include <cstring>
+
#include "gtest/gtest.h"
#include "chre/util/unique_ptr.h"
using chre::UniquePtr;
using chre::MakeUnique;
+using chre::MakeUniqueZeroFill;
struct Value {
Value(int value) : value(value) {
@@ -34,6 +37,21 @@
EXPECT_EQ(myInt[0].value, 0xcafe);
}
+struct BigArray {
+ int x[2048];
+};
+
+TEST(UniquePtr, MakeUniqueZeroFill) {
+ BigArray baseline = {};
+ auto myArray = MakeUniqueZeroFill<BigArray>();
+ ASSERT_FALSE(myArray.isNull());
+ // Note that this doesn't actually test things properly, because we don't
+ // guarantee that malloc is not already giving us zeroed out memory. To
+ // properly do it, we could inject the allocator, but this function is simple
+ // enough that it's not really worth the effort.
+ EXPECT_EQ(std::memcmp(&baseline, myArray.get(), sizeof(baseline)), 0);
+}
+
TEST(UniquePtr, MoveConstruct) {
UniquePtr<Value> myInt = MakeUnique<Value>(0xcafe);
ASSERT_FALSE(myInt.isNull());
diff --git a/variant/simulator/variant.mk b/variant/simulator/variant.mk
index e37c007..affcb4a 100644
--- a/variant/simulator/variant.mk
+++ b/variant/simulator/variant.mk
@@ -16,6 +16,9 @@
# nanoapp list.
COMMON_CFLAGS += -DCHRE_VARIANT_SUPPLIES_STATIC_NANOAPP_LIST
+# Enable exceptions for TCLAP.
+GOOGLE_X86_LINUX_CFLAGS += -fexceptions
+
# Common Source Files ##########################################################
COMMON_SRCS += variant/simulator/static_nanoapps.cc