Merge pi-qpr1-release PQ1A.181105.017.A1 to pi-platform-release
am: cc1118a4e8
Change-Id: I5fc7ebc3c80bf2ec21144eaed386cce7d0cb48b5
diff --git a/Android.bp b/Android.bp
index 4ffb2da..c99a3ab 100644
--- a/Android.bp
+++ b/Android.bp
@@ -59,6 +59,26 @@
gtest: false,
}
+cc_test {
+ name: "audio_stress_test",
+ vendor: true,
+ local_include_dirs: [
+ "chre_api/include/chre_api",
+ "util/include",
+ ],
+ srcs: [
+ "host/common/audio_stress_test/audio_stress_test.cc",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ static_libs: ["chre_client"],
+ gtest: false,
+}
+
cc_library_shared {
name: "[email protected]",
vendor: true,
diff --git a/apps/audio_stress_test/Makefile b/apps/audio_stress_test/Makefile
new file mode 100644
index 0000000..7421478
--- /dev/null
+++ b/apps/audio_stress_test/Makefile
@@ -0,0 +1,37 @@
+#
+# Audio Stress Test Nanoapp Makefile
+#
+
+# Environment Checks ###########################################################
+
+ifeq ($(CHRE_PREFIX),)
+ifneq ($(ANDROID_BUILD_TOP),)
+CHRE_PREFIX = $(ANDROID_BUILD_TOP)/system/chre
+else
+$(error "You must run 'lunch' to setup ANDROID_BUILD_TOP, or explicitly define \
+ the CHRE_PREFIX environment variable to point to the CHRE root \
+ directory.")
+endif
+endif
+
+# Nanoapp Configuration ########################################################
+
+NANOAPP_NAME_STRING = \"Audio\ Stress\ Test\"
+NANOAPP_NAME = audio_stress_test
+NANOAPP_ID = 0x012345678900000e
+NANOAPP_VERSION = 0x00000001
+
+# Common Compiler Flags ########################################################
+
+# Defines.
+COMMON_CFLAGS += -DCHRE_NANOAPP_DISABLE_BACKCOMPAT
+COMMON_CFLAGS += -DNANOAPP_MINIMUM_LOG_LEVEL=CHRE_LOG_LEVEL_DEBUG
+
+# Common Source Files ##########################################################
+
+COMMON_SRCS += audio_stress_test.cc
+COMMON_SRCS += $(CHRE_PREFIX)/util/nanoapp/audio.cc
+
+# Makefile Includes ############################################################
+
+include $(CHRE_PREFIX)/build/nanoapp/app.mk
diff --git a/apps/audio_stress_test/audio_stress_test.cc b/apps/audio_stress_test/audio_stress_test.cc
new file mode 100644
index 0000000..86c5dfb
--- /dev/null
+++ b/apps/audio_stress_test/audio_stress_test.cc
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.h>
+#include <cinttypes>
+
+#include "chre/util/macros.h"
+#include "chre/util/nanoapp/audio.h"
+#include "chre/util/nanoapp/log.h"
+#include "chre/util/time.h"
+
+#define LOG_TAG "[AudioStress]"
+
+/**
+ * @file
+ *
+ * This nanoapp is designed to subscribe to audio for varying durations of
+ * time and verify that audio data is delivered when it is expected to be.
+ */
+
+using chre::Milliseconds;
+using chre::Nanoseconds;
+using chre::Seconds;
+
+namespace {
+
+//! The required buffer size for the stress test.
+constexpr Nanoseconds kBufferDuration = Nanoseconds(Seconds(2));
+
+//! The required sample format for the stress test.
+constexpr uint8_t kBufferFormat = CHRE_AUDIO_DATA_FORMAT_16_BIT_SIGNED_PCM;
+
+//! The required sample rate for the stress test.
+constexpr uint32_t kBufferSampleRate = 16000;
+
+//! The maximum amount of time that audio will not be delivered for.
+constexpr Seconds kMaxAudioGap = Seconds(300);
+
+//! The list of durations to subscribe to audio for. Even durations are for when
+//! audio is enabled and odd is for when audio is disabled.
+constexpr Milliseconds kStressPlan[] = {
+ // Enabled, Disabled
+ Milliseconds(20000), Milliseconds(20000),
+ Milliseconds(30000), Milliseconds(200),
+ Milliseconds(10000), Milliseconds(1000),
+ Milliseconds(10000), Milliseconds(1999),
+ Milliseconds(8000), Milliseconds(60000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+ Milliseconds(1000), Milliseconds(1000),
+};
+
+//! The discovered audio handle found at startup.
+uint32_t gAudioHandle;
+
+//! The current position in the stress plan.
+size_t gTestPosition = 0;
+
+//! The timer handle to advance through the stress test.
+uint32_t gTimerHandle;
+
+//! Whether or not audio is currently suspended. If audio is delivered when this
+//! is set to true, this is considered a test failure.
+bool gAudioIsSuspended = true;
+
+//! The timestamp of the last audio data event.
+Nanoseconds gLastAudioTimestamp;
+
+/**
+ * @return true when the current test phase is expecting audio data events to be
+ * delivered.
+ */
+bool audioIsExpected() {
+ // Even test intervals are expected to return audio events. The current test
+ // interval is gTestPosition - 1 so there is no need to invert the bit.
+ return (gTestPosition % 2);
+}
+
+/**
+ * Discovers an audio source to use for the stress test. The gAudioHandle will
+ * be set if the audio source was found.
+ *
+ * @return true if a matching source was discovered successfully.
+ */
+bool discoverAudioHandle() {
+ bool success = false;
+ struct chreAudioSource source;
+ for (uint32_t i = 0; !success && chreAudioGetSource(i, &source); i++) {
+ LOGI("Found audio source '%s' with %" PRIu32 "Hz %s data",
+ source.name, source.sampleRate,
+ chre::getChreAudioFormatString(source.format));
+ LOGI(" buffer duration: [%" PRIu64 "ns, %" PRIu64 "ns]",
+ source.minBufferDuration, source.maxBufferDuration);
+
+ if (source.sampleRate == kBufferSampleRate
+ && source.minBufferDuration <= kBufferDuration.toRawNanoseconds()
+ && source.maxBufferDuration >= kBufferDuration.toRawNanoseconds()
+ && source.format == kBufferFormat) {
+ gAudioHandle = i;
+ success = true;
+ }
+ }
+
+ if (!success) {
+ LOGW("Failed to find suitable audio source");
+ }
+
+ return success;
+}
+
+void checkTestPassing() {
+ auto lastAudioDuration = Nanoseconds(chreGetTime()) - gLastAudioTimestamp;
+ if (lastAudioDuration > kMaxAudioGap) {
+ LOGE("Test fail - audio not received for %" PRIu64 "ns",
+ lastAudioDuration.toRawNanoseconds());
+ chreAbort(-1);
+ }
+}
+
+bool requestAudioForCurrentTestState(const Nanoseconds& testStateDuration) {
+ bool success = false;
+ LOGD("Test stage %zu", gTestPosition);
+ if (audioIsExpected()) {
+ if (!chreAudioConfigureSource(gAudioHandle, true, kBufferDuration.toRawNanoseconds(),
+ kBufferDuration.toRawNanoseconds())) {
+ LOGE("Failed to enable audio");
+ } else {
+ LOGI("Enabled audio for %" PRIu64, testStateDuration.toRawNanoseconds());
+ success = true;
+ }
+ } else {
+ if (!chreAudioConfigureSource(0, false, 0, 0)) {
+ LOGE("Failed to disable audio");
+ } else {
+ LOGI("Disabled audio for %" PRIu64, testStateDuration.toRawNanoseconds());
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+bool advanceTestPosition() {
+ checkTestPassing();
+ gTimerHandle = chreTimerSet(kStressPlan[gTestPosition].toRawNanoseconds(),
+ nullptr, true /* oneShot */);
+ bool success = (gTimerHandle != CHRE_TIMER_INVALID);
+ if (!success) {
+ LOGE("Failed to set timer");
+ } else {
+ // Grab the duration prior to incrementing the test position.
+ Nanoseconds timerDuration = kStressPlan[gTestPosition++];
+ if (gTestPosition >= ARRAY_SIZE(kStressPlan)) {
+ gTestPosition = 0;
+ }
+
+ success = requestAudioForCurrentTestState(timerDuration);
+ }
+
+ return success;
+}
+
+void handleTimerEvent() {
+ if (!advanceTestPosition()) {
+ LOGE("Test fail");
+ }
+}
+
+void handleAudioDataEvent(const chreAudioDataEvent *audioDataEvent) {
+ LOGI("Handling audio data event");
+ gLastAudioTimestamp = Nanoseconds(audioDataEvent->timestamp);
+
+ if (gAudioIsSuspended) {
+ LOGE("Test fail - received audio when suspended");
+ } else if (!audioIsExpected()) {
+ LOGE("Test fail - received audio unexpectedly");
+ } else {
+ LOGI("Test passing - received audio when expected");
+ }
+}
+
+void handleAudioSamplingChangeEvent(
+ const chreAudioSourceStatusEvent *audioSourceStatusEvent) {
+ LOGI("Handling audio sampling change event - suspended: %d",
+ audioSourceStatusEvent->status.suspended);
+ gAudioIsSuspended = audioSourceStatusEvent->status.suspended;
+}
+
+} // namespace
+
+
+bool nanoappStart() {
+ LOGI("start");
+ gLastAudioTimestamp = Nanoseconds(chreGetTime());
+ return (discoverAudioHandle() && advanceTestPosition());
+}
+
+void nanoappHandleEvent(uint32_t senderInstanceId,
+ uint16_t eventType,
+ const void *eventData) {
+ switch (eventType) {
+ case CHRE_EVENT_TIMER:
+ handleTimerEvent();
+ break;
+
+ case CHRE_EVENT_AUDIO_DATA:
+ handleAudioDataEvent(
+ static_cast<const chreAudioDataEvent *>(eventData));
+ break;
+
+ case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
+ handleAudioSamplingChangeEvent(
+ static_cast<const chreAudioSourceStatusEvent *>(eventData));
+ break;
+
+ default:
+ LOGW("Unexpected event %" PRIu16, eventType);
+ break;
+ }
+}
+
+void nanoappEnd() {
+ LOGI("stop");
+}
diff --git a/apps/chqts/src/busy_startup/busy_startup.cc b/apps/chqts/src/busy_startup/busy_startup.cc
index 91e4b69..8759a93 100644
--- a/apps/chqts/src/busy_startup/busy_startup.cc
+++ b/apps/chqts/src/busy_startup/busy_startup.cc
@@ -47,13 +47,13 @@
#include <chre.h>
#include <shared/send_message.h>
+#include <shared/time_util.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;
@@ -198,9 +198,12 @@
chreLog(CHRE_LOG_ERROR, "Failed sensorFindDefault in start");
return false;
}
+
+ // Configure accel request at 50 Hz (reasonable rate, e.g. for AR)
+ // TODO: Add a way to find the range of possible sample rates
if (!chreSensorConfigure(gSensorHandle,
CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS,
- CHRE_SENSOR_INTERVAL_DEFAULT,
+ 20 * nanoapp_testing::kOneMillisecondInNanoseconds,
CHRE_SENSOR_LATENCY_ASAP)) {
chreLog(CHRE_LOG_ERROR, "Failed sensorConfigure in start");
return false;
diff --git a/apps/chqts/src/general_test/basic_audio_test.cc b/apps/chqts/src/general_test/basic_audio_test.cc
index 01aabb1..7452c49 100644
--- a/apps/chqts/src/general_test/basic_audio_test.cc
+++ b/apps/chqts/src/general_test/basic_audio_test.cc
@@ -17,16 +17,15 @@
#include <general_test/basic_audio_test.h>
#include <shared/send_message.h>
+#include <shared/time_util.h>
+using nanoapp_testing::kOneSecondInNanoseconds;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
namespace general_test {
namespace {
-//! Unit conversion nanoseconds per second.
-constexpr uint64_t kNanosecondsPerSecond = 1000000000;
-
//! This is a reasonably high limit on the number of audio sources that a system
//! would expose. Use this to verify that there are no gaps in the source
//! handles.
@@ -50,12 +49,12 @@
//! sample rate possible, a minimum number of samples will be delivered in
//! a batch.
constexpr uint64_t kMinBufferDuration =
- (kNanosecondsPerSecond / kMaxAudioSampleRate) * 10;
+ (kOneSecondInNanoseconds / kMaxAudioSampleRate) * 10;
//! Provide a ceiling for the maximum buffer duration. This is to catch buggy
//! descriptors of audio sources who expose very long buffers of data which are
//! not practical for always-on, low-power use-cases.
-constexpr uint64_t kMaxBufferDuration = kNanosecondsPerSecond * 120;
+constexpr uint64_t kMaxBufferDuration = kOneSecondInNanoseconds * 120;
/**
* @return true if the character is ASCII printable.
@@ -126,7 +125,7 @@
bool validateMinimumAudioSource(const struct chreAudioSource& source) {
// CHQTS requires a 16kHz, PCM-format, 2 second buffer.
constexpr uint32_t kRequiredSampleRate = 16000;
- constexpr uint64_t kRequiredBufferDuration = 2 * kNanosecondsPerSecond;
+ constexpr uint64_t kRequiredBufferDuration = 2 * kOneSecondInNanoseconds;
// Ensure that the minimum buffer size is less than or equal to the required
// size.
diff --git a/apps/chqts/src/general_test/basic_sensor_test_base.cc b/apps/chqts/src/general_test/basic_sensor_test_base.cc
index 3024eda..c686a06 100644
--- a/apps/chqts/src/general_test/basic_sensor_test_base.cc
+++ b/apps/chqts/src/general_test/basic_sensor_test_base.cc
@@ -20,10 +20,13 @@
#include <cstddef>
#include <shared/send_message.h>
+#include <shared/time_util.h>
#include <chre.h>
using nanoapp_testing::MessageType;
+using nanoapp_testing::kOneMillisecondInNanoseconds;
+using nanoapp_testing::kOneSecondInNanoseconds;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendInternalFailureToHost;
using nanoapp_testing::sendStringToHost;
@@ -52,9 +55,7 @@
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
+constexpr uint64_t kEventLoopSlack = 100 * kOneMillisecondInNanoseconds;
uint64_t getEventDuration(const chreSensorThreeAxisData *event) {
uint64_t duration = 0;
@@ -70,7 +71,6 @@
: Test(CHRE_API_VERSION_1_0),
mInMethod(true),
mExternalSamplingStatusChange(false),
- mDoneWithPassiveConfigure(false),
mState(State::kPreStart),
mInstanceId(chreGetInstanceId())
/* All other members initialized later */ {
@@ -121,19 +121,19 @@
} else {
if (!chreSensorConfigure(mSensorHandle, mode,
CHRE_SENSOR_INTERVAL_DEFAULT,
- kNanosecondsPerSecond)) {
+ kOneSecondInNanoseconds)) {
sendFatalFailureToHost("chreSensorConfigure() failed passive with "
"default interval and non-default latency");
}
if (!isOneShotSensor() && !chreSensorConfigure(
- mSensorHandle, mode, kNanosecondsPerSecond,
+ mSensorHandle, mode, kOneSecondInNanoseconds,
CHRE_SENSOR_LATENCY_DEFAULT)) {
sendFatalFailureToHost("chreSensorConfigure() failed passive with "
"non-default interval and default latency");
}
if (!isOneShotSensor() && !chreSensorConfigure(
- mSensorHandle, mode, kNanosecondsPerSecond,
- kNanosecondsPerSecond)) {
+ mSensorHandle, mode, kOneSecondInNanoseconds,
+ kOneSecondInNanoseconds)) {
sendFatalFailureToHost("chreSensorConfigure() failed passive with "
"non-default interval and latency");
}
@@ -176,15 +176,9 @@
sendFatalFailureToHost("chreGetSensorSamplingStatus() failed");
}
- // Nanoapp may start getting events with a passive request. Set the base
- // timestamp to compare against before configuring the sensor.
+ // 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 */
@@ -211,7 +205,7 @@
// 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 */
+ kOneSecondInNanoseconds, /* 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.
@@ -241,6 +235,8 @@
}
void BasicSensorTestBase::finishTest() {
+ checkPassiveConfigure();
+
if (!chreSensorConfigureModeOnly(mSensorHandle,
CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
sendFatalFailureToHost("Unable to configure sensor mode to DONE");
@@ -255,11 +251,14 @@
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");
+ // Interval and latency values are only relevent if the sensor is enabled.
+ if (status.enabled) {
+ 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;
@@ -362,7 +361,7 @@
}
// Passive sensor requests do not guarantee sensors will always be enabled.
// Bypass 'enabled' check for passive configurations.
- if (mDoneWithPassiveConfigure && !eventData->status.enabled) {
+ if (!eventData->status.enabled) {
sendFatalFailureToHost("SamplingChangeEvent disabled the sensor.");
}
@@ -412,10 +411,7 @@
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);
diff --git a/apps/chqts/src/general_test/basic_sensor_test_base.h b/apps/chqts/src/general_test/basic_sensor_test_base.h
index ccbf43a..e268693 100644
--- a/apps/chqts/src/general_test/basic_sensor_test_base.h
+++ b/apps/chqts/src/general_test/basic_sensor_test_base.h
@@ -100,7 +100,6 @@
// 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;
uint32_t mInstanceId;
uint32_t mSensorHandle;
diff --git a/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc b/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc
index 9828a3a..0aae364 100644
--- a/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc
+++ b/apps/chqts/src/general_test/heap_exhaustion_stability_test.cc
@@ -20,9 +20,12 @@
#include <cstddef>
#include <shared/send_message.h>
+#include <shared/time_util.h>
#include <chre.h>
+using nanoapp_testing::kOneMillisecondInNanoseconds;
+using nanoapp_testing::kOneSecondInNanoseconds;
using nanoapp_testing::sendFailureToHost;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
@@ -49,10 +52,8 @@
// 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);
+static const uint64_t kExhaustionDuration = 5 * kOneSecondInNanoseconds;
+static const uint64_t kShortDuration = 10 * kOneMillisecondInNanoseconds;
constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
diff --git a/apps/chqts/src/general_test/timer_cancel_test.cc b/apps/chqts/src/general_test/timer_cancel_test.cc
index 689d868..6a2fa6c 100644
--- a/apps/chqts/src/general_test/timer_cancel_test.cc
+++ b/apps/chqts/src/general_test/timer_cancel_test.cc
@@ -20,9 +20,11 @@
#include <cstddef>
#include <shared/send_message.h>
+#include <shared/time_util.h>
#include <chre.h>
+using nanoapp_testing::kOneMillisecondInNanoseconds;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendInternalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
@@ -36,8 +38,7 @@
* When all of our stages have succeeded, then we send success to the host.
*/
-// 10 milliseconds
-static uint64_t kDuration = UINT64_C(10000000);
+static uint64_t kDuration = 10 * kOneMillisecondInNanoseconds;
namespace general_test {
diff --git a/apps/chqts/src/general_test/timer_set_test.cc b/apps/chqts/src/general_test/timer_set_test.cc
index fb7165b..2a04e9a 100644
--- a/apps/chqts/src/general_test/timer_set_test.cc
+++ b/apps/chqts/src/general_test/timer_set_test.cc
@@ -21,9 +21,12 @@
#include <new>
#include <shared/send_message.h>
+#include <shared/time_util.h>
#include <chre.h>
+using nanoapp_testing::kOneMillisecondInNanoseconds;
+using nanoapp_testing::kOneSecondInNanoseconds;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendInternalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
@@ -44,11 +47,8 @@
* 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;
+static uint64_t kShortDuration = 10 * kOneMillisecondInNanoseconds;
+static uint64_t kLongDuration = kOneSecondInNanoseconds;
namespace general_test {
@@ -79,7 +79,7 @@
sendFatalFailureToHost("Timer triggered too soon ", &mStage);
}
// TODO(b/32179037): Make this check much stricter.
- if (timestamp > (expectedTime + kOneSecond)) {
+ if (timestamp > (expectedTime + kOneSecondInNanoseconds)) {
sendFatalFailureToHost("Timer triggered over a second late ", &mStage);
}
diff --git a/apps/chqts/src/shared/time_util.h b/apps/chqts/src/shared/time_util.h
new file mode 100644
index 0000000..38c9e1e
--- /dev/null
+++ b/apps/chqts/src/shared/time_util.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_TIME_UTIL_H_
+#define _GTS_NANOAPPS_SHARED_TIME_UTIL_H_
+
+#include <cstdint>
+
+namespace nanoapp_testing {
+
+//! The number of milliseconds in one min.
+constexpr uint64_t kOneMinuteInMilliseconds(60000);
+
+//! The number of milliseconds in one second.
+constexpr uint64_t kOneSecondInMilliseconds(1000);
+
+//! The number of nanoseconds in one second.
+constexpr uint64_t kOneSecondInNanoseconds(1000000000);
+
+//! The number of nanoseconds in one millisecond.
+constexpr uint64_t kOneMillisecondInNanoseconds(1000000);
+
+//! The number of nanoseconds in one microsecond.
+constexpr uint64_t kOneMicrosecondInNanoseconds(1000);
+
+//! The number of microseconds in one millisecond.
+constexpr uint64_t kOneMillisecondInMicroseconds(1000);
+
+} // namespace nanoapp_testing
+
+#endif // _GTS_NANOAPPS_SHARED_TIME_UTIL_H_
diff --git a/apps/timer_world/timer_world.cc b/apps/timer_world/timer_world.cc
index 578b799..5c421c7 100644
--- a/apps/timer_world/timer_world.cc
+++ b/apps/timer_world/timer_world.cc
@@ -17,6 +17,7 @@
#include <chre.h>
#include <cinttypes>
+#include "chre/util/nanoapp/audio.h"
#include "chre/util/nanoapp/log.h"
#define LOG_TAG "[TimerWorld]"
diff --git a/core/audio_request_manager.cc b/core/audio_request_manager.cc
index a4f6128..14d8839 100644
--- a/core/audio_request_manager.cc
+++ b/core/audio_request_manager.cc
@@ -19,6 +19,7 @@
#include "chre/core/event_loop_manager.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/system_time.h"
+#include "chre/util/system/debug_dump.h"
/*
* TODO(P1-62e045): Evict pending audio events from the event queue as needed.
@@ -59,52 +60,10 @@
uint64_t bufferDuration,
uint64_t deliveryInterval) {
uint32_t numSamples;
- bool success = validateConfigureSourceArguments(
- handle, enable, bufferDuration, deliveryInterval, &numSamples);
- if (success) {
- size_t requestIndex;
- auto *audioRequest = findAudioRequest(handle, nanoapp->getInstanceId(),
- &requestIndex);
- Nanoseconds nextEventTimestamp = SystemTime::getMonotonicTime()
- + Nanoseconds(deliveryInterval);
- size_t lastNumRequests = mAudioRequestLists[handle].requests.size();
- if (audioRequest == nullptr) {
- // The nanoapp is making a new request for audio data.
- if (enable) {
- mAudioRequestLists[handle].requests.emplace_back(
- nanoapp->getInstanceId(), numSamples,
- Nanoseconds(deliveryInterval), nextEventTimestamp);
- postAudioSamplingChangeEvent(nanoapp->getInstanceId(), handle,
- mAudioRequestLists[handle].available);
- scheduleNextAudioDataEvent(handle);
- } else {
- LOGW("Nanoapp disabling nonexistent audio request");
- }
- } else {
- // The nanoapp is modifying an existing request for audio.
- if (enable) {
- audioRequest->numSamples = numSamples;
- audioRequest->deliveryInterval = Nanoseconds(deliveryInterval);
- audioRequest->nextEventTimestamp = nextEventTimestamp;
- } else {
- mAudioRequestLists[handle].requests.erase(requestIndex);
- }
-
- // Note that if the next request did not change, this call is not strictly
- // necessary. The expectation is that the platform will gracefully handle
- // rescheduling the same request.
- scheduleNextAudioDataEvent(handle);
- }
-
- size_t numRequests = mAudioRequestLists[handle].requests.size();
- if (lastNumRequests == 0 && numRequests > 0) {
- mPlatformAudio.setHandleEnabled(handle, true);
- } else if (lastNumRequests > 0 && numRequests == 0) {
- mPlatformAudio.setHandleEnabled(handle, false);
- }
- }
-
- return success;
+ return validateConfigureSourceArguments(handle, enable, bufferDuration,
+ deliveryInterval, &numSamples)
+ && doConfigureSource(nanoapp->getInstanceId(), handle, enable, numSamples,
+ Nanoseconds(deliveryInterval));
}
void AudioRequestManager::handleAudioDataEvent(
@@ -148,6 +107,35 @@
}
}
+bool AudioRequestManager::logStateToBuffer(char *buffer, size_t *bufferPos,
+ size_t bufferSize) const {
+ bool success = debugDumpPrint(buffer, bufferPos, bufferSize, "\nAudio:\n");
+ for (size_t i = 0; i < mAudioRequestLists.size(); i++) {
+ uint32_t handle = static_cast<uint32_t>(i);
+ struct chreAudioSource source;
+ mPlatformAudio.getAudioSource(handle, &source);
+ success &= debugDumpPrint(buffer, bufferPos, bufferSize,
+ " handle=%" PRIu32 ", name=\"%s\", sampleRate=%" PRIu32
+ ", buffer(ms)=[%" PRIu64 ",%" PRIu64 "], format=%" PRIu8 "\n",
+ handle, source.name, source.sampleRate,
+ Milliseconds(Nanoseconds(source.minBufferDuration)).getMilliseconds(),
+ Milliseconds(Nanoseconds(source.maxBufferDuration)).getMilliseconds(),
+ source.format);
+
+ for (const auto& request : mAudioRequestLists[i].requests) {
+ for (const auto& instanceId : request.instanceIds) {
+ success &= debugDumpPrint(buffer, bufferPos, bufferSize,
+ " nanoappId=%" PRIu32 ", numSamples=%" PRIu32
+ ", interval(ms)=%" PRIu64 "\n", instanceId, request.numSamples,
+ Milliseconds(Nanoseconds(request.deliveryInterval))
+ .getMilliseconds());
+ }
+ }
+ }
+
+ return success;
+}
+
bool AudioRequestManager::validateConfigureSourceArguments(
uint32_t handle, bool enable, uint64_t bufferDuration,
uint64_t deliveryInterval, uint32_t *numSamples) {
@@ -178,13 +166,127 @@
return success;
}
-AudioRequestManager::AudioRequest *AudioRequestManager::findAudioRequest(
- uint32_t handle, uint32_t instanceId, size_t *index) {
+bool AudioRequestManager::doConfigureSource(
+ uint32_t instanceId, uint32_t handle, bool enable, uint32_t numSamples,
+ Nanoseconds deliveryInterval) {
+ size_t requestIndex;
+ size_t requestInstanceIdIndex;
+ auto *audioRequest = findAudioRequestByInstanceId(
+ handle, instanceId, &requestIndex, &requestInstanceIdIndex);
+
+ AudioRequestList& requestList = mAudioRequestLists[handle];
+ size_t lastNumRequests = requestList.requests.size();
+
+ bool success = false;
+ if (audioRequest == nullptr) {
+ if (enable) {
+ success = createAudioRequest(handle, instanceId, numSamples,
+ deliveryInterval);
+ } else {
+ LOGW("Nanoapp disabling nonexistent audio request");
+ }
+ } else {
+ if (audioRequest->instanceIds.size() > 1) {
+ // If there are other clients listening in this configuration, remove
+ // just the instance ID.
+ audioRequest->instanceIds.erase(requestInstanceIdIndex);
+ } else {
+ // If this is the last client listening in this configuration, remove
+ // the entire request.
+ requestList.requests.erase(requestIndex);
+ }
+
+ // If the client is disabling, there is nothing to do, otherwise a request
+ // must be created successfully.
+ success = (!enable || createAudioRequest(handle, instanceId, numSamples,
+ deliveryInterval));
+ }
+
+ if (success) {
+ scheduleNextAudioDataEvent(handle);
+ updatePlatformHandleEnabled(handle, lastNumRequests);
+ }
+
+ return success;
+}
+
+void AudioRequestManager::updatePlatformHandleEnabled(
+ uint32_t handle, size_t lastNumRequests) {
+ size_t numRequests = mAudioRequestLists[handle].requests.size();
+ if (lastNumRequests == 0 && numRequests > 0) {
+ mPlatformAudio.setHandleEnabled(handle, true /* enabled */);
+ } else if (lastNumRequests > 0 && numRequests == 0) {
+ mPlatformAudio.setHandleEnabled(handle, false /* enabled */);
+ }
+}
+
+bool AudioRequestManager::createAudioRequest(
+ uint32_t handle, uint32_t instanceId, uint32_t numSamples,
+ Nanoseconds deliveryInterval) {
+ AudioRequestList& requestList = mAudioRequestLists[handle];
+
+ size_t matchingRequestIndex;
+ auto *matchingAudioRequest = findAudioRequestByConfiguration(
+ handle, numSamples, deliveryInterval, &matchingRequestIndex);
+
+ bool success = false;
+ if (matchingAudioRequest != nullptr) {
+ if (!matchingAudioRequest->instanceIds.push_back(instanceId)) {
+ LOG_OOM();
+ } else {
+ success = true;
+ }
+ } else {
+ Nanoseconds timeNow = SystemTime::getMonotonicTime();
+ Nanoseconds nextEventTimestamp = timeNow + deliveryInterval;
+ if (!requestList.requests.emplace_back(numSamples, deliveryInterval,
+ nextEventTimestamp)) {
+ LOG_OOM();
+ } else if (!requestList.requests.back().instanceIds.push_back(instanceId)) {
+ requestList.requests.pop_back();
+ LOG_OOM();
+ } else {
+ success = true;
+ }
+ }
+
+ if (success) {
+ postAudioSamplingChangeEvent(instanceId, handle, requestList.available);
+ }
+
+ return success;
+}
+
+AudioRequestManager::AudioRequest *AudioRequestManager::
+ findAudioRequestByInstanceId(
+ uint32_t handle, uint32_t instanceId, size_t *index,
+ size_t *instanceIdIndex) {
AudioRequest *foundAudioRequest = nullptr;
auto& requests = mAudioRequestLists[handle].requests;
for (size_t i = 0; i < requests.size(); i++) {
auto& audioRequest = requests[i];
- if (audioRequest.instanceId == instanceId) {
+ size_t foundInstanceIdIndex = audioRequest.instanceIds.find(instanceId);
+ if (foundInstanceIdIndex != audioRequest.instanceIds.size()) {
+ foundAudioRequest = &audioRequest;
+ *index = i;
+ *instanceIdIndex = foundInstanceIdIndex;
+ break;
+ }
+ }
+
+ return foundAudioRequest;
+}
+
+AudioRequestManager::AudioRequest *AudioRequestManager::
+ findAudioRequestByConfiguration(
+ uint32_t handle, uint32_t numSamples, Nanoseconds deliveryInterval,
+ size_t *index) {
+ AudioRequest *foundAudioRequest = nullptr;
+ auto& requests = mAudioRequestLists[handle].requests;
+ for (size_t i = 0; i < requests.size(); i++) {
+ auto& audioRequest = requests[i];
+ if (audioRequest.numSamples == numSamples
+ && audioRequest.deliveryInterval == deliveryInterval) {
foundAudioRequest = &audioRequest;
*index = i;
break;
@@ -216,14 +318,13 @@
if (handle < mAudioRequestLists.size()) {
auto& reqList = mAudioRequestLists[handle];
AudioRequest *nextAudioRequest = reqList.nextAudioRequest;
-
- if (reqList.nextAudioRequest != nullptr) {
- postAudioDataEventFatal(event, nextAudioRequest->instanceId);
+ if (nextAudioRequest != nullptr) {
+ postAudioDataEventFatal(event, nextAudioRequest->instanceIds);
nextAudioRequest->nextEventTimestamp = SystemTime::getMonotonicTime()
+ nextAudioRequest->deliveryInterval;
- reqList.nextAudioRequest = nullptr;
} else {
LOGW("Received audio data event with no pending audio request");
+ mPlatformAudio.releaseAudioDataEvent(event);
}
scheduleNextAudioDataEvent(handle);
@@ -235,8 +336,11 @@
void AudioRequestManager::handleAudioAvailabilitySync(uint32_t handle,
bool available) {
if (handle < mAudioRequestLists.size()) {
- mAudioRequestLists[handle].available = available;
- postAudioSamplingChangeEvents(handle, available);
+ if (mAudioRequestLists[handle].available != available) {
+ mAudioRequestLists[handle].available = available;
+ postAudioSamplingChangeEvents(handle);
+ }
+
scheduleNextAudioDataEvent(handle);
} else {
LOGE("Audio availability handle out of range: %" PRIu32, handle);
@@ -247,6 +351,8 @@
auto& reqList = mAudioRequestLists[handle];
AudioRequest *nextRequest = findNextAudioRequest(handle);
+ // Clear the next request and it will be reset below if needed.
+ reqList.nextAudioRequest = nullptr;
if (reqList.available && nextRequest != nullptr) {
Nanoseconds curTime = SystemTime::getMonotonicTime();
Nanoseconds eventDelay = Nanoseconds(0);
@@ -261,10 +367,12 @@
}
}
-void AudioRequestManager::postAudioSamplingChangeEvents(uint32_t handle,
- bool available) {
- for (const auto& request : mAudioRequestLists[handle].requests) {
- postAudioSamplingChangeEvent(request.instanceId, handle, available);
+void AudioRequestManager::postAudioSamplingChangeEvents(uint32_t handle) {
+ const auto& requestList = mAudioRequestLists[handle];
+ for (const auto& request : requestList.requests) {
+ for (const auto& instanceId : request.instanceIds) {
+ postAudioSamplingChangeEvent(instanceId, handle, requestList.available);
+ }
}
}
@@ -282,16 +390,43 @@
}
void AudioRequestManager::postAudioDataEventFatal(
- struct chreAudioDataEvent *event, uint32_t instanceId) {
- EventLoopManagerSingleton::get()->getEventLoop()
- .postEvent(CHRE_EVENT_AUDIO_DATA, event,
- freeAudioDataEventCallback,
- kSystemInstanceId, instanceId);
+ struct chreAudioDataEvent *event,
+ const DynamicVector<uint32_t>& instanceIds) {
+ if (instanceIds.empty()) {
+ LOGW("Received audio data event for no clients");
+ mPlatformAudio.releaseAudioDataEvent(event);
+ } else {
+ for (const auto& instanceId : instanceIds) {
+ EventLoopManagerSingleton::get()->getEventLoop()
+ .postEvent(CHRE_EVENT_AUDIO_DATA, event,
+ freeAudioDataEventCallback,
+ kSystemInstanceId, instanceId);
+ }
+
+ mAudioDataEventRefCounts.emplace_back(
+ event, static_cast<uint32_t>(instanceIds.size()));
+ }
}
void AudioRequestManager::handleFreeAudioDataEvent(
struct chreAudioDataEvent *audioDataEvent) {
- mPlatformAudio.releaseAudioDataEvent(audioDataEvent);
+ size_t audioDataEventRefCountIndex =
+ mAudioDataEventRefCounts.find(AudioDataEventRefCount(audioDataEvent));
+ if (audioDataEventRefCountIndex == mAudioDataEventRefCounts.size()) {
+ LOGE("Freeing invalid audio data event");
+ } else {
+ auto& audioDataEventRefCount =
+ mAudioDataEventRefCounts[audioDataEventRefCountIndex];
+ if (audioDataEventRefCount.refCount == 0) {
+ LOGE("Attempting to free an event with zero published events");
+ } else {
+ audioDataEventRefCount.refCount--;
+ if (audioDataEventRefCount.refCount == 0) {
+ mAudioDataEventRefCounts.erase(audioDataEventRefCountIndex);
+ mPlatformAudio.releaseAudioDataEvent(audioDataEvent);
+ }
+ }
+ }
}
void AudioRequestManager::freeAudioDataEventCallback(uint16_t eventType,
diff --git a/core/event_loop.cc b/core/event_loop.cc
index e6f80fa..027b7a2 100644
--- a/core/event_loop.cc
+++ b/core/event_loop.cc
@@ -152,7 +152,7 @@
LOGE("App with ID 0x%016" PRIx64 " already exists as instance ID 0x%"
PRIx32, nanoapp->getAppId(), existingInstanceId);
} else if (!mNanoapps.prepareForPush()) {
- LOGE("Failed to allocate space for new nanoapp");
+ LOG_OOM();
} else {
nanoapp->setInstanceId(eventLoopManager->getNextInstanceId());
LOGD("Instance ID %" PRIu32 " assigned to app ID 0x%016" PRIx64,
@@ -433,7 +433,7 @@
const Nanoapp& nanoapp) {
auto *info = memoryAlloc<chreNanoappInfo>();
if (info == nullptr) {
- LOGE("Couldn't alloc app status change event");
+ LOG_OOM();
} else {
info->appId = nanoapp.getAppId();
info->version = nanoapp.getAppVersion();
diff --git a/core/event_loop_manager.cc b/core/event_loop_manager.cc
index 182ddc6..0b0d254 100644
--- a/core/event_loop_manager.cc
+++ b/core/event_loop_manager.cc
@@ -41,22 +41,27 @@
size_t debugStrPos = 0;
if (!mMemoryManager.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("Memory manager debug dump failed.");
+ LOG_OOM();
} else if (!mEventLoop.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("Event loop debug dump failed.");
+ LOG_OOM();
} else if (!mSensorRequestManager.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("Sensor request manager debug dump failed.");
+ LOG_OOM();
} else if (!mGnssManager.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("GNSS manager debug dump failed.");
+ LOG_OOM();
} else if (!mWifiRequestManager.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("Wifi request manager debug dump failed.");
+ LOG_OOM();
} else if (!mWwanRequestManager.logStateToBuffer(debugStr, &debugStrPos,
kDebugStringSize)) {
- LOGE("WWAN request manager debug dump failed.");
+ LOG_OOM();
+#ifdef CHRE_AUDIO_SUPPORT_ENABLED
+ } else if (!mAudioRequestManager.logStateToBuffer(debugStr, &debugStrPos,
+ kDebugStringSize)) {
+ LOG_OOM();
+#endif // CHRE_AUDIO_SUPPORT_ENABLED
}
LOGD("Debug dump used %zu bytes of log buffer", debugStrPos);
}
diff --git a/core/gnss_manager.cc b/core/gnss_manager.cc
index eec4e8b..0939289 100644
--- a/core/gnss_manager.cc
+++ b/core/gnss_manager.cc
@@ -91,7 +91,7 @@
auto *cbState = memoryAlloc<CallbackState>();
if (cbState == nullptr) {
- LOGE("Failed to allocate callback state for GNSS session state change");
+ LOG_OOM();
} else {
cbState->enabled = enabled;
cbState->errorCode = errorCode;
@@ -280,7 +280,7 @@
request.minInterval = minInterval;
success = mRequests.push_back(request);
if (!success) {
- LOGE("Failed to add nanoapp to the list of GNSS session nanoapps");
+ LOG_OOM();
} else {
nanoapp->registerForBroadcastEvent(mReportEventType);
}
@@ -308,7 +308,7 @@
if (!success || updateRequests(enable, minInterval, instanceId)) {
chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
if (event == nullptr) {
- LOGE("Failed to allocate GNSS session async result event");
+ LOG_OOM();
} else {
event->requestType = enable ? mStartRequestType : mStopRequestType;
event->success = success;
diff --git a/core/host_comms_manager.cc b/core/host_comms_manager.cc
index 399db9f..1019628 100644
--- a/core/host_comms_manager.cc
+++ b/core/host_comms_manager.cc
@@ -46,7 +46,7 @@
MessageToHost *msgToHost = mMessagePool.allocate();
if (msgToHost == nullptr) {
- LOGE("Couldn't allocate message to host");
+ LOG_OOM();
} else {
msgToHost->appId = nanoapp->getAppId();
msgToHost->message.wrap(static_cast<uint8_t *>(messageData), messageSize);
@@ -75,7 +75,7 @@
MessageFromHost *msgFromHost = mMessagePool.allocate();
if (msgFromHost == nullptr) {
- LOGE("Couldn't allocate message from host");
+ LOG_OOM();
} else if (!msgFromHost->message.copy_array(
static_cast<const uint8_t *>(messageData), messageSize)) {
LOGE("Couldn't allocate %" PRIu32 " bytes for message data from host "
diff --git a/core/include/chre/core/audio_request_manager.h b/core/include/chre/core/audio_request_manager.h
index 4e7a06e..11caf20 100644
--- a/core/include/chre/core/audio_request_manager.h
+++ b/core/include/chre/core/audio_request_manager.h
@@ -97,6 +97,19 @@
void handleAudioAvailability(uint32_t handle, bool available);
/**
+ * Prints state in a string buffer. Must only be called from the context of
+ * the main CHRE thread.
+ *
+ * @param buffer Pointer to the start of the buffer.
+ * @param bufferPos Pointer to buffer position to start the print (in-out).
+ * @param size Size of the buffer in bytes.
+ *
+ * @return true if entire log printed, false if overflow or error.
+ */
+ bool logStateToBuffer(char *buffer, size_t *bufferPos,
+ size_t bufferSize) const;
+
+ /**
* A convenience function to convert sample count and sample rate into a time
* duration. It is illegal to call this function with a rate of zero.
*
@@ -134,20 +147,27 @@
/ kOneSecondInNanoseconds);
}
+ /**
+ * @return the instance of platform audio to allow platform-specific
+ * funtionality to call it. Example: handling host awake events.
+ */
+ PlatformAudio& getPlatformAudio() {
+ return mPlatformAudio;
+ }
+
private:
/**
* One instance of an audio request from a nanoapp.
*/
struct AudioRequest {
- AudioRequest(uint32_t instanceId, uint32_t numSamples,
- Nanoseconds deliveryInterval, Nanoseconds nextEventTimestamp)
- : instanceId(instanceId),
- numSamples(numSamples),
+ AudioRequest(uint32_t numSamples, Nanoseconds deliveryInterval,
+ Nanoseconds nextEventTimestamp)
+ : numSamples(numSamples),
deliveryInterval(deliveryInterval),
nextEventTimestamp(nextEventTimestamp) {}
- //! The nanoapp instance ID that owns this request.
- uint32_t instanceId;
+ //! The nanoapp instance IDs that own this request.
+ DynamicVector<uint32_t> instanceIds;
//! The number of samples requested for this request.
uint32_t numSamples;
@@ -175,6 +195,49 @@
DynamicVector<AudioRequest> requests;
};
+ /**
+ * Keep track of the number of times an audio data event is published to a
+ * nanoapp.
+ *
+ * TODO: Add support for multicast events to the core event loop to avoid this
+ * kind of logic from appearing in the AudioRequestManager.
+ */
+ struct AudioDataEventRefCount {
+ /**
+ * Constructs an AudioDataEventRefCount object with an uninitialized
+ * refCount to allow searching in a list using the equality comparison
+ * below.
+ *
+ * @param event The event that this object tracks.
+ */
+ explicit AudioDataEventRefCount(struct chreAudioDataEvent *event)
+ : event(event) {}
+
+ AudioDataEventRefCount(struct chreAudioDataEvent *event, uint32_t refCount)
+ : event(event), refCount(refCount) {}
+
+ /**
+ * @param audioDataEventRefCount The other object to perform an equality
+ * comparison against.
+ * @return true if the supplied AudioDataEventRefCount is tracking the same
+ * published event as current object.
+ */
+ bool operator==(const AudioDataEventRefCount& audioDataEventRefCount) {
+ return (event == audioDataEventRefCount.event);
+ }
+
+ //! The event that is ref counted here.
+ struct chreAudioDataEvent *event;
+
+ //! The number of outstanding published events.
+ uint32_t refCount;
+ };
+
+ //! Maps published audio data events to a refcount that is used to determine
+ //! when to let the platform audio implementation know that this audio data
+ //! event no longer needed.
+ DynamicVector<AudioDataEventRefCount> mAudioDataEventRefCounts;
+
//! Maps audio handles to requests from multiple nanoapps for an audio source.
//! The array index implies the audio handle which is being managed.
DynamicVector<AudioRequestList> mAudioRequestLists;
@@ -198,6 +261,43 @@
uint64_t bufferDuration, uint64_t deliveryInterval, uint32_t *numSamples);
/**
+ * Performs the configuration of an audio source with validated arguments. See
+ * configureSource for more details.
+ *
+ * @param instanceId The instanceId of the nanoapp making the request.
+ * @param handle The audio source that is being configured.
+ * @param enable true if enabling the source, false if disabling.
+ * @param numSamples The number of samples being requested.
+ * @param deliveryInterval When to deliver the samples.
+ * @return true if successful, false otherwise.
+ */
+ bool doConfigureSource(uint32_t instanceId, uint32_t handle, bool enable,
+ uint32_t numSamples, Nanoseconds deliveryInterval);
+
+ /**
+ * Notify the platform if a given handle has been enabled or disabled.
+ *
+ * @param handle The handle that may have changed enabled state.
+ * @param lastNumRequests The last number of requests for a handle before it
+ * was reconfigured.
+ */
+ void updatePlatformHandleEnabled(uint32_t handle, size_t lastNumRequests);
+
+ /**
+ * Creates an audio request given a configuration and instance ID for a given
+ * handle.
+ *
+ * @param handle The handle to create a request for.
+ * @param instanceId The instance ID that will own this request.
+ * @param numSamples The number of samples requested.
+ * @param deliveryInterval When to deliver the samples.
+ * @return true if successful, false otherwise.
+ */
+ bool createAudioRequest(
+ uint32_t handle, uint32_t instanceId, uint32_t numSamples,
+ Nanoseconds deliveryInterval);
+
+ /**
* Finds an audio request for a given audio handle and nanoapp instance ID. If
* no existing request is available, nullptr is returned.
*
@@ -206,11 +306,30 @@
* @param instanceId The nanoapp instance ID that owns the existing request
* for this handle.
* @param index Populated with the index of the request if it was found.
+ * @param instanceIdIndex Populated with the index of the instance ID within
+ * the returned audio request if it was found.
* @return The AudioRequest for this handle and instanceId, nullptr if not
* found.
*/
- AudioRequest *findAudioRequest(uint32_t handle, uint32_t instanceId,
- size_t *index);
+ AudioRequest *findAudioRequestByInstanceId(
+ uint32_t handle, uint32_t instanceId, size_t *index,
+ size_t *instanceIdIndex);
+
+ /**
+ * Finds an audio request for a given handle and configuration. If no existing
+ * request is available, nullptr is returned.
+ *
+ * @param handle The audio handle to query for. This must be guaranteed by the
+ * caller to be less than the size of the mAudioRequestLists member.
+ * @param numSamples The number of samples to match for.
+ * @param deliveryInterval The delivery interval to match for.
+ * @param index Populated with the index of the request if it was found.
+ * @return The AudioRequest for this handle and configuration, nullptr if not
+ * found.
+ */
+ AudioRequest *findAudioRequestByConfiguration(
+ uint32_t handle, uint32_t numSamples, Nanoseconds deliveryInterval,
+ size_t *index);
/**
* Finds the next expiring request for audio data for a given handle.
@@ -246,13 +365,11 @@
/**
* Posts CHRE_EVENT_AUDIO_SAMPLING_CHANGE events to all nanoapps subscribed to
- * the supplied handle.
+ * the supplied handle with the current availability of the source.
*
* @param handle The handle for the audio source that is changing.
- * @param available true if audio is available for the supplied handle, false
- * otherwise.
*/
- void postAudioSamplingChangeEvents(uint32_t handle, bool available);
+ void postAudioSamplingChangeEvents(uint32_t handle);
/**
* Posts a CHRE_EVENT_AUDIO_SAMPLING_CHANGE event to the specified nanoapp.
@@ -272,10 +389,10 @@
* requirements of the API without posting an event.
*
* @param audioDataEvent The audio data event to send to a nanoapp.
- * @param instanceId The nanoapp instance ID to direct the event to.
+ * @param instanceIds The list of nanoapp instance IDs to direct the event to.
*/
void postAudioDataEventFatal(struct chreAudioDataEvent *event,
- uint32_t instanceId);
+ const DynamicVector<uint32_t>& instanceIds);
/**
* Invoked by the freeAudioDataEventCallback to decrement the reference count
diff --git a/core/include/chre/core/event_loop_manager.h b/core/include/chre/core/event_loop_manager.h
index c1991ff..50d9dec 100644
--- a/core/include/chre/core/event_loop_manager.h
+++ b/core/include/chre/core/event_loop_manager.h
@@ -59,6 +59,7 @@
WifiHandleFailedRanging,
WifiHandleRangingEvent,
AudioAvailabilityChange,
+ AudioHandleHostAwake,
};
//! The function signature of a system callback mirrors the CHRE event free
diff --git a/core/include/chre/core/sensor_type.h b/core/include/chre/core/sensor_type.h
index e542794..047f5ad 100644
--- a/core/include/chre/core/sensor_type.h
+++ b/core/include/chre/core/sensor_type.h
@@ -58,6 +58,7 @@
VendorType0,
VendorType1,
VendorType2,
+ VendorType3,
// Note to future developers: don't forget to update the implementation of
// 1) getSensorTypeName,
@@ -85,6 +86,7 @@
Vendor0,
Vendor1,
Vendor2,
+ Vendor3,
Unknown,
};
diff --git a/core/sensor_type.cc b/core/sensor_type.cc
index 4bac1eb..d1a486f 100644
--- a/core/sensor_type.cc
+++ b/core/sensor_type.cc
@@ -62,6 +62,8 @@
return "Vendor Type 1";
case SensorType::VendorType2:
return "Vendor Type 2";
+ case SensorType::VendorType3:
+ return "Vendor Type 3";
default:
CHRE_ASSERT(false);
return "";
@@ -116,6 +118,8 @@
return SensorType::VendorType1;
case (CHRE_SENSOR_TYPE_VENDOR_START + 2):
return SensorType::VendorType2;
+ case (CHRE_SENSOR_TYPE_VENDOR_START + 3):
+ return SensorType::VendorType3;
default:
return SensorType::Unknown;
}
@@ -157,6 +161,8 @@
return (CHRE_SENSOR_TYPE_VENDOR_START + 1);
case SensorType::VendorType2:
return (CHRE_SENSOR_TYPE_VENDOR_START + 2);
+ case SensorType::VendorType3:
+ return (CHRE_SENSOR_TYPE_VENDOR_START + 3);
default:
// Update implementation to prevent undefined or SensorType::Unknown from
// being used.
@@ -201,12 +207,16 @@
return SensorSampleType::Occurrence;
case SensorType::Proximity:
return SensorSampleType::Byte;
+#ifdef CHREX_SENSOR_SUPPORT
case SensorType::VendorType0:
return SensorSampleType::Vendor0;
case SensorType::VendorType1:
return SensorSampleType::Vendor1;
case SensorType::VendorType2:
return SensorSampleType::Vendor2;
+ case SensorType::VendorType3:
+ return SensorSampleType::Vendor3;
+#endif // CHREX_SENSOR_SUPPORT
case SensorType::Unknown:
return SensorSampleType::Unknown;
default:
diff --git a/core/wifi_request_manager.cc b/core/wifi_request_manager.cc
index 0df3a2a..651ea80 100644
--- a/core/wifi_request_manager.cc
+++ b/core/wifi_request_manager.cc
@@ -109,7 +109,7 @@
success = req.targetList.copy_array(params->targetList,
params->targetListLen);
if (!success) {
- LOGE("Couldn't make copy of target list");
+ LOG_OOM();
mPendingRangingRequests.pop_back();
}
}
@@ -168,7 +168,7 @@
auto *cbState = memoryAlloc<CallbackState>();
if (cbState == nullptr) {
- LOGE("Failed to allocate callback state for scan monitor state change");
+ LOG_OOM();
} else {
cbState->enabled = enabled;
cbState->errorCode = errorCode;
@@ -194,7 +194,7 @@
auto *cbState = memoryAlloc<CallbackState>();
if (cbState == nullptr) {
- LOGE("Failed to allocate callback state for wifi scan response");
+ LOG_OOM();
} else {
cbState->pending = pending;
cbState->errorCode = errorCode;
@@ -350,8 +350,7 @@
// nanoapps.
success = mScanMonitorNanoapps.push_back(instanceId);
if (!success) {
- LOGE("Failed to add nanoapp to the list of scan monitoring "
- "nanoapps");
+ LOG_OOM();
} else {
nanoapp->registerForBroadcastEvent(CHRE_EVENT_WIFI_SCAN_RESULT);
}
@@ -380,7 +379,7 @@
if (!success || updateNanoappScanMonitoringList(enable, nanoappInstanceId)) {
chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
if (event == nullptr) {
- LOGE("Failed to allocate wifi scan monitor async result event");
+ LOG_OOM();
} else {
event->requestType = CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR;
event->success = success;
@@ -416,7 +415,7 @@
bool eventPosted = false;
chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
if (event == nullptr) {
- LOGE("Failed to allocate wifi scan request async result event");
+ LOG_OOM();
} else {
event->requestType = CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN;
event->success = success;
@@ -553,7 +552,7 @@
} else {
auto *event = memoryAlloc<struct chreAsyncResult>();
if (event == nullptr) {
- LOGE("Couldn't allocate ranging async result");
+ LOG_OOM();
} else {
const PendingRangingRequest& req = mPendingRangingRequests.front();
diff --git a/host/common/audio_stress_test/audio_stress_test.cc b/host/common/audio_stress_test/audio_stress_test.cc
new file mode 100644
index 0000000..fbef0c8
--- /dev/null
+++ b/host/common/audio_stress_test/audio_stress_test.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/util/nanoapp/app_id.h"
+#include "chre_host/host_protocol_host.h"
+#include "chre_host/log.h"
+#include "chre_host/socket_client.h"
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <fstream>
+#include <thread>
+
+#include <cutils/sockets.h>
+#include <utils/StrongPointer.h>
+
+/**
+ * @file
+ * A test utility that loads the audio stress test nanoapp and quits.
+ */
+
+using android::sp;
+using android::chre::getStringFromByteVector;
+using android::chre::FragmentedLoadTransaction;
+using android::chre::HostProtocolHost;
+using android::chre::IChreMessageHandlers;
+using android::chre::SocketClient;
+using flatbuffers::FlatBufferBuilder;
+
+// Aliased for consistency with the way these symbols are referenced in
+// CHRE-side code
+namespace fbs = ::chre::fbs;
+
+namespace {
+
+class SocketCallbacks : public SocketClient::ICallbacks,
+ public IChreMessageHandlers {
+ public:
+ void onMessageReceived(const void *data, size_t length) override {
+ if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
+ LOGE("Failed to decode message");
+ }
+ }
+
+ void onConnected() override {
+ LOGI("Socket (re)connected");
+ }
+
+ void onConnectionAborted() override {
+ LOGI("Socket (re)connection aborted");
+ }
+
+ void onDisconnected() override {
+ LOGI("Socket disconnected");
+ }
+
+ void handleLoadNanoappResponse(const fbs::LoadNanoappResponseT& response)
+ override {
+ LOGI("Got load nanoapp response, transaction ID 0x%" PRIx32 " result %d",
+ response.transaction_id, response.success);
+ }
+};
+
+void sendLoadNanoappRequest(SocketClient& client, const char *filename,
+ uint64_t appId, uint32_t appVersion) {
+ std::ifstream file(filename, std::ios::binary | std::ios::ate);
+ if (!file) {
+ LOGE("Couldn't open file '%s': %s", filename, strerror(errno));
+ return;
+ }
+ ssize_t size = file.tellg();
+ file.seekg(0, std::ios::beg);
+
+ std::vector<uint8_t> buffer(size);
+ if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) {
+ LOGE("Couldn't read from file: %s", strerror(errno));
+ return;
+ }
+
+ // Perform loading with 1 fragment for simplicity
+ FlatBufferBuilder builder(size + 128);
+ FragmentedLoadTransaction transaction = FragmentedLoadTransaction(
+ 1 /* transactionId */, appId, appVersion,
+ 0x01000000 /* targetApiVersion */, buffer,
+ buffer.size() /* fragmentSize */);
+ HostProtocolHost::encodeFragmentedLoadNanoappRequest(
+ builder, transaction.getNextRequest());
+
+ LOGI("Sending load nanoapp request (%" PRIu32 " bytes total w/%zu bytes of "
+ "payload)", builder.GetSize(), buffer.size());
+ if (!client.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
+ LOGE("Failed to send message");
+ }
+}
+
+} // anonymous namespace
+
+int main() {
+ SocketClient client;
+ sp<SocketCallbacks> callbacks = new SocketCallbacks();
+
+ if (!client.connect("chre", callbacks)) {
+ LOGE("Couldn't connect to socket");
+ } else {
+ sendLoadNanoappRequest(client, "/data/audio_stress_test.so",
+ chre::kAudioStressTestAppId,
+ 1 /* appVersion */);
+ }
+
+ return 0;
+}
diff --git a/host/msm/daemon/chre_daemon.cc b/host/msm/daemon/chre_daemon.cc
index 4194659..f2f2921 100644
--- a/host/msm/daemon/chre_daemon.cc
+++ b/host/msm/daemon/chre_daemon.cc
@@ -392,17 +392,19 @@
}
/**
- * Unloads the LPMA use case via the SoundTrigger HAL HIDL service.
+ * Unloads the LPMA use case via the SoundTrigger HAL HIDL service. This
+ * function does not indicate success/failure as it is expected that even in the
+ * event of a failure to unload, the use case will be unloaded. As long as the
+ * sound trigger HAL received the request we can be assured that the use case
+ * will be unloaded (even if it means reseting the codec or otherwise).
*
* @param lpmaHandle A handle that was previously produced by the setLpmaEnabled
* function. This is the handle that is unloaded from the ST HAL to
* disable LPMA.
- * @return true if LPMA was disabled successfully, false otherwise.
*/
-static bool unloadLpma(SoundModelHandle lpmaHandle) {
+static void unloadLpma(SoundModelHandle lpmaHandle) {
LOGD("Unloading LPMA");
- bool unloaded = false;
sp<ISoundTriggerHw> stHal = ISoundTriggerHw::getService();
if (stHal == nullptr) {
LOGE("Failed to get ST HAL service for LPMA unload");
@@ -412,7 +414,6 @@
if (hidlResult.isOk()) {
if (hidlResult == 0) {
LOGI("Unloaded LPMA");
- unloaded = true;
} else {
LOGE("Failed to unload LPMA with %" PRId32, int32_t(hidlResult));
}
@@ -421,8 +422,6 @@
hidlResult.description().c_str());
}
}
-
- return unloaded;
}
static void *chreLpmaEnableThread(void *arg) {
@@ -445,8 +444,14 @@
releaseWakeLock(); // Allow the system to suspend while waiting.
pthread_cond_wait(&state->cond, &state->mutex);
acquireWakeLock(); // Ensure the system stays up while retrying.
- } else if ((state->targetLpmaEnabled && loadLpma(&lpmaHandle))
- || (!state->targetLpmaEnabled && unloadLpma(lpmaHandle))) {
+ } else if (state->targetLpmaEnabled && loadLpma(&lpmaHandle)) {
+ state->currentLpmaEnabled = state->targetLpmaEnabled;
+ } else if (!state->targetLpmaEnabled) {
+ // Regardless of whether the use case fails to unload, set the
+ // currentLpmaEnabled to the targetLpmaEnabled. This will allow the next
+ // enable request to proceed. After a failure to unload occurs, the
+ // supplied handle is invalid and should not be unloaded again.
+ unloadLpma(lpmaHandle);
state->currentLpmaEnabled = state->targetLpmaEnabled;
} else {
// Unlock while delaying to avoid blocking the client thread. No shared
diff --git a/platform/android/platform_audio.cc b/platform/android/platform_audio.cc
index bb97146..8844c1c 100644
--- a/platform/android/platform_audio.cc
+++ b/platform/android/platform_audio.cc
@@ -159,7 +159,7 @@
}
bool PlatformAudio::getAudioSource(uint32_t handle,
- chreAudioSource *audioSource) {
+ chreAudioSource *audioSource) const {
bool success = false;
if (handle == 0) {
audioSource->name = "Default Android Audio Input";
diff --git a/platform/include/chre/platform/platform_audio.h b/platform/include/chre/platform/platform_audio.h
index 8e755ff..0757ffd 100644
--- a/platform/include/chre/platform/platform_audio.h
+++ b/platform/include/chre/platform/platform_audio.h
@@ -126,7 +126,7 @@
* @param audioSource the chreAudioSource to populate with details of the
* audio source. This pointer must never be null.
*/
- bool getAudioSource(uint32_t handle, chreAudioSource *audioSource);
+ bool getAudioSource(uint32_t handle, chreAudioSource *audioSource) const;
};
} // namespace chre
diff --git a/platform/linux/platform_audio.cc b/platform/linux/platform_audio.cc
index c146edc..51382a7 100644
--- a/platform/linux/platform_audio.cc
+++ b/platform/linux/platform_audio.cc
@@ -108,7 +108,7 @@
}
bool PlatformAudio::getAudioSource(uint32_t handle,
- chreAudioSource *audioSource) {
+ chreAudioSource *audioSource) const {
bool success = (handle < gAudioSources.size());
if (success) {
const auto& source = gAudioSources[handle];
diff --git a/platform/platform.mk b/platform/platform.mk
index 6be1352..6a3ed77 100644
--- a/platform/platform.mk
+++ b/platform/platform.mk
@@ -70,8 +70,13 @@
SLPI_SEE_CFLAGS += -I$(SLPI_PREFIX)/ssc/inc/pb
SLPI_SEE_CFLAGS += -DCHRE_SLPI_SEE
+
+# Needed to define __SIZEOF_ATTR_THREAD in sns_osa_thread.h, included in
+# sns_memmgr.h.
SLPI_SEE_CFLAGS += -DSSC_TARGET_HEXAGON
-SLPI_SEE_CFLAGS += -DSNS_ISLAND_INCLUDE_QCM
+
+# Defined in slpi_proc/ssc/build/ssc.scons
+SLPI_SEE_CFLAGS += -DPB_FIELD_16BIT
# SLPI-specific Source Files ###################################################
@@ -122,30 +127,24 @@
# SLPI/SEE-specific Source Files ###############################################
-# TODO(P2-c001d8): (b/68860346) replace pb-related source files with exported
-# symbols.
-SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/utils/nanopb/src/pb_common.c
-SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/utils/nanopb/src/pb_decode.c
-SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/utils/nanopb/src/pb_encode.c
-
SLPI_SEE_SRCS += platform/slpi/see/island_vote_client.cc
SLPI_SEE_SRCS += platform/slpi/see/platform_sensor.cc
SLPI_SEE_SRCS += platform/slpi/see/power_control_manager.cc
+SLPI_SEE_SRCS += platform/slpi/see/see_cal_helper.cc
SLPI_SEE_SRCS += platform/slpi/see/see_helper.cc
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/framework/cm/pb/sns_client.pb.c
-SLPI_SEE_QSK_SRCS += $(SLPI_PREFIX)/ssc/framework/qcm/pb/sns_client_qsocket.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/framework/suid_sensor/pb/sns_suid.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_cal.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_physical_sensor_test.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_proximity.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_remote_proc_state.pb.c
+SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_resampler.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_std.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_std_sensor.pb.c
SLPI_SEE_SRCS += $(SLPI_PREFIX)/ssc/sensors/pb/sns_std_type.pb.c
-SLPI_SEE_SRCS += $(SLPI_PREFIX)/chre/chre/src/system/chre/platform/slpi/sns_osa.c
-SLPI_SEE_QSK_SRCS += $(SLPI_PREFIX)/chre/chre/src/system/chre/platform/slpi/sns_qsocket_client.c
+SLPI_SEE_QSK_SRCS += $(SLPI_PREFIX)/chre/chre/src/system/chre/platform/slpi/sns_qmi_client_alt.c
SLPI_SEE_QMI_SRCS += $(SLPI_PREFIX)/chre/chre/src/system/chre/platform/slpi/sns_qmi_client.c
# Simulator-specific Compiler Flags ############################################
diff --git a/platform/shared/chre_api_sensor.cc b/platform/shared/chre_api_sensor.cc
index 13b8e71..b186dce 100644
--- a/platform/shared/chre_api_sensor.cc
+++ b/platform/shared/chre_api_sensor.cc
@@ -30,7 +30,35 @@
using chre::getSensorModeFromEnum;
using chre::getSensorTypeFromUnsignedInt;
+#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+namespace {
+constexpr uint8_t kBigImageAccelSensorType =
+ (CHRE_SENSOR_TYPE_VENDOR_START + 3);
+} // anonymous namespace
+#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+
DLL_EXPORT bool chreSensorFindDefault(uint8_t sensorType, uint32_t *handle) {
+#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+ // HACK: as SEE does not support software batching in uimg via QCM/uQSockets,
+ // reroute requests for accelerometer from a big image nanoapp to a separate
+ // sensor type internally. Accel is the only always-on sensor used today by
+ // big image nanoapps, and this change allows these requests to transparently
+ // go to a separate sensor implementation that supports uimg batching via
+ // CM/QMI.
+ // TODO(P2-5673a9): work with QC to determine a better long-term solution
+ chre::Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
+ if (!nanoapp->isUimgApp()) {
+ if (sensorType == CHRE_SENSOR_TYPE_ACCELEROMETER) {
+ sensorType = kBigImageAccelSensorType;
+ } else if (sensorType == kBigImageAccelSensorType) {
+ // Since we have an accompanying hack in PlatformNanoapp::handleEvent(),
+ // hide the vendor sensor type from big image nanoapps as we're unable to
+ // deliver events for it
+ return false;
+ }
+ }
+#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+
SensorType validatedSensorType = getSensorTypeFromUnsignedInt(sensorType);
return (validatedSensorType != SensorType::Unknown
&& EventLoopManagerSingleton::get()->getSensorRequestManager()
@@ -47,6 +75,15 @@
if (info != nullptr) {
success = EventLoopManagerSingleton::get()->getSensorRequestManager().
getSensorInfo(sensorHandle, *nanoapp, info);
+
+ // The distinction between big/uimg accel should be abstracted away from
+ // big image nanoapps, so overwrite any platform implementation here.
+#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+ if (!nanoapp->isUimgApp() && info->sensorType == kBigImageAccelSensorType) {
+ info->sensorType = CHRE_SENSOR_TYPE_ACCELEROMETER;
+ }
+#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+
}
return success;
}
diff --git a/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h b/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
index 283cef8..f112e99 100644
--- a/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
+++ b/platform/slpi/include/chre/platform/slpi/see/island_vote_client.h
@@ -67,6 +67,10 @@
void decrementBigImageRefCount();
private:
+ //! The maximum allowed duration to be voted into big image by
+ //! incrementBigImageRefCount before a FATAL_ERROR is triggered.
+ static constexpr Seconds kSeeMaxBigImageDuration = Seconds(300);
+
//! Last big image request made through voteBigImage().
bool mLastBigImageRequest = false;
@@ -93,6 +97,16 @@
* @return true if the vote returned success.
*/
bool voteSnsPowerMode(bool bigImage);
+
+ /**
+ * Check how long the system has been voted into big image due to
+ * incrementBigImageRefCount. If longer than kSeeMaxBigImageDuration, trigger
+ * a crash.
+ *
+ * @return the duration in milliseconds since the system has been voted into
+ * big image due to incrementBigImageRefCount.
+ */
+ uint64_t checkBigImageDuration() const;
#endif // CHRE_SLPI_UIMG_ENABLED
};
diff --git a/platform/slpi/include/chre/platform/slpi/see/see_cal_helper.h b/platform/slpi/include/chre/platform/slpi/see/see_cal_helper.h
new file mode 100644
index 0000000..9015649
--- /dev/null
+++ b/platform/slpi/include/chre/platform/slpi/see/see_cal_helper.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_SEE_SEE_CAL_HELPER_H_
+#define CHRE_PLATFORM_SLPI_SEE_SEE_CAL_HELPER_H_
+
+extern "C" {
+
+#include "sns_client.h"
+
+} // extern "C"
+
+#include "sns_suid.pb.h"
+
+#include "chre/core/sensor_type.h"
+#include "chre/platform/mutex.h"
+#include "chre/util/non_copyable.h"
+#include "chre/util/optional.h"
+
+namespace chre {
+
+class SeeHelper;
+
+/**
+ * Helps manage and apply sensor calibration data provided through SEE.
+ */
+class SeeCalHelper : public NonCopyable {
+ public:
+ /**
+ * Applies cached calibration (if any) to raw 3-axis sensor readings.
+ * Thread-safe.
+ *
+ * @param sensorType Type of sensor that generated the sample
+ * @param input 3-axis raw sample {x,y,z}
+ * @param output Location to store sample with calibration applied (can be
+ * same as input)
+ */
+ void applyCalibration(SensorType sensorType, const float input[3],
+ float output[3]) const;
+
+ /**
+ * Get the cached SUID of a calibration sensor that corresponds to the
+ * specified sensorType.
+ *
+ * @param sensorType The sensor type of the calibration sensor.
+ *
+ * @return A constant reference to the calibration sensor's SUID if present.
+ * Otherwise, a reference to sns_suid_sensor_init_zero is returned.
+ */
+ const sns_std_suid& getCalSuidFromSensorType(SensorType sensorType) const;
+
+ /**
+ * Uses the supplied SeeHelper instance to register for updates to all
+ * supported SEE calibration sensors. The SeeHelper instance should then pass
+ * decoded calibration data to updateCalibration() and use applyCalibration()
+ * as needed.
+ *
+ * @param seeHelper SeeHelper instance to use when looking up calibration
+ * sensor SUIDs and registering for their output
+ *
+ * @return true if all SEE calibration sensors were successfully registered
+ */
+ bool registerForCalibrationUpdates(SeeHelper& seeHelper);
+
+ /**
+ * Updates the cached calibration data used in subsequent calls to
+ * applyCalibration. Thread-safe.
+ *
+ * @param suid Sensor UID associated with the incoming calibration data
+ * @param hasBias true if bias was decoded from the proto
+ * @param bias 3-axis bias; only valid if hasBias is true
+ * @param hasScale true if scale was decoded from the proto
+ * @param scale 3-axis scale factor; only valid if hasScale is true
+ * @param hasMatrix true if matrix was decoded from the proto
+ * @param matrix 3x3 compensation matrix; only valid if hasMatrix is true
+ * @param accuracy Android accuracy rating of the calibration quality (see
+ * sns_std_sensor_sample_status)
+ */
+ void updateCalibration(const sns_std_suid& suid, bool hasBias, float bias[3],
+ bool hasScale, float scale[3], bool hasMatrix,
+ float matrix[9], uint8_t accuracy);
+
+ private:
+ //! A struct to store a sensor's calibration data
+ struct SeeCalData {
+ float bias[3];
+ float scale[3];
+ float matrix[9];
+ bool hasBias;
+ bool hasScale;
+ bool hasMatrix;
+ uint8_t accuracy;
+ };
+
+ //! A struct to store a cal sensor's UID and its cal data.
+ struct SeeCalInfo {
+ Optional<sns_std_suid> suid;
+ SeeCalData cal;
+ };
+
+ //! The list of SEE cal sensors supported.
+ enum class SeeCalSensor : size_t {
+ AccelCal,
+ GyroCal,
+ MagCal,
+ NumCalSensors,
+ };
+
+ //! A convenience constant.
+ static constexpr size_t kNumSeeCalSensors = static_cast<size_t>(
+ SeeCalSensor::NumCalSensors);
+
+ //! Protects access to calibration data, which may be used in multiple threads
+ mutable Mutex mMutex;
+
+ //! Cal info of all the cal sensors.
+ SeeCalInfo mCalInfo[kNumSeeCalSensors] = {};
+
+ //! Map SensorType to associated index in mCalInfo
+ static size_t getCalIndexFromSensorType(SensorType sensorType);
+
+ //! Map index in mCalInfo to SEE sensor data type string
+ static const char *getDataTypeForCalSensorIndex(size_t calSensorIndex);
+
+ //! Map SUID to associated index in mCalInfo
+ size_t getCalIndexFromSuid(const sns_std_suid& suid) const;
+};
+
+} // namespace chre
+
+#endif // CHRE_PLATFORM_SLPI_SEE_SEE_CAL_HELPER_H_
diff --git a/platform/slpi/include/chre/platform/slpi/see/see_client.h b/platform/slpi/include/chre/platform/slpi/see/see_client.h
index 0665832..64064f1 100644
--- a/platform/slpi/include/chre/platform/slpi/see/see_client.h
+++ b/platform/slpi/include/chre/platform/slpi/see/see_client.h
@@ -40,6 +40,14 @@
return SeeHelperSingleton::get();
}
+#ifdef CHRE_SLPI_UIMG_ENABLED
+typedef Singleton<BigImageSeeHelper> BigImageSeeHelperSingleton;
+
+inline SeeHelper *getBigImageSeeHelper() {
+ return BigImageSeeHelperSingleton::get();
+}
+#endif // CHRE_SLPI_UIMG_ENABLED
+
} // namespace chre
#endif // CHRE_PLATFORM_SLPI_SEE_SEE_CLIENT_H_
diff --git a/platform/slpi/include/chre/platform/slpi/see/see_helper.h b/platform/slpi/include/chre/platform/slpi/see/see_helper.h
index 32481c3..a8bc173 100644
--- a/platform/slpi/include/chre/platform/slpi/see/see_helper.h
+++ b/platform/slpi/include/chre/platform/slpi/see/see_helper.h
@@ -21,14 +21,17 @@
#include "sns_client.h"
-} // extern "C"
+} // extern "C"
+
+#include "sns_suid.pb.h"
#include "chre/core/sensor_type.h"
#include "chre/platform/condition_variable.h"
#include "chre/platform/mutex.h"
-#include "chre/platform/slpi/see/see_helper_internal.h"
+#include "chre/platform/slpi/see/see_cal_helper.h"
#include "chre/util/dynamic_vector.h"
#include "chre/util/non_copyable.h"
+#include "chre/util/optional.h"
#include "chre/util/time.h"
#include "chre/util/unique_ptr.h"
@@ -76,6 +79,9 @@
//! Default timeout for sendReq indication
constexpr Nanoseconds kDefaultSeeIndTimeout = Seconds(2);
+//! Allowed number of consecutive missing responses.
+constexpr uint32_t kSeeNumMissingResp = 5;
+
//! Length of the char array to store sensor string attributes.
constexpr size_t kSeeAttrStrValLen = 64;
@@ -114,14 +120,43 @@
sns_std_suid suid;
SensorType sensorType;
sns_client *client;
+ //! The SUID of the underlying physical sensor, different from suid if
+ //! resampler is used.
+ sns_std_suid physicalSuid;
};
/**
+ * Constructor for a SeeHelper that manages its own SeeCalHelper
+ */
+ SeeHelper();
+
+ /**
+ * Constructor for a SeeHelper that uses the supplied SeeCalHelper object
+ * rather than creating its own. Caller must ensure that the lifetime of the
+ * SeeCalHelper object exceeds the lifetime of this SeeHelper instance.
+ *
+ * TODO: this would be a good case for a shared ptr implementation
+ *
+ * @param calHelper Non-null pointer to a calibration helper object to use
+ */
+ SeeHelper(SeeCalHelper *calHelper);
+
+ /**
* Deinits clients before destructing this object.
*/
~SeeHelper();
/**
+ * Makes a request to SEE to enable an on-change sensor, with no additional
+ * payload. Can be used for registering a calibration sensor, for example.
+ *
+ * @param suid Sensor UID, usually determined via findSuidSync()
+ *
+ * @return true on success
+ */
+ bool configureOnChangeSensor(const sns_std_suid& suid, bool enable);
+
+ /**
* A synchronous call to discover SUID(s) that supports the specified data
* type. This API will clear the provided dynamic vector before populating it.
*
@@ -165,6 +200,13 @@
bool getAttributesSync(const sns_std_suid& suid, SeeAttributes *attr);
/**
+ * @return the SeeCalHelper instance used by this SeeHelper
+ */
+ SeeCalHelper *getCalHelper() {
+ return mCalHelper;
+ }
+
+ /**
* Initializes and waits for the sensor client service to become available,
* and obtains remote_proc and cal sensors' info for future operations. This
* function must be called first to initialize the object and be called only
@@ -174,11 +216,15 @@
* handle all async requests with callback data type defined in
* the interface.
* @param timeout The wait timeout in microseconds.
+ * @param skipDefaultSensorInit If true, don't register remote proc status and
+ * calibration sensors (e.g. if another SeeHelper
+ * instance will manage these)
*
* @return true if all initialization steps succeeded.
*/
bool init(SeeHelperCallbackInterface *cbIf,
- Microseconds timeout = kDefaultSeeWaitTimeout);
+ Microseconds timeout = kDefaultSeeWaitTimeout,
+ bool skipDefaultSensorInit = false);
/**
* Makes a sensor request to SEE.
@@ -201,13 +247,14 @@
*
* @param sensorType The SensorType to register.
* @param suid The SUID of the sensor.
+ * @param resample Whether to resample this sensorType.
* @param prevRegistered A non-null pointer to a boolean that indicates
* whether the SUID/SensorType pair has been previously registered.
*
* @return true if the SUID/SensorType pair was successfully registered.
*/
bool registerSensor(SensorType sensorType, const sns_std_suid& suid,
- bool *prevRegistered);
+ bool resample, bool *prevRegistered);
/**
* Checks whether the given SensorType has been successfully registered
@@ -226,6 +273,9 @@
decltype(sns_client_send) *sns_client_send;
};
+ //! Contains the API this SeeHelper instance uses to interact with SEE
+ const SnsClientApi *mSnsClientApi = &kDefaultApi;
+
/**
* Get the cached SUID of a calibration sensor that corresponds to the
* specified sensorType.
@@ -235,7 +285,9 @@
* @return A constant reference to the calibration sensor's SUID if present.
* Otherwise, a reference to sns_suid_sensor_init_zero is returned.
*/
- const sns_std_suid& getCalSuidFromSensorType(SensorType sensorType) const;
+ const sns_std_suid& getCalSuidFromSensorType(SensorType sensorType) const {
+ return mCalHelper->getCalSuidFromSensorType(sensorType);
+ }
/**
* A convenience method to send a request and wait for the indication if it's
@@ -259,10 +311,6 @@
timeoutResp, timeoutInd);
}
- void setSnsClientApi(const SnsClientApi *api) {
- mSnsClientApi = api;
- }
-
private:
static const SnsClientApi kDefaultApi;
@@ -308,21 +356,20 @@
//! A transaction ID that increments for each request.
uint32_t mCurrentTxnId = 0;
+ //! The number of consecutive missing responses.
+ uint32_t mNumMissingResp = 0;
+
//! The SUID for the remote_proc sensor.
Optional<sns_std_suid> mRemoteProcSuid;
- //! Cal info of all the cal sensors.
- SeeCalInfo mCalInfo[kNumSeeCalSensors];
+ //! The SUID for the resampler sensor.
+ Optional<sns_std_suid> mResamplerSuid;
- //! Contains the API this SeeHelper instance uses to interact with SEE
- const SnsClientApi *mSnsClientApi = &kDefaultApi;
+ //! Handles sensor calibration data
+ SeeCalHelper *mCalHelper;
- /**
- * Initializes SEE calibration sensors and makes data request.
- *
- * @return true if cal sensor have been succcessfully initialized.
- */
- bool initCalSensors();
+ //! true if we own the memory to mCalHelper and should free it when done
+ bool mOwnsCalHelper;
/**
* Initializes the SEE remote processor sensor and makes a data request.
@@ -332,6 +379,13 @@
bool initRemoteProcSensor();
/**
+ * Initializes the SEE resampler sensor.
+ *
+ * @return true if the resampler sensor was successfully initialized.
+ */
+ bool initResamplerSensor();
+
+ /**
* Sends a request to SEE and waits for the response.
*
* @param client The pointer to sns_client to make the request with.
@@ -431,17 +485,31 @@
Microseconds timeout = kDefaultSeeWaitTimeout);
/**
- * Obtains the pointer to cal data by SUID.
- */
- SeeCalData *getCalDataFromSuid(const sns_std_suid& suid);
-
- /**
* @return SensorInfo instance found in mSensorInfos with the given
* SensorType, or nullptr if not found
*/
const SensorInfo *getSensorInfo(SensorType sensorType) const;
};
+#ifdef CHRE_SLPI_UIMG_ENABLED
+/**
+ * A version of SeeHelper that explicitly uses the QMI API on the bottom edge
+ * and therefore only works in big image (but goes through CM instead of QCM
+ * within SEE).
+ *
+ * @see SeeHelper
+ */
+class BigImageSeeHelper : public SeeHelper {
+ public:
+ BigImageSeeHelper(SeeCalHelper *calHelper) : SeeHelper(calHelper) {
+ mSnsClientApi = &kQmiApi;
+ }
+
+ private:
+ static const SnsClientApi kQmiApi;
+};
+#endif // CHRE_SLPI_UIMG_ENABLED
+
} // namespace chre
#endif // CHRE_PLATFORM_SLPI_SEE_SEE_HELPER_H_
diff --git a/platform/slpi/include/chre/platform/slpi/see/see_helper_internal.h b/platform/slpi/include/chre/platform/slpi/see/see_helper_internal.h
deleted file mode 100644
index c1537e8..0000000
--- a/platform/slpi/include/chre/platform/slpi/see/see_helper_internal.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_SEE_SEE_HELPER_INTERNAL_H_
-#define CHRE_PLATFORM_SLPI_SEE_SEE_HELPER_INTERNAL_H_
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "sns_suid.pb.h"
-
-#include "chre/util/optional.h"
-
-namespace chre {
-
-//! A struct to store a sensor's calibration data
-struct SeeCalData {
- float bias[3];
- float scale[3];
- float matrix[9];
- bool hasBias;
- bool hasScale;
- bool hasMatrix;
- uint8_t accuracy;
-};
-
-//! A struct to store a cal sensor's UID and its cal data.
-struct SeeCalInfo {
- Optional<sns_std_suid> suid;
- SeeCalData cal;
-};
-
-//! The list of SEE cal sensors supported.
-enum class SeeCalSensor {
- AccelCal,
- GyroCal,
- MagCal,
- NumCalSensors,
-};
-
-//! A convenience constant.
-constexpr size_t kNumSeeCalSensors = static_cast<size_t>(
- SeeCalSensor::NumCalSensors);
-
-} // namespace chre
-
-#endif // CHRE_PLATFORM_SLPI_SEE_SEE_HELPER_INTERNAL_H_
diff --git a/platform/slpi/include/chre/target_platform/platform_audio_base.h b/platform/slpi/include/chre/target_platform/platform_audio_base.h
index 3b84c3c..d4424d5 100644
--- a/platform/slpi/include/chre/target_platform/platform_audio_base.h
+++ b/platform/slpi/include/chre/target_platform/platform_audio_base.h
@@ -24,10 +24,24 @@
* functionality from.
*/
class PlatformAudioBase {
+ public:
+ /**
+ * Invoked whenever the host goes awake. This is used to implement the
+ * deferred audio disable operation. This is called on the CHRE thread.
+ */
+ void onHostAwake();
+
protected:
//! The number of open audio clients. This is incremented/decremented by the
//! setHandleEnabled platform API.
uint32_t mNumAudioClients = 0;
+
+ //! The current state of the audio feature enabled on the host.
+ bool mCurrentAudioEnabled = false;
+
+ //! The target state of the audio feature enabled on the host. This is used to
+ //! support deferred disabling when the next AP wake occurs.
+ bool mTargetAudioEnabled = false;
};
} // namespace chre
diff --git a/platform/slpi/platform_audio.cc b/platform/slpi/platform_audio.cc
index 96c74dc..834dd3a 100644
--- a/platform/slpi/platform_audio.cc
+++ b/platform/slpi/platform_audio.cc
@@ -58,6 +58,7 @@
auto *dataEvent = memoryAlloc<struct chreAudioDataEvent>();
if (dataEvent == nullptr) {
LOGE("Failed to allocate data event");
+ wcd_spi_client_release_audio_data_event(event->handle);
} else {
dataEvent->handle = event->handle;
dataEvent->timestamp = event->timestamp_ns;
@@ -106,11 +107,20 @@
}
if (lastNumAudioClients == 0 && mNumAudioClients > 0) {
- LOGD("Enabling WCD SLPI");
- sendAudioRequest();
+ mTargetAudioEnabled = true;
+ if (!mCurrentAudioEnabled) {
+ LOGD("Enabling WCD SLPI");
+ mCurrentAudioEnabled = true;
+ sendAudioRequest();
+ }
} else if (lastNumAudioClients > 0 && mNumAudioClients == 0) {
- LOGD("Disabling WCD SLPI");
- sendAudioRelease();
+ mTargetAudioEnabled = false;
+ if (EventLoopManagerSingleton::get()->getEventLoop()
+ .getPowerControlManager().hostIsAwake()) {
+ onHostAwake();
+ } else {
+ LOGD("Deferring disable WCD SLPI");
+ }
}
}
@@ -138,7 +148,7 @@
}
bool PlatformAudio::getAudioSource(uint32_t handle,
- chreAudioSource *source) {
+ chreAudioSource *source) const {
slpiForceBigImage();
wcd_spi_audio_source_s wcd_spi_audio_source;
bool result = wcd_spi_client_get_source(handle, &wcd_spi_audio_source);
@@ -151,4 +161,12 @@
return result;
}
+void PlatformAudioBase::onHostAwake() {
+ if (mCurrentAudioEnabled && !mTargetAudioEnabled) {
+ LOGD("Disabling WCD SPI");
+ mCurrentAudioEnabled = mTargetAudioEnabled;
+ sendAudioRelease();
+ }
+}
+
} // namespace chre
diff --git a/platform/slpi/platform_nanoapp.cc b/platform/slpi/platform_nanoapp.cc
index cb3cb78..20b8196 100644
--- a/platform/slpi/platform_nanoapp.cc
+++ b/platform/slpi/platform_nanoapp.cc
@@ -55,6 +55,21 @@
const void *eventData) {
if (!isUimgApp()) {
slpiForceBigImage();
+
+#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
+ // HACK: as SEE does not support software batching in uimg via
+ // QCM/uQSockets, we rewrite requests for accel from big image nanoapps to
+ // vendor type 3 in chreSensorFindDefault(), which is implemented as accel
+ // routed through CM/QMI and supports batching. Rewrite sensor data arriving
+ // on this event type to the vanilla accel event type so that this appears
+ // transparent to the nanoapp.
+ // TODO(P2-5673a9): work with QC to determine a better long-term solution
+ constexpr uint16_t kAccelBigImageEventType =
+ (CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_VENDOR_START + 3);
+ if (eventType == kAccelBigImageEventType) {
+ eventType = CHRE_EVENT_SENSOR_ACCELEROMETER_DATA;
+ }
+#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED)
}
mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
diff --git a/platform/slpi/see/include/chre/target_platform/power_control_manager_base.h b/platform/slpi/see/include/chre/target_platform/power_control_manager_base.h
index 9081262..22cdba4 100644
--- a/platform/slpi/see/include/chre/target_platform/power_control_manager_base.h
+++ b/platform/slpi/see/include/chre/target_platform/power_control_manager_base.h
@@ -17,10 +17,17 @@
#ifndef CHRE_PLATFORM_SLPI_SEE_POWER_CONTROL_MANAGER_BASE_H_
#define CHRE_PLATFORM_SLPI_SEE_POWER_CONTROL_MANAGER_BASE_H_
+extern "C" {
+#include "sns_client_thread_util.h"
+} // extern "C"
+
namespace chre {
class PowerControlManagerBase {
public:
+ PowerControlManagerBase();
+ ~PowerControlManagerBase();
+
/**
* Makes a power mode request. An actual vote to the SLPI power manager may
* not be cast depending on current power mode and mBigImageRefCount.
@@ -43,6 +50,13 @@
protected:
//! Set to true if the host is awake, false if suspended.
bool mHostIsAwake = true;
+
+ //! Set to true if the thread is currently idle (no pending events),
+ //! false otherwise.
+ bool mIsThreadIdle = true;
+
+ //! A pointer to the client to compute thread utilization
+ sns_thread_util_client *mThreadUtilClient = nullptr;
};
} // namespace chre
diff --git a/platform/slpi/see/island_vote_client.cc b/platform/slpi/see/island_vote_client.cc
index 02fe701..a80f8d0 100644
--- a/platform/slpi/see/island_vote_client.cc
+++ b/platform/slpi/see/island_vote_client.cc
@@ -26,6 +26,10 @@
namespace chre {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+constexpr Seconds IslandVoteClient::kSeeMaxBigImageDuration;
+#endif // CHRE_SLPI_UIMG_ENABLED
+
IslandVoteClient::IslandVoteClient(const char *clientName) {
#ifdef CHRE_SLPI_UIMG_ENABLED
mClientHandle = sns_island_aggregator_register_client(clientName);
@@ -75,6 +79,8 @@
voteSnsPowerMode(true /* bigImage */);
mLastBigImageVote = true;
}
+ } else {
+ checkBigImageDuration();
}
}
@@ -83,10 +89,8 @@
CHRE_ASSERT_LOG(mBigImageRefCount > 0,
"Tried to decrement big image ref count when it's 0");
+ uint64_t duration = checkBigImageDuration();
if (--mBigImageRefCount == 0) {
- uint64_t duration =
- Milliseconds(SystemTime::getMonotonicTime()).getMilliseconds()
- - mRefCountStart.getMilliseconds();
LOGW("Big image ref count ends: %" PRIu64 " ms", duration);
// There's no big image activity now, restore the intended uimg power state.
@@ -110,6 +114,22 @@
}
return success;
}
+
+uint64_t IslandVoteClient::checkBigImageDuration() const {
+ uint64_t duration = 0;
+ if (mBigImageRefCount > 0) {
+ duration = Milliseconds(SystemTime::getMonotonicTime()).getMilliseconds()
+ - mRefCountStart.getMilliseconds();
+ }
+
+ // Bimg memory fallback only intends to handle a surge of uimg memory
+ // requests. If there's a prolonged period of memory fallback, this might
+ // indicate a memory leak or inadequate uimg heap size.
+ if (duration > kSeeMaxBigImageDuration.getMilliseconds()) {
+ FATAL_ERROR("Forced into big image for %" PRIu64 " msec", duration);
+ }
+ return duration;
+}
#endif // CHRE_SLPI_UIMG_ENABLED
//! Explicitly instantiate the IslandVoteClient singleton to reduce code size.
diff --git a/platform/slpi/see/platform_sensor.cc b/platform/slpi/see/platform_sensor.cc
index fa56270..5cf6a51 100644
--- a/platform/slpi/see/platform_sensor.cc
+++ b/platform/slpi/see/platform_sensor.cc
@@ -28,6 +28,7 @@
#include "chre/platform/fatal_error.h"
#include "chre/platform/log.h"
#include "chre/platform/shared/platform_sensor_util.h"
+#include "chre/platform/slpi/power_control_util.h"
#include "chre/platform/slpi/see/see_client.h"
#include "chre/platform/slpi/see/see_helper.h"
#include "chre/platform/system_time.h"
@@ -46,6 +47,17 @@
namespace chre {
namespace {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+#ifndef CHREX_SENSOR_SUPPORT
+// The current implementation uses vendor sensor type 3 to remap into accel,
+// with requests made through QMI instead of QSockets, as SEE does not support
+// micro-image batching in QCM.
+#error "CHRE extensions are required for micro-image SEE support"
+#endif // CHREX_SENSOR_SUPPORT
+
+constexpr SensorType kAccelBigImageSensorType = SensorType::VendorType3;
+#endif // CHRE_SLPI_UIMG_ENABLED
+
//! A class that implements SeeHelperCallbackInterface.
class SeeHelperCallback : public SeeHelperCallbackInterface {
void onSamplingStatusUpdate(
@@ -178,9 +190,9 @@
newStatus.latency = update.status.latency;
}
- if (newStatus.enabled != prevStatus.enabled
- || newStatus.interval != prevStatus.interval
- || newStatus.latency != prevStatus.latency) {
+ if (newStatus.enabled != prevStatus.enabled ||
+ (newStatus.enabled && (newStatus.interval != prevStatus.interval
+ || newStatus.latency != prevStatus.latency))) {
sensor->setSamplingStatus(newStatus);
// Only post to Nanoapps with an open request.
@@ -279,14 +291,16 @@
/**
* Constructs and initializes a sensor, and adds it to the sensor list.
*
+ * @param seeHelper SeeHelper instance to register sensor with
* @param suid The SUID of the sensor as provided by SEE.
* @param sensorType The sensor type of the sensor.
* @param calibrated Whether the sensor is runtime-calibrated or not.
* @param attr A reference to SeeAttrbutes.
* @param sensor The sensor list.
*/
-void addSensor(SensorType sensorType, const sns_std_suid& suid,
- const SeeAttributes& attr, DynamicVector<Sensor> *sensors) {
+void addSensor(SeeHelper& seeHelper, SensorType sensorType,
+ const sns_std_suid& suid, const SeeAttributes& attr,
+ DynamicVector<Sensor> *sensors) {
// Concatenate vendor and name with a space in between.
char sensorName[kSensorNameMaxLen];
strlcpy(sensorName, attr.vendor, sizeof(sensorName));
@@ -311,9 +325,15 @@
FATAL_ERROR("Failed to allocate new sensor: out of memory");
}
+ // Resample big image accel to reduce system load during sw flush.
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ bool resample = (sensorType == kAccelBigImageSensorType);
+#else
+ bool resample = false;
+#endif
bool prevRegistered;
- bool registered = getSeeHelper()->registerSensor(
- sensorType, suid, &prevRegistered);
+ bool registered = seeHelper.registerSensor(
+ sensorType, suid, resample, &prevRegistered);
if (!registered && prevRegistered) {
LOGW("SUID has been previously registered");
} else if (!registered) {
@@ -344,10 +364,10 @@
* Obtains the list of SUIDs and their attributes that support the specified
* data type.
*/
-bool getSuidAndAttrs(const char *dataType, DynamicVector<SuidAttr> *suidAttrs,
- uint8_t minNumSuids) {
+bool getSuidAndAttrs(SeeHelper& seeHelper, const char *dataType,
+ DynamicVector<SuidAttr> *suidAttrs, uint8_t minNumSuids) {
DynamicVector<sns_std_suid> suids;
- bool success = getSeeHelper()->findSuidSync(dataType, &suids, minNumSuids);
+ bool success = seeHelper.findSuidSync(dataType, &suids, minNumSuids);
if (!success) {
LOGE("Failed to find sensor '%s'", dataType);
} else {
@@ -355,7 +375,7 @@
for (const auto& suid : suids) {
SeeAttributes attr;
- if (!getSeeHelper()->getAttributesSync(suid, &attr)) {
+ if (!seeHelper.getAttributesSync(suid, &attr)) {
success = false;
LOGE("Failed to get attributes of SUID 0x%" PRIx64 " %" PRIx64,
suid.suid_high, suid.suid_low);
@@ -378,8 +398,8 @@
return success;
}
-// Check whether two sensors with the specified attrtibutes belong to the same
-// sensor hardware module.
+//! Check whether two sensors with the specified attrtibutes belong to the same
+//! sensor hardware module.
bool sensorHwMatch(const SeeAttributes& attr0, const SeeAttributes& attr1) {
// When HW ID is absent, it's default to 0 and won't be a factor.
return ((strncmp(attr0.vendor, attr1.vendor, kSeeAttrStrValLen) == 0)
@@ -387,6 +407,92 @@
&& (attr0.hwId == attr1.hwId));
}
+/**
+ * Looks up SUID(s) associated with a given sensor data type string and sensor
+ * type enum, registers them with SeeHelper, and adds a Sensor instance to the
+ * supplied vector for use in CHRE. When given an uncalibrated sensor type, will
+ * also look for and add the calibrated sensor type.
+ *
+ * @param seeHelper SeeHelper instance to use for lookup/registration
+ * @param temperatureSensors List of previously discovered temperature sensor
+ * info to use for adding temp sensors associated with this sensor type
+ * @param dataType SEE data type string
+ * @param sensorType CHRE sensor type enum associated with dataType
+ * @param skipAdditionalTypes if true, don't attempt to add
+ * calibrated/temperature sensor types associated with this sensorType
+ * @param sensors Vector to append found sensor(s) to
+ */
+void findAndAddSensorsForType(
+ SeeHelper& seeHelper, const DynamicVector<SuidAttr>& temperatureSensors,
+ const char *dataType, SensorType sensorType, bool skipAdditionalTypes,
+ DynamicVector<Sensor> *sensors) {
+ DynamicVector<SuidAttr> primarySensors;
+ if (!getSuidAndAttrs(seeHelper, dataType, &primarySensors,
+ 1 /* minNumSuids */)) {
+ FATAL_ERROR("Failed to get primary sensor UID and attributes");
+ }
+
+ for (const auto& primarySensor : primarySensors) {
+ sns_std_suid suid = primarySensor.suid;
+ SeeAttributes attr = primarySensor.attr;
+
+ // Some sensors support both continuous and on-change streams.
+ // If there are more than one SUIDs that support the data type,
+ // choose the first one that has the expected stream type.
+ if (isStreamTypeCorrect(sensorType, attr.streamType)) {
+ addSensor(seeHelper, sensorType, suid, attr, sensors);
+
+ if (!skipAdditionalTypes) {
+ // Check if this sensor has a runtime-calibrated version.
+ SensorType calibratedType = getSensorTypeFromDataType(
+ dataType, true /* calibrated */);
+ if (calibratedType != sensorType) {
+ addSensor(seeHelper, calibratedType, suid, attr, sensors);
+ }
+
+ // Check if this sensor has a secondary temperature sensor.
+ SensorType temperatureType = getTempSensorType(sensorType);
+ if (temperatureType != SensorType::Unknown) {
+ bool tempFound = false;
+ for (const auto& tempSensor : temperatureSensors) {
+ sns_std_suid tempSuid = tempSensor.suid;
+ SeeAttributes tempAttr = tempSensor.attr;
+
+ if (sensorHwMatch(attr, tempAttr)) {
+ LOGD("Found matching temperature sensor type");
+ tempFound = true;
+ addSensor(seeHelper, temperatureType, tempSuid, tempAttr,
+ sensors);
+ break;
+ }
+ }
+ if (!tempFound) {
+ LOGW("Temperature sensor type %" PRIu8 " not found!",
+ static_cast<uint8_t>(temperatureType));
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+#ifdef CHRE_SLPI_UIMG_ENABLED
+/**
+ * Registers alternate sensor(s) to be used separately by big image nanoapps.
+ */
+void getBigImageSensors(DynamicVector<Sensor> *sensors) {
+ // Currently, just adding calibrated accel, as it's the one we know that big
+ // image nanoapps will need at a different batching rate compared to uimg
+ SeeHelper& seeHelper = *getBigImageSeeHelper();
+ const char *kAccelDataType = "accel";
+ DynamicVector<SuidAttr> nullTemperatureSensorList;
+ findAndAddSensorsForType(
+ seeHelper, nullTemperatureSensorList, kAccelDataType,
+ kAccelBigImageSensorType, true /* skipAdditionalTypes */, sensors);
+}
+#endif // CHRE_SLPI_UIMG_ENABLED
+
} // anonymous namespace
PlatformSensor::~PlatformSensor() {
@@ -404,17 +510,30 @@
if (!getSeeHelper()->init(&seeHelperCallback)) {
FATAL_ERROR("Failed to initialize SEE helper");
}
+
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ BigImageSeeHelperSingleton::init(getSeeHelper()->getCalHelper());
+ if (!getBigImageSeeHelper()->init(&seeHelperCallback, kDefaultSeeWaitTimeout,
+ true /* skipDefaultSensorInit */)) {
+ FATAL_ERROR("Failed to init bimg SEE helper");
+ }
+#endif // CHRE_SLPI_UIMG_ENABLED
}
void PlatformSensor::deinit() {
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ BigImageSeeHelperSingleton::deinit();
+#endif
+
SeeHelperSingleton::deinit();
}
bool PlatformSensor::getSensors(DynamicVector<Sensor> *sensors) {
CHRE_ASSERT(sensors);
+ SeeHelper& seeHelper = *getSeeHelper();
DynamicVector<SuidAttr> tempSensors;
- if (!getSuidAndAttrs("sensor_temperature", &tempSensors,
+ if (!getSuidAndAttrs(seeHelper, "sensor_temperature", &tempSensors,
CHRE_SEE_NUM_TEMP_SENSORS)) {
FATAL_ERROR("Failed to get temperature sensor UID and attributes");
}
@@ -435,52 +554,14 @@
continue;
}
- DynamicVector<SuidAttr> primarySensors;
- if (!getSuidAndAttrs(dataType, &primarySensors, 1 /* minNumSuids */)) {
- FATAL_ERROR("Failed to get primary sensor UID and attributes");
- } else {
- for (const auto& primarySensor : primarySensors) {
- sns_std_suid suid = primarySensor.suid;
- SeeAttributes attr = primarySensor.attr;
-
- // Some sensors support both continuous and on-change streams.
- // If there are more than one SUIDs that support the data type,
- // choose the first one that has the expected stream type.
- if (isStreamTypeCorrect(sensorType, attr.streamType)) {
- addSensor(sensorType, suid, attr, sensors);
-
- // Check if this sensor has a runtime-calibrated version.
- SensorType calibratedType = getSensorTypeFromDataType(
- dataType, true /* calibrated */);
- if (calibratedType != sensorType) {
- addSensor(calibratedType, suid, attr, sensors);
- }
-
- // Check if this sensor has a secondary temperature sensor.
- SensorType temperatureType = getTempSensorType(sensorType);
- if (temperatureType != SensorType::Unknown) {
- bool tempFound = false;
- for (const auto& tempSensor : tempSensors) {
- sns_std_suid tempSuid = tempSensor.suid;
- SeeAttributes tempAttr = tempSensor.attr;
-
- if (sensorHwMatch(attr, tempAttr)) {
- LOGD("Found matching temperature sensor type");
- tempFound = true;
- addSensor(temperatureType, tempSuid, tempAttr, sensors);
- break;
- }
- }
- if (!tempFound) {
- LOGW("Temperature sensor type %" PRIu8 " not found!",
- static_cast<uint8_t>(temperatureType));
- }
- }
- break;
- }
- }
- }
+ findAndAddSensorsForType(seeHelper, tempSensors, dataType, sensorType,
+ false /* skipAdditionalTypes */, sensors);
}
+
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ getBigImageSensors(sensors);
+#endif
+
return true;
}
@@ -503,11 +584,36 @@
static_cast<uint8_t>(getSensorType()));
}
- bool success = SeeHelperSingleton::get()->makeRequest(req);
+ SeeHelper *seeHelper = getSeeHelper();
+#ifdef CHRE_SLPI_UIMG_ENABLED
+ if (getSensorType() == kAccelBigImageSensorType) {
+ seeHelper = getBigImageSeeHelper();
+ slpiForceBigImage();
+ }
+#endif
- // TODO: remove setSamplingStatus when .latency is available in status update
- // from SEE.
+ bool wasInUImage = slpiInUImage();
+ bool success = seeHelper->makeRequest(req);
+
+ // If we dropped into micro-image during that blocking call to SEE, go back to
+ // big image. This won't happen if the calling nanoapp is a big image one, but
+ // other code paths currently assume that we will only transition from big
+ // image to micro-image from CHRE's perspective while it's waiting for an
+ // event to arrive in its empty queue.
+ // TODO: transition back to big image only when needed, at the point of
+ // invoking a nanoapp's free event/message callback
+ if (!wasInUImage && slpiInUImage()) {
+ LOGD("Restoring big image operating mode");
+ slpiForceBigImage();
+ }
+
if (success) {
+ if (request.getMode() == SensorMode::Off) {
+ mLastEventValid = false;
+ }
+
+ // TODO: remove setSamplingStatus when .latency is available in status
+ // update from SEE.
struct chreSensorSamplingStatus status;
if (getSamplingStatus(&status)) {
diff --git a/platform/slpi/see/power_control_manager.cc b/platform/slpi/see/power_control_manager.cc
index b0fd3b8..ad49094 100644
--- a/platform/slpi/see/power_control_manager.cc
+++ b/platform/slpi/see/power_control_manager.cc
@@ -23,6 +23,14 @@
namespace chre {
+PowerControlManagerBase::PowerControlManagerBase() {
+ sns_client_create_thread_utilization_client(&mThreadUtilClient);
+}
+
+PowerControlManagerBase::~PowerControlManagerBase() {
+ sns_client_remove_thread_utilization_client(mThreadUtilClient);
+}
+
bool PowerControlManagerBase::voteBigImage(bool bigImage) {
return IslandVoteClientSingleton::get()->voteBigImage(bigImage);
}
@@ -34,10 +42,39 @@
EventLoopManagerSingleton::get()->getEventLoop().postEvent(
mHostIsAwake ? CHRE_EVENT_HOST_AWAKE : CHRE_EVENT_HOST_ASLEEP,
nullptr /* eventData */, nullptr /* freeCallback */);
+
+#ifdef CHRE_AUDIO_SUPPORT_ENABLED
+ if (awake) {
+ auto callback = [](uint16_t /* eventType */, void * /* eventData*/) {
+ EventLoopManagerSingleton::get()->getAudioRequestManager()
+ .getPlatformAudio().onHostAwake();
+ };
+
+ EventLoopManagerSingleton::get()->deferCallback(
+ SystemCallbackType::AudioHandleHostAwake, nullptr, callback);
+ }
+#endif // CHRE_AUDIO_SUPPORT_ENABLED
}
}
void PowerControlManager::postEventLoopProcess(size_t numPendingEvents) {
+ // Although this execution point does not actually represent the start
+ // of the CHRE thread's activity, we only care about cases where the
+ // CHRE's event queue is highly backlogged for voting higher clock rates.
+ if (mIsThreadIdle && numPendingEvents != 0) {
+ sns_client_thread_utilization_start(mThreadUtilClient);
+ mIsThreadIdle = false;
+ } else if (!mIsThreadIdle) {
+ // Update the time profile as frequently as possible so that clock updates
+ // are not deferred until all events are processed.
+ sns_client_thread_utilization_stop(mThreadUtilClient);
+ if (numPendingEvents != 0) {
+ sns_client_thread_utilization_start(mThreadUtilClient);
+ } else {
+ mIsThreadIdle = true;
+ }
+ }
+
if (numPendingEvents == 0 && !slpiInUImage()) {
voteBigImage(false /* bigImage */);
}
diff --git a/platform/slpi/see/see_cal_helper.cc b/platform/slpi/see/see_cal_helper.cc
new file mode 100644
index 0000000..d910012
--- /dev/null
+++ b/platform/slpi/see/see_cal_helper.cc
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/see/see_cal_helper.h"
+
+#include "chre/platform/assert.h"
+#include "chre/platform/log.h"
+#include "chre/platform/slpi/see/see_helper.h"
+#include "chre/util/lock_guard.h"
+#include "chre/util/macros.h"
+
+namespace chre {
+
+void SeeCalHelper::applyCalibration(SensorType sensorType, const float input[3],
+ float output[3]) const {
+ bool applied = false;
+ size_t index = getCalIndexFromSensorType(sensorType);
+ if (index < ARRAY_SIZE(mCalInfo)) {
+ LockGuard<Mutex> lock(mMutex);
+
+ // TODO: Support compensation matrix and scaling factor calibration
+ if (mCalInfo[index].cal.hasBias) {
+ for (size_t i = 0; i < 3; i++) {
+ output[i] = input[i] - mCalInfo[index].cal.bias[i];
+ }
+ applied = true;
+ }
+ }
+
+ if (!applied) {
+ for (size_t i = 0; i < 3; i++) {
+ output[i] = input[i];
+ }
+ }
+}
+
+const sns_std_suid& SeeCalHelper::getCalSuidFromSensorType(
+ SensorType sensorType) const {
+ static sns_std_suid suid = sns_suid_sensor_init_zero;
+
+ // Mutex not needed, SUID is not modified after init
+ size_t calIndex = getCalIndexFromSensorType(sensorType);
+ if (calIndex < ARRAY_SIZE(mCalInfo) && mCalInfo[calIndex].suid.has_value()) {
+ suid = mCalInfo[calIndex].suid.value();
+ }
+ return suid;
+}
+
+bool SeeCalHelper::registerForCalibrationUpdates(SeeHelper& seeHelper) {
+ bool success = true;
+
+ // Find the cal sensor's SUID, assign it to mCalInfo, and make cal sensor data
+ // request.
+ DynamicVector<sns_std_suid> suids;
+ for (size_t i = 0; i < ARRAY_SIZE(mCalInfo); i++) {
+ const char *calType = getDataTypeForCalSensorIndex(i);
+ if (!seeHelper.findSuidSync(calType, &suids)) {
+ success = false;
+ LOGE("Failed to find sensor '%s'", calType);
+ } else {
+ mCalInfo[i].suid = suids[0];
+ if (!seeHelper.configureOnChangeSensor(suids[0], true /* enable */)) {
+ success = false;
+ LOGE("Failed to request '%s' data", calType);
+ }
+ }
+ }
+
+ return success;
+}
+
+void SeeCalHelper::updateCalibration(
+ const sns_std_suid& suid, bool hasBias, float bias[3], bool hasScale,
+ float scale[3], bool hasMatrix, float matrix[9], uint8_t accuracy) {
+ size_t index = getCalIndexFromSuid(suid);
+ if (index < ARRAY_SIZE(mCalInfo)) {
+ LockGuard<Mutex> lock(mMutex);
+ SeeCalData& calData = mCalInfo[index].cal;
+
+ calData.hasBias = hasBias;
+ if (hasBias) {
+ memcpy(calData.bias, bias, sizeof(calData.bias));
+ }
+
+ calData.hasScale = hasScale;
+ if (hasScale) {
+ memcpy(calData.scale, scale, sizeof(calData.scale));
+ }
+
+ calData.hasMatrix = hasMatrix;
+ if (hasMatrix) {
+ memcpy(calData.matrix, matrix, sizeof(calData.matrix));
+ }
+
+ calData.accuracy = accuracy;
+ }
+}
+
+size_t SeeCalHelper::getCalIndexFromSensorType(SensorType sensorType) {
+ SeeCalSensor index;
+ switch (sensorType) {
+ case SensorType::Accelerometer:
+ index = SeeCalSensor::AccelCal;
+ break;
+ case SensorType::Gyroscope:
+ index = SeeCalSensor::GyroCal;
+ break;
+ case SensorType::GeomagneticField:
+ index = SeeCalSensor::MagCal;
+ break;
+ default:
+ index = SeeCalSensor::NumCalSensors;
+ }
+ return static_cast<size_t>(index);
+}
+
+const char *SeeCalHelper::getDataTypeForCalSensorIndex(size_t calSensorIndex) {
+ switch (static_cast<SeeCalSensor>(calSensorIndex)) {
+ case SeeCalSensor::AccelCal:
+ return "accel_cal";
+ case SeeCalSensor::GyroCal:
+ return "gyro_cal";
+ case SeeCalSensor::MagCal:
+ return "mag_cal";
+ default:
+ CHRE_ASSERT(false);
+ }
+ return nullptr;
+}
+
+size_t SeeCalHelper::getCalIndexFromSuid(const sns_std_suid& suid) const {
+ size_t i = 0;
+ for (; i < ARRAY_SIZE(mCalInfo); i++) {
+ if (mCalInfo[i].suid.has_value()
+ && suidsMatch(suid, mCalInfo[i].suid.value())) {
+ break;
+ }
+ }
+ return i;
+}
+
+} // namespace chre
diff --git a/platform/slpi/see/see_helper.cc b/platform/slpi/see/see_helper.cc
index 7f49190..d2c8334 100644
--- a/platform/slpi/see/see_helper.cc
+++ b/platform/slpi/see/see_helper.cc
@@ -24,11 +24,16 @@
#include "sns_proximity.pb.h"
#include "sns_rc.h"
#include "sns_remote_proc_state.pb.h"
+#include "sns_resampler.pb.h"
#include "sns_std.pb.h"
#include "sns_std_sensor.pb.h"
#include "stringl.h"
#include "timer.h"
+#ifdef CHRE_SLPI_UIMG_ENABLED
+#include "sns_qmi_client.h"
+#endif
+
#include <algorithm>
#include <cfloat>
#include <cinttypes>
@@ -93,7 +98,6 @@
size_t totalSamples;
UniquePtr<uint8_t> event;
UniquePtr<SeeHelperCallbackInterface::SamplingStatusData> status;
- SeeCalData *cal;
SensorType sensorType;
bool isHostWakeSuspendEvent;
bool isHostAwake;
@@ -108,7 +112,7 @@
SeeDataArg *data;
bool decodeMsgIdOnly;
Optional<sns_std_suid> *remoteProcSuid;
- SeeCalInfo *calInfo;
+ SeeCalHelper *calHelper;
};
//! A struct to facilitate decoding sensor attributes.
@@ -125,48 +129,6 @@
bool initialized;
};
-size_t getCalIndexFromSensorType(SensorType sensorType) {
- SeeCalSensor index;
- switch (sensorType) {
- case SensorType::Accelerometer:
- index = SeeCalSensor::AccelCal;
- break;
- case SensorType::Gyroscope:
- index = SeeCalSensor::GyroCal;
- break;
- case SensorType::GeomagneticField:
- index = SeeCalSensor::MagCal;
- break;
- default:
- index = SeeCalSensor::NumCalSensors;
- }
- return static_cast<size_t>(index);
-}
-
-size_t getCalIndexFromDataType(const char *dataType) {
- SensorType sensorType = SensorType::Unknown;
- if (strcmp(dataType, "accel_cal") == 0) {
- sensorType = SensorType::Accelerometer;
- } else if (strcmp(dataType, "gyro_cal") == 0) {
- sensorType = SensorType::Gyroscope;
- } else if (strcmp(dataType, "mag_cal") == 0) {
- sensorType = SensorType::GeomagneticField;
- }
- return getCalIndexFromSensorType(sensorType);
-}
-
-size_t getCalIndexFromSuid(const sns_std_suid& suid,
- const SeeCalInfo *calInfo) {
- size_t i = 0;
- for (; i < kNumSeeCalSensors; i++) {
- if (calInfo[i].suid.has_value()
- && suidsMatch(suid, calInfo[i].suid.value())) {
- break;
- }
- }
- return i;
-}
-
/**
* Copy an encoded pb message to a wrapper proto's field.
*/
@@ -272,6 +234,55 @@
}
/**
+ * Encodes sns_resampler_config pb message.
+ *
+ * @param request The request to be encoded.
+ * @param suid The SUID of the physical sensor to be resampled.
+ * @param msg A non-null pointer to the pb message unique pointer whose object
+ * will be assigned here.
+ * @param msgLen A non-null pointer to the size of the encoded pb message.
+ *
+ * @return true if the pb message and length were obtained.
+ */
+bool encodeSnsResamplerConfig(const SeeSensorRequest& request,
+ const sns_std_suid& suid,
+ UniquePtr<pb_byte_t> *msg, size_t *msgLen) {
+ CHRE_ASSERT(msg);
+ CHRE_ASSERT(msgLen);
+ bool success = false;
+
+ // Initialize the pb message
+ sns_resampler_config req = {
+ .sensor_uid = suid,
+ .resampled_rate = request.samplingRateHz,
+ .rate_type = SNS_RESAMPLER_RATE_FIXED,
+ .filter = true,
+ .has_axis_cnt = true,
+ .axis_cnt = 3, // TODO: set this properly.
+ };
+
+ if (!pb_get_encoded_size(msgLen, sns_resampler_config_fields, &req)) {
+ LOGE("pb_get_encoded_size failed for sns_resampler_config");
+ } else if (*msgLen == 0) {
+ LOGE("Invalid pb encoded size for sns_resampler_config");
+ } else {
+ UniquePtr<pb_byte_t> buf(static_cast<pb_byte_t *>(memoryAlloc(*msgLen)));
+ *msg = std::move(buf);
+ if (msg->isNull()) {
+ LOG_OOM();
+ } else {
+ pb_ostream_t stream = pb_ostream_from_buffer(msg->get(), *msgLen);
+
+ success = pb_encode(&stream, sns_resampler_config_fields, &req);
+ if (!success) {
+ LOG_NANOPB_ERROR(&stream);
+ }
+ }
+ }
+ return success;
+}
+
+/**
* Encodes sns_std_sensor_config pb message.
*
* @param request The request to be encoded.
@@ -363,6 +374,9 @@
req->susp_config.delivery_type = SNS_CLIENT_DELIVERY_WAKEUP;
req->request.has_batching = batchValid;
req->request.batching.batch_period = batchPeriodUs;
+ // TODO: remove flush_period setting after resolving b/110823194.
+ req->request.batching.has_flush_period = true;
+ req->request.batching.flush_period = batchPeriodUs + 3000000;
req->request.payload.funcs.encode = copyPayload;
req->request.payload.arg = data;
req->request.has_is_passive = true,
@@ -727,20 +741,8 @@
return success;
}
-// TODO: Support compensation matrix and scaling factor calibration
-void applyThreeAxisCalibration(
- chreSensorThreeAxisData::chreSensorThreeAxisSampleData *sample,
- const float *val, const SeeCalData *cal) {
- float bias[3] = {};
- if (cal != nullptr && cal->hasBias) {
- memcpy(bias, cal->bias, sizeof(bias));
- }
- sample->x = val[0] - bias[0];
- sample->y = val[1] - bias[1];
- sample->z = val[2] - bias[2];
-}
-
-void populateEventSample(SeeDataArg *data, const float *val) {
+void populateEventSample(SeeInfoArg *info, const float *val) {
+ SeeDataArg *data = info->data;
size_t index = data->sampleIndex;
if (!data->event.isNull() && index < data->totalSamples) {
SensorSampleType sampleType = getSensorSampleTypeFromSensorType(
@@ -751,7 +753,8 @@
case SensorSampleType::ThreeAxis: {
auto *event = reinterpret_cast<chreSensorThreeAxisData *>(
data->event.get());
- applyThreeAxisCalibration(&event->readings[index], val, data->cal);
+ info->calHelper->applyCalibration(
+ data->sensorType, val, event->readings[index].values);
timestampDelta = &event->readings[index].timestampDelta;
break;
}
@@ -805,6 +808,15 @@
timestampDelta = &event->readings[index].timestampDelta;
break;
}
+
+ case SensorSampleType::Vendor3: {
+ auto *event = reinterpret_cast<chrexSensorVendor3Data *>(
+ data->event.get());
+ memcpy(event->readings[index].values, val,
+ sizeof(event->readings[index].values));
+ timestampDelta = &event->readings[index].timestampDelta;
+ break;
+ }
#endif // CHREX_SENSOR_SUPPORT
default:
@@ -912,7 +924,7 @@
LOG_NANOPB_ERROR(stream);
} else {
auto *info = static_cast<SeeInfoArg *>(*arg);
- populateEventSample(info->data, sample.val);
+ populateEventSample(info, sample.val);
}
return success;
}
@@ -959,31 +971,16 @@
LOG_NANOPB_ERROR(stream);
} else {
auto *info = static_cast<SeeInfoArg *>(*arg);
- SeeCalInfo *calInfo = info->calInfo;
- size_t calIndex = getCalIndexFromSuid(info->suid, calInfo);
- if (calIndex >= kNumSeeCalSensors) {
- LOGW("Cal sensor index out of bounds 0x%" PRIx64 " %" PRIx64,
- info->suid.suid_high, info->suid.suid_low);
- } else {
- SeeCalData *cal = &calInfo[calIndex].cal;
+ SeeCalHelper *calHelper = info->calHelper;
- cal->hasBias = (offset.index == 3);
- if (cal->hasBias) {
- memcpy(cal->bias, offset.val, sizeof(cal->bias));
- }
+ bool hasBias = (offset.index == 3);
+ bool hasScale = (scale.index == 3);
+ bool hasMatrix = (matrix.index == 9);
+ uint8_t accuracy = static_cast<uint8_t>(event.status);
- cal->hasScale = (scale.index == 3);
- if (cal->hasScale) {
- memcpy(cal->scale, scale.val, sizeof(cal->scale));
- }
-
- cal->hasMatrix = (matrix.index == 9);
- if (cal->hasScale) {
- memcpy(cal->matrix, matrix.val, sizeof(cal->matrix));
- }
-
- cal->accuracy = static_cast<uint8_t>(event.status);
- }
+ calHelper->updateCalibration(
+ info->suid, hasBias, offset.val, hasScale, scale.val,
+ hasMatrix, matrix.val, accuracy);
}
return success;
}
@@ -1017,7 +1014,7 @@
} else {
float value = static_cast<float>(event.proximity_event_type);
auto *info = static_cast<SeeInfoArg *>(*arg);
- populateEventSample(info->data, &value);
+ populateEventSample(info, &value);
}
return success;
}
@@ -1041,6 +1038,41 @@
return success;
}
+bool decodeSnsResamplerConfigEvent(pb_istream_t *stream,
+ const pb_field_t *field, void **arg) {
+ sns_resampler_config_event event = {};
+
+ bool success = pb_decode(stream, sns_resampler_config_event_fields, &event);
+ if (!success) {
+ LOG_NANOPB_ERROR(stream);
+ } else {
+ auto *info = static_cast<SeeInfoArg *>(*arg);
+ LOGD("SensorType %" PRIu8 " resampler quality %" PRIu8,
+ static_cast<uint8_t>(info->data->sensorType),
+ static_cast<uint8_t>(event.quality));
+ }
+ return success;
+}
+
+/**
+ * Decode messages defined in sns_resampler.proto
+ */
+bool decodeSnsResamplerProtoEvent(pb_istream_t *stream, const pb_field_t *field,
+ void **arg) {
+ bool success = false;
+
+ auto *info = static_cast<SeeInfoArg *>(*arg);
+ switch (info->msgId) {
+ case SNS_RESAMPLER_MSGID_SNS_RESAMPLER_CONFIG_EVENT:
+ success = decodeSnsResamplerConfigEvent(stream, field, arg);
+ break;
+
+ default:
+ LOGW("Unhandled sns_resampler.proto msg ID %" PRIu32, info->msgId);
+ }
+ return success;
+}
+
bool decodeSnsRemoteProcStateEvent(
pb_istream_t *stream, const pb_field_t *field, void **arg) {
sns_remote_proc_state_event event = sns_remote_proc_state_event_init_default;
@@ -1107,6 +1139,10 @@
payload->funcs.decode = decodeSnsProximityProtoEvent;
break;
+ case SNS_RESAMPLER_MSGID_SNS_RESAMPLER_CONFIG_EVENT:
+ payload->funcs.decode = decodeSnsResamplerProtoEvent;
+ break;
+
default:
success = false;
LOGW("Unhandled msg ID %" PRIu32, info->msgId);
@@ -1192,12 +1228,6 @@
if (suidFound) {
LOGE("Unmatched client: %p, SUID 0x%016" PRIx64 " %016" PRIx64,
client, suid.suid_high, suid.suid_low);
- // TODO: remove after b/79993302 is resolved.
- for (const auto& sensorInfo : sensorInfos) {
- LOGE(" %p, 0x%016" PRIx64 " %016" PRIx64,
- sensorInfo.client,
- sensorInfo.suid.suid_high, sensorInfo.suid.suid_low);
- }
// Return SensorType in the other sns_client that matches the SUID as a
// backup plan.
@@ -1245,6 +1275,10 @@
case SensorSampleType::Vendor2:
sampleSize = sizeof(chrexSensorVendor2SampleData);
break;
+
+ case SensorSampleType::Vendor3:
+ sampleSize = sizeof(chrexSensorVendor3SampleData);
+ break;
#endif // CHREX_SENSOR_SUPPORT
default:
@@ -1300,6 +1334,25 @@
.sns_client_send = sns_client_send,
};
+#ifdef CHRE_SLPI_UIMG_ENABLED
+const SeeHelper::SnsClientApi BigImageSeeHelper::kQmiApi = {
+ .sns_client_init = sns_qmi_client_init,
+ .sns_client_deinit = sns_qmi_client_deinit,
+ .sns_client_send = sns_qmi_client_send,
+};
+#endif // CHRE_SLPI_UIMG_ENABLED
+
+SeeHelper::SeeHelper() {
+ mCalHelper = memoryAlloc<SeeCalHelper>();
+ if (mCalHelper == nullptr) {
+ FATAL_ERROR("Failed to allocate SeeCalHelper");
+ }
+ mOwnsCalHelper = true;
+}
+
+SeeHelper::SeeHelper(SeeCalHelper *calHelper)
+ : mCalHelper(calHelper), mOwnsCalHelper(false) {}
+
SeeHelper::~SeeHelper() {
for (auto *client : mSeeClients) {
int status = mSnsClientApi->sns_client_deinit(client);
@@ -1307,6 +1360,11 @@
LOGE("Failed to release sensor client: %d", status);
}
}
+
+ if (mOwnsCalHelper) {
+ mCalHelper->~SeeCalHelper();
+ memoryFree(mCalHelper);
+ }
}
void SeeHelper::handleSnsClientEventMsg(
@@ -1336,7 +1394,7 @@
data->info.data = &data->dataArg;
data->info.decodeMsgIdOnly = true;
data->info.remoteProcSuid = &mRemoteProcSuid;
- data->info.calInfo = &mCalInfo[0];
+ data->info.calHelper = mCalHelper;
data->event.events.funcs.decode = decodeSnsClientEventMsg;
data->event.events.arg = &data->info;
@@ -1346,7 +1404,6 @@
} else {
data->info.suid = data->event.suid;
data->info.decodeMsgIdOnly = false;
- data->info.data->cal = getCalDataFromSuid(data->info.suid);
data->info.data->sensorType = getSensorTypeFromSensorInfo(
data->info.client, data->info.suid, mSensorInfos);
@@ -1480,7 +1537,8 @@
return success;
}
-bool SeeHelper::init(SeeHelperCallbackInterface *cbIf, Microseconds timeout) {
+bool SeeHelper::init(SeeHelperCallbackInterface *cbIf, Microseconds timeout,
+ bool skipDefaultSensorInit) {
CHRE_ASSERT(cbIf);
mCbIf = cbIf;
@@ -1489,8 +1547,10 @@
// Initialize cal/remote_proc_state sensors before making sensor data request.
return (waitForService(&client, timeout)
&& mSeeClients.push_back(client)
- && initCalSensors()
- && initRemoteProcSensor());
+ && initResamplerSensor()
+ && (skipDefaultSensorInit
+ || (mCalHelper->registerForCalibrationUpdates(*this)
+ && initRemoteProcSensor())));
}
bool SeeHelper::makeRequest(const SeeSensorRequest& request) {
@@ -1510,8 +1570,14 @@
msgId = SNS_CLIENT_MSGID_SNS_CLIENT_DISABLE_REQ;
success = true;
} else if (sensorTypeIsContinuous(request.sensorType)) {
- msgId = SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG;
- success = encodeSnsStdSensorConfig(request, &msg, &msgLen);
+ if (suidsMatch(sensorInfo->suid, mResamplerSuid.value())) {
+ msgId = SNS_RESAMPLER_MSGID_SNS_RESAMPLER_CONFIG;
+ success = encodeSnsResamplerConfig(
+ request, sensorInfo->physicalSuid, &msg, &msgLen);
+ } else {
+ msgId = SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG;
+ success = encodeSnsStdSensorConfig(request, &msg, &msgLen);
+ }
} else {
msgId = SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG;
// No sample rate needed to configure on-change or one-shot sensors.
@@ -1529,15 +1595,15 @@
return success;
}
-const sns_std_suid& SeeHelper::getCalSuidFromSensorType(
- SensorType sensorType) const {
- static sns_std_suid suid = sns_suid_sensor_init_zero;
-
- size_t calIndex = getCalIndexFromSensorType(sensorType);
- if (calIndex < kNumSeeCalSensors && mCalInfo[calIndex].suid.has_value()) {
- suid = mCalInfo[calIndex].suid.value();
- }
- return suid;
+bool SeeHelper::configureOnChangeSensor(const sns_std_suid& suid, bool enable) {
+ uint32_t msgId = (enable)
+ ? SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG
+ : SNS_CLIENT_MSGID_SNS_CLIENT_DISABLE_REQ;
+ return sendReq(
+ suid, nullptr /* syncData */, nullptr /* syncDataType */,
+ msgId, nullptr /* msg */, 0 /* msgLen */,
+ false /* batchValid */, 0 /* batchPeriodUs */,
+ false /* passive */, false /* waitForIndication */);
}
/**
@@ -1582,11 +1648,19 @@
if (!waitSuccess) {
LOGE("SEE resp timed out after %" PRIu64 " ms",
Milliseconds(timeoutResp).getMilliseconds());
- } else if (mRespError != SNS_STD_ERROR_NO_ERROR) {
- LOGE("SEE txn ID %" PRIu32 " failed with error %d",
- mCurrentTxnId, mRespError);
+
+ if (++mNumMissingResp >= kSeeNumMissingResp) {
+ FATAL_ERROR("%" PRIu32 " consecutive missing responses",
+ mNumMissingResp);
+ }
} else {
- success = true;
+ mNumMissingResp = 0;
+ if (mRespError != SNS_STD_ERROR_NO_ERROR) {
+ LOGE("SEE txn ID %" PRIu32 " failed with error %d",
+ mCurrentTxnId, mRespError);
+ } else {
+ success = true;
+ }
}
}
mWaitingOnResp = false;
@@ -1674,44 +1748,54 @@
}
bool SeeHelper::registerSensor(
- SensorType sensorType, const sns_std_suid& suid, bool *prevRegistered) {
+ SensorType sensorType, const sns_std_suid& suid, bool resample,
+ bool *prevRegistered) {
CHRE_ASSERT(sensorType != SensorType::Unknown);
CHRE_ASSERT(prevRegistered != nullptr);
bool success = false;
- // Check whether the SUID/SensorType pair has been previously registered.
- // Also count how many other SensorTypes the SUID has been registered with.
- *prevRegistered = false;
- size_t suidRegCount = 0;
- for (const auto& sensorInfo : mSensorInfos) {
- if (suidsMatch(suid, sensorInfo.suid)) {
- suidRegCount++;
- if (sensorInfo.sensorType == sensorType) {
- *prevRegistered = true;
+ bool doResample = resample && sensorTypeIsContinuous(sensorType);
+ if (doResample && !mResamplerSuid.has_value()) {
+ LOGE("Unable to use resampler without its SUID");
+ } else {
+ // The SUID to make request to.
+ const sns_std_suid& reqSuid = doResample ? mResamplerSuid.value() : suid;
+
+ // Check whether the SUID/SensorType pair has been previously registered.
+ // Also count how many other SensorTypes the SUID has been registered with.
+ *prevRegistered = false;
+ size_t suidRegCount = 0;
+ for (const auto& sensorInfo : mSensorInfos) {
+ if (suidsMatch(reqSuid, sensorInfo.suid)) {
+ suidRegCount++;
+ if (sensorInfo.sensorType == sensorType) {
+ *prevRegistered = true;
+ }
}
}
- }
- // Initialize another SEE client if the SUID has been previously
- // registered with more SensorTypes than the number of SEE clients can
- // disambiguate.
- bool clientAvailable = true;
- if (mSeeClients.size() <= suidRegCount) {
- sns_client *client;
- clientAvailable = waitForService(&client);
- if (clientAvailable) {
- clientAvailable = mSeeClients.push_back(client);
+ // Initialize another SEE client if the SUID has been previously
+ // registered with more SensorTypes than the number of SEE clients can
+ // disambiguate.
+ bool clientAvailable = true;
+ if (mSeeClients.size() <= suidRegCount) {
+ sns_client *client;
+ clientAvailable = waitForService(&client);
+ if (clientAvailable) {
+ clientAvailable = mSeeClients.push_back(client);
+ }
}
- }
- // Add a new entry only if this SUID/SensorType pair hasn't been registered.
- if (!*prevRegistered && clientAvailable) {
- SensorInfo sensorInfo = {
- .suid = suid,
- .sensorType = sensorType,
- .client = mSeeClients[suidRegCount],
- };
- success = mSensorInfos.push_back(sensorInfo);
+ // Add a new entry only if this SUID/SensorType pair hasn't been registered.
+ if (!*prevRegistered && clientAvailable) {
+ SensorInfo sensorInfo = {
+ .suid = reqSuid,
+ .sensorType = sensorType,
+ .client = mSeeClients[suidRegCount],
+ .physicalSuid = suid,
+ };
+ success = mSensorInfos.push_back(sensorInfo);
+ }
}
return success;
}
@@ -1737,50 +1821,6 @@
return success;
}
-bool SeeHelper::initCalSensors() {
- bool success = true;
-
- // Zero out mCalInfo to avoid accidental suid and data match.
- memset(mCalInfo, 0, sizeof(mCalInfo));
-
- const char *kCalTypes[] = {
- "accel_cal",
- "gyro_cal",
- "mag_cal",
- };
-
- // Find the cal sensor's SUID, assign it to mCalInfo, and make cal sensor data
- // request.
- DynamicVector<sns_std_suid> suids;
- for (size_t i = 0; i < ARRAY_SIZE(kCalTypes); i++) {
- const char *calType = kCalTypes[i];
- if (!findSuidSync(calType, &suids)) {
- success = false;
- LOGE("Failed to find sensor '%s'", calType);
- } else {
- size_t index = getCalIndexFromDataType(calType);
- if (index >= kNumSeeCalSensors) {
- success = false;
- LOGE("Cal sensor '%s' index out of bounds", calType);
- } else {
- mCalInfo[index].suid = suids[0];
-
- if (!sendReq(suids[0], nullptr /* syncData */,
- nullptr /* syncDataType */,
- SNS_STD_SENSOR_MSGID_SNS_STD_ON_CHANGE_CONFIG,
- nullptr /* msg */, 0 /* msgLen */,
- false /* batchValid */, 0 /* batchPeriodUs */,
- false /* passive */, false /* waitForIndication */)) {
- success = false;
- LOGE("Failed to request '%s' data", calType);
- }
- }
- }
- }
-
- return success;
-}
-
bool SeeHelper::initRemoteProcSensor() {
bool success = false;
@@ -1811,9 +1851,18 @@
return success;
}
-SeeCalData *SeeHelper::getCalDataFromSuid(const sns_std_suid& suid) {
- size_t calIndex = getCalIndexFromSuid(suid, mCalInfo);
- return (calIndex < kNumSeeCalSensors) ? &mCalInfo[calIndex].cal : nullptr;
+bool SeeHelper::initResamplerSensor() {
+ bool success = false;
+
+ const char *kResamplerType = "resampler";
+ DynamicVector<sns_std_suid> suids;
+ if (!findSuidSync(kResamplerType, &suids)) {
+ LOGE("Failed to find sensor '%s'", kResamplerType);
+ } else {
+ mResamplerSuid = suids[0];
+ success = true;
+ }
+ return success;
}
const SeeHelper::SensorInfo *SeeHelper::getSensorInfo(
diff --git a/util/include/chre/util/array_queue.h b/util/include/chre/util/array_queue.h
index db70634..2ff6e7a 100644
--- a/util/include/chre/util/array_queue.h
+++ b/util/include/chre/util/array_queue.h
@@ -146,6 +146,11 @@
bool emplace(Args&&... args);
/**
+ * Removes all the elements of the queue.
+ */
+ void clear();
+
+ /**
* A template class that implements a forward iterator for the array queue.
*/
template<typename ValueType>
diff --git a/util/include/chre/util/array_queue_impl.h b/util/include/chre/util/array_queue_impl.h
index d3ba2e8..0de6341 100644
--- a/util/include/chre/util/array_queue_impl.h
+++ b/util/include/chre/util/array_queue_impl.h
@@ -27,9 +27,7 @@
template<typename ElementType, size_t kCapacity>
ArrayQueue<ElementType, kCapacity>::~ArrayQueue() {
- while (!empty()) {
- pop();
- }
+ clear();
}
template<typename ElementType, size_t kCapacity>
@@ -166,6 +164,19 @@
}
template<typename ElementType, size_t kCapacity>
+void ArrayQueue<ElementType, kCapacity>::clear() {
+ if (!std::is_trivially_destructible<ElementType>::value) {
+ while (!empty()) {
+ pop();
+ }
+ } else {
+ mSize = 0;
+ mHead = 0;
+ mTail = kCapacity - 1;
+ }
+}
+
+template<typename ElementType, size_t kCapacity>
typename ArrayQueue<ElementType, kCapacity>::iterator
ArrayQueue<ElementType, kCapacity>::begin() {
// Align begin() and end() outside of the memory block when empty.
diff --git a/util/include/chre/util/nanoapp/app_id.h b/util/include/chre/util/nanoapp/app_id.h
index 9ee7b18..7057954 100644
--- a/util/include/chre/util/nanoapp/app_id.h
+++ b/util/include/chre/util/nanoapp/app_id.h
@@ -65,19 +65,20 @@
return makeNanoappId(CHRE_VENDOR_ID_GOOGLE, appNumber);
}
-constexpr uint64_t kHelloWorldAppId = makeExampleNanoappId(1);
-constexpr uint64_t kMessageWorldAppId = makeExampleNanoappId(2);
-constexpr uint64_t kTimerWorldAppId = makeExampleNanoappId(3);
-constexpr uint64_t kSensorWorldAppId = makeExampleNanoappId(4);
-constexpr uint64_t kGnssWorldAppId = makeExampleNanoappId(5);
-constexpr uint64_t kWifiWorldAppId = makeExampleNanoappId(6);
-constexpr uint64_t kWwanWorldAppId = makeExampleNanoappId(7);
+constexpr uint64_t kHelloWorldAppId = makeExampleNanoappId(1);
+constexpr uint64_t kMessageWorldAppId = makeExampleNanoappId(2);
+constexpr uint64_t kTimerWorldAppId = makeExampleNanoappId(3);
+constexpr uint64_t kSensorWorldAppId = makeExampleNanoappId(4);
+constexpr uint64_t kGnssWorldAppId = makeExampleNanoappId(5);
+constexpr uint64_t kWifiWorldAppId = makeExampleNanoappId(6);
+constexpr uint64_t kWwanWorldAppId = makeExampleNanoappId(7);
// 8 = reserved (previously used by ImuCal)
-constexpr uint64_t kSpammerAppId = makeExampleNanoappId(9);
-constexpr uint64_t kUnloadTesterAppId = makeExampleNanoappId(10);
-constexpr uint64_t kAshWorldAppId = makeExampleNanoappId(11);
-constexpr uint64_t kAudioWorldAppId = makeExampleNanoappId(12);
-constexpr uint64_t kHostAwakeWorldAppId = makeExampleNanoappId(13);
+constexpr uint64_t kSpammerAppId = makeExampleNanoappId(9);
+constexpr uint64_t kUnloadTesterAppId = makeExampleNanoappId(10);
+constexpr uint64_t kAshWorldAppId = makeExampleNanoappId(11);
+constexpr uint64_t kAudioWorldAppId = makeExampleNanoappId(12);
+constexpr uint64_t kHostAwakeWorldAppId = makeExampleNanoappId(13);
+constexpr uint64_t kAudioStressTestAppId = makeExampleNanoappId(14);
} // namespace chre
diff --git a/util/include/chre/util/time.h b/util/include/chre/util/time.h
index 9a2c065..0d97231 100644
--- a/util/include/chre/util/time.h
+++ b/util/include/chre/util/time.h
@@ -22,10 +22,10 @@
namespace chre {
//! The number of milliseconds in one min.
-constexpr uint64_t kOneMinuteInMillisecods(60000);
+constexpr uint64_t kOneMinuteInMilliseconds(60000);
//! The number of milliseconds in one second.
-constexpr uint64_t kOneSecondInMillisecods(1000);
+constexpr uint64_t kOneSecondInMilliseconds(1000);
//! The number of nanoseconds in one second.
constexpr uint64_t kOneSecondInNanoseconds(1000000000);
@@ -60,6 +60,13 @@
*/
constexpr uint64_t toRawNanoseconds() const;
+ /**
+ * Obtains the number of Milliseconds stored by this time duration.
+ *
+ * @return the value of milliseconds.
+ */
+ constexpr uint64_t getMilliseconds() const;
+
private:
uint64_t mSeconds;
};
diff --git a/util/include/chre/util/time_impl.h b/util/include/chre/util/time_impl.h
index 9d8adba..7e79cf8 100644
--- a/util/include/chre/util/time_impl.h
+++ b/util/include/chre/util/time_impl.h
@@ -28,10 +28,18 @@
// Perform the simple unit conversion. Warning: overflow is caught and
// handled by returning UINT64_MAX. A ternary expression is used because
// constexpr requires it.
- return mSeconds > (UINT64_MAX / kOneSecondInNanoseconds) ? UINT64_MAX
+ return (mSeconds > (UINT64_MAX / kOneSecondInNanoseconds)) ? UINT64_MAX
: mSeconds * kOneSecondInNanoseconds;
}
+constexpr uint64_t Seconds::getMilliseconds() const {
+ // Perform the simple unit conversion. Warning: overflow is caught and
+ // handled by returning UINT64_MAX. A ternary expression is used because
+ // constexpr requires it.
+ return (mSeconds > (UINT64_MAX / kOneSecondInMilliseconds)) ? UINT64_MAX
+ : mSeconds * kOneSecondInMilliseconds;
+}
+
constexpr Milliseconds::Milliseconds()
: mMilliseconds(0) {}
@@ -51,7 +59,11 @@
}
constexpr uint64_t Milliseconds::getMicroseconds() const {
- return mMilliseconds * kOneMillisecondInMicroseconds;
+ // Perform the simple unit conversion. Warning: overflow is caught and
+ // handled by returning UINT64_MAX. A ternary expression is used because
+ // constexpr requires it.
+ return (mMilliseconds > (UINT64_MAX / kOneMillisecondInMicroseconds))
+ ? UINT64_MAX : mMilliseconds * kOneMillisecondInMicroseconds ;
}
constexpr uint64_t Milliseconds::getMilliseconds() const {
diff --git a/util/tests/array_queue_test.cc b/util/tests/array_queue_test.cc
index 8dd631e..3f425ce 100644
--- a/util/tests/array_queue_test.cc
+++ b/util/tests/array_queue_test.cc
@@ -11,6 +11,7 @@
constexpr int kMaxTestCapacity = 10;
int destructor_count[kMaxTestCapacity];
int constructor_count;
+int total_destructor_count;
class DummyElement {
public:
@@ -22,6 +23,7 @@
constructor_count++;
};
~DummyElement() {
+ total_destructor_count++;
if (val_ >= 0 && val_ < kMaxTestCapacity) {
destructor_count[val_]++;
}
@@ -466,3 +468,44 @@
std::is_same<traits::iterator_category, std::forward_iterator_tag>::value,
"ArrayQueueIterator should be a forward iterator");
}
+
+TEST(ArrayQueueTest, ArrayClear) {
+ ArrayQueue<size_t, 4> q;
+
+ q.clear();
+ EXPECT_TRUE(q.empty());
+
+ for (size_t i = 0; i < 4; i++) {
+ q.push(i);
+ }
+
+ q.clear();
+ EXPECT_TRUE(q.empty());
+
+ // Make sure that insertion/access still work after a clear.
+ for (size_t i = 0; i < 4; i++) {
+ q.push(i);
+ }
+ for (size_t i = 0; i < 4; i++) {
+ EXPECT_EQ(q[i], i);
+ }
+}
+
+TEST(ArrayQueueTest, ElementsDestructedArrayClear) {
+ for (size_t i = 0; i < kMaxTestCapacity; ++i) {
+ destructor_count[i] = 0;
+ }
+ total_destructor_count = 0;
+
+ ArrayQueue<DummyElement, 4> q;
+ for (size_t i = 0; i < 3; ++i) {
+ q.emplace(i);
+ }
+
+ q.clear();
+
+ for (size_t i = 0; i < 3; ++i) {
+ EXPECT_EQ(1, destructor_count[i]);
+ }
+ EXPECT_EQ(3, total_destructor_count);
+}
diff --git a/util/tests/time_test.cc b/util/tests/time_test.cc
index 94d1032..96616e4 100644
--- a/util/tests/time_test.cc
+++ b/util/tests/time_test.cc
@@ -22,10 +22,11 @@
using chre::Milliseconds;
using chre::Microseconds;
using chre::Nanoseconds;
+using chre::kOneSecondInMilliseconds;
using chre::kOneSecondInNanoseconds;
+using chre::kOneMillisecondInMicroseconds;
using chre::kOneMillisecondInNanoseconds;
using chre::kOneMicrosecondInNanoseconds;
-using chre::kOneMillisecondInMicroseconds;
// Tests for Time constants
TEST(Time, CheckTimeConversionConstants) {
@@ -46,6 +47,16 @@
EXPECT_EQ(t.toRawNanoseconds(), UINT64_MAX);
}
+TEST(Time, ConvertSecToMillisec) {
+ Seconds t(5);
+ EXPECT_EQ(t.getMilliseconds(), 5 * kOneSecondInMilliseconds);
+}
+
+TEST(Time, ConvertSecToMillisecOverflowIsUint64Max) {
+ Seconds t(UINT64_MAX / kOneSecondInMilliseconds + 1);
+ EXPECT_EQ(t.getMilliseconds(), UINT64_MAX);
+}
+
// Tests for Milliseconds
TEST(Time, DefaultMillisecIsZero) {
Milliseconds t;
@@ -68,6 +79,11 @@
EXPECT_EQ(t.getMicroseconds(), 5 * kOneMillisecondInMicroseconds);
}
+TEST(Time, ConvertMillisecToMicrosecOverflowIsUint64Max) {
+ Milliseconds t(UINT64_MAX / kOneMillisecondInMicroseconds + 1);
+ EXPECT_EQ(t.getMicroseconds(), UINT64_MAX);
+}
+
TEST(Time, ConvertMillisecToNanosec) {
Milliseconds t(5);
EXPECT_EQ(t.toRawNanoseconds(), 5 * kOneMillisecondInNanoseconds);