blob: 4253866fe2f7e729dd2d76001ea131ebd52fa137 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <general_test/basic_sensor_test_base.h>
#include <cinttypes>
#include <cstddef>
#include <shared/send_message.h>
#include <shared/time_util.h>
#include <chre.h>
using nanoapp_testing::kOneMillisecondInNanoseconds;
using nanoapp_testing::kOneSecondInNanoseconds;
using nanoapp_testing::MessageType;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendFatalFailureToHostUint8;
using nanoapp_testing::sendInternalFailureToHost;
using nanoapp_testing::sendStringToHost;
using nanoapp_testing::sendSuccessToHost;
/*
* Our general test flow is as follows:
*
* Constructor: Send startEvent to self to start.
* StartEvent: Get default sensor and perform various sanity checks. Configure
* the sensor.
*
* At this point, it depends what kind of sensor we have for how we proceed
* with the test.
*
* One-shot: finishTest()
* On-change: Wait for one data event from sensor. Then finishTest().
* Continuous: Wait for two data events from sensor. Then finishTest().
*
* We also look for and perform basic sanity checking on sampling status
* change events, as well as bias data reports.
*/
namespace general_test {
namespace {
constexpr uint16_t kStartEvent = CHRE_EVENT_FIRST_USER_VALUE;
constexpr uint64_t kEventLoopSlack = 100 * kOneMillisecondInNanoseconds;
uint64_t getEventDuration(const chreSensorThreeAxisData *event) {
uint64_t duration = 0;
for (size_t i = 0; i < event->header.readingCount; i++) {
duration += event->readings[i].timestampDelta;
}
return duration;
}
} // anonymous namespace
BasicSensorTestBase::BasicSensorTestBase()
: Test(CHRE_API_VERSION_1_0),
mInMethod(true),
mExternalSamplingStatusChange(false),
mState(State::kPreStart),
mInstanceId(chreGetInstanceId())
/* All other members initialized later */ {}
void BasicSensorTestBase::setUp(uint32_t messageSize,
const void * /* message */) {
if (messageSize != 0) {
sendFatalFailureToHost("Beginning message expects 0 additional bytes, got ",
&messageSize);
}
// Most tests start running in the constructor. However, since this
// is a base class, and we invoke abstract methods when running our
// test, we don't start until after the class has been fully
// constructed.
if (!chreSendEvent(kStartEvent, nullptr, nullptr, mInstanceId)) {
sendFatalFailureToHost("Failed chreSendEvent to begin test");
}
mInMethod = false;
}
void BasicSensorTestBase::checkPassiveConfigure() {
chreSensorConfigureMode mode =
isOneShotSensor() ? CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_ONE_SHOT
: CHRE_SENSOR_CONFIGURE_MODE_PASSIVE_CONTINUOUS;
if (mApiVersion == CHRE_API_VERSION_1_0) {
// Any attempt to make a PASSIVE call with a non-default interval
// or latency should fail.
if (chreSensorConfigure(mSensorHandle, mode, CHRE_SENSOR_INTERVAL_DEFAULT,
999)) {
sendFatalFailureToHost(
"chreSensorConfigure() allowed passive with different latency");
}
if (chreSensorConfigure(mSensorHandle, mode, 999,
CHRE_SENSOR_LATENCY_DEFAULT)) {
sendFatalFailureToHost(
"chreSensorConfigure() allowed passive with different interval");
}
// TODO: In a more in-depth test, we should test passive mode
// receiving data. This is somewhat complicated by the fact that
// pretty much by definition, we don't control whether a sensor
// we're passively listening to is enabled or not. We could try
// to control this with an additional test nanoapp toggling sensor
// usage, but there's still the complication of other nanoapps in
// the system.
} else {
bool configureSuccess =
chreSensorConfigure(mSensorHandle, mode, CHRE_SENSOR_INTERVAL_DEFAULT,
kOneSecondInNanoseconds);
if (mSupportsPassiveMode && !configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() failed passive with default interval and "
"non-default latency");
} else if (!mSupportsPassiveMode && configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() accepted passive with default interval and "
"non-default latency");
}
if (!isOneShotSensor()) {
configureSuccess =
chreSensorConfigure(mSensorHandle, mode, kOneSecondInNanoseconds,
CHRE_SENSOR_LATENCY_DEFAULT);
if (mSupportsPassiveMode && !configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() failed passive with non-default interval "
"and default latency");
} else if (!mSupportsPassiveMode && configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() accepted passive with non-default "
"interval and default latency");
}
configureSuccess =
chreSensorConfigure(mSensorHandle, mode, kOneSecondInNanoseconds,
kOneSecondInNanoseconds);
if (mSupportsPassiveMode && !configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() failed passive with non-default interval "
"and latency");
} else if (!mSupportsPassiveMode && configureSuccess) {
sendFatalFailureToHost(
"chreSensorConfigure() accepted passive with non-default interval "
"and latency");
}
}
}
}
void BasicSensorTestBase::startTest() {
mState = State::kPreConfigure;
if (!chreSensorFindDefault(getSensorType(), &mSensorHandle)) {
if (isRequiredSensor()) {
sendFatalFailureToHost("Sensor is required, but no default found.");
}
sendStringToHost(MessageType::kSkipped,
"No default sensor found for optional sensor.");
return;
}
chreSensorInfo info;
if (!chreGetSensorInfo(mSensorHandle, &info)) {
sendFatalFailureToHost("GetSensorInfo() call failed");
}
if (info.sensorName == nullptr) {
sendFatalFailureToHost("chreSensorInfo::sensorName is NULL");
}
if (info.sensorType != getSensorType()) {
uint32_t type = info.sensorType;
sendFatalFailureToHost(
"chreSensorInfo::sensorType is not expected value, is:", &type);
}
if (info.isOnChange != isOnChangeSensor()) {
sendFatalFailureToHost(
"chreSensorInfo::isOnChange is opposite of what we expected");
}
if (info.isOneShot != isOneShotSensor()) {
sendFatalFailureToHost(
"chreSensorInfo::isOneShot is opposite of what we expected");
}
if (mApiVersion >= CHRE_API_VERSION_1_4) {
mSupportsPassiveMode = info.supportsPassiveMode;
} else if (info.supportsPassiveMode != 0) {
sendFatalFailureToHost("chreSensorInfo::supportsPassiveMode should be 0");
}
if (!chreGetSensorSamplingStatus(mSensorHandle, &mOriginalStatus)) {
sendFatalFailureToHost("chreGetSensorSamplingStatus() failed");
}
// Set the base timestamp to compare against before configuring the sensor.
mPreTimestamp = chreGetTime();
// Default interval/latency must be accepted by all sensors.
mNewStatus = {
CHRE_SENSOR_INTERVAL_DEFAULT, /* interval */
CHRE_SENSOR_LATENCY_DEFAULT, /* latency */
true /* enabled */
};
chreSensorConfigureMode mode = isOneShotSensor()
? CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT
: CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS;
if (!chreSensorConfigure(mSensorHandle, mode, mNewStatus.interval,
mNewStatus.latency)) {
sendFatalFailureToHost(
"chreSensorConfigure() call failed with default interval and latency");
}
// handleEvent may start getting events, and our testing continues there.
// (Note: The CHRE is not allow to call handleEvent() while we're still
// in this method, so it's not a race to set this state here.)
// Set a new request so the test can receive events before test timeout.
mNewStatus = {
// This will be valid on all required sensors.
// TODO: A more in-depth test could try to change this interval
// from what it currently is for the sensor, and confirm it
// changes back when we're DONE. But that's beyond the current
// scope of this 'basic' test.
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.
CHRE_SENSOR_LATENCY_ASAP, /* latency */
true /* enabled */
};
// Skip one-shot sensors for non-default interval configurations.
if (!isOneShotSensor() &&
!chreSensorConfigure(mSensorHandle, mode, mNewStatus.interval,
mNewStatus.latency)) {
sendFatalFailureToHost("chreSensorConfigure() call failed");
}
if (isOnChangeSensor()) {
// We should receive the current state of this sensor after the
// configure call. However, we're not assured additional events,
// since we don't know if this is going to change. Thus, we jump
// our testing state to waiting for the last event.
mState = State::kExpectingLastDataEvent;
} else if (isOneShotSensor()) {
// There's no assurance we'll get any events from a one-shot
// sensor, so we'll just skip to the end of the test.
finishTest();
} else {
mState = State::kExpectingInitialDataEvent;
}
}
void BasicSensorTestBase::finishTest() {
checkPassiveConfigure();
if (!chreSensorConfigureModeOnly(mSensorHandle,
CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
sendFatalFailureToHost("Unable to configure sensor mode to DONE");
}
mDoneTimestamp = chreGetTime();
chreSensorSamplingStatus status;
if (!chreGetSensorSamplingStatus(mSensorHandle, &status)) {
sendFatalFailureToHost("Could not get final sensor info");
}
if (!mExternalSamplingStatusChange) {
// No one else changed this, so it should be what we had before.
if (status.enabled != mOriginalStatus.enabled) {
sendFatalFailureToHost("SensorInfo.enabled not back to original");
}
// 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;
sendSuccessToHost();
}
void BasicSensorTestBase::sanityCheckHeader(const chreSensorDataHeader *header,
bool modifyTimestamps,
uint64_t eventDuration) {
if (header->sensorHandle != mSensorHandle) {
sendFatalFailureToHost("SensorDataHeader for wrong handle",
&header->sensorHandle);
}
if (!isOnChangeSensor()) {
// An on-change sensor is supposed to send its current state, which
// could be timestamped in the past. Everything else should be
// getting recent data.
uint64_t *minTime = nullptr;
uint64_t *timeToUpdate = nullptr;
if (mState == State::kExpectingInitialDataEvent) {
minTime = &mPreTimestamp;
timeToUpdate = &mFirstEventTimestamp;
} else if (mState == State::kExpectingLastDataEvent) {
minTime = &mFirstEventTimestamp;
timeToUpdate = &mLastEventTimestamp;
} else { // State::kFinished
minTime = &mLastEventTimestamp;
// Go ahead and update this timestamp again.
timeToUpdate = &mLastEventTimestamp;
}
// If there's another CHRE client requesting batched sensor data,
// baseTimestamp can be before mPreTimestamp. Also allow
// kEventLoopSlack to handle this nanoapp before handling the sensor
// event.
uint64_t minTimeWithSlack =
(*minTime > eventDuration + kEventLoopSlack)
? (*minTime - eventDuration - kEventLoopSlack)
: 0;
if (header->baseTimestamp < minTimeWithSlack) {
chreLog(CHRE_LOG_ERROR,
"baseTimestamp %" PRIu64 " < minTimeWithSlack %" PRIu64
": minTime %" PRIu64 " eventDuration %" PRIu64
" kEventLoopSlack %" PRIu64,
header->baseTimestamp, minTimeWithSlack, *minTime, eventDuration,
kEventLoopSlack);
sendFatalFailureToHost("SensorDataHeader is in the past");
}
if ((mState == State::kFinished) &&
(header->baseTimestamp > mDoneTimestamp)) {
sendFatalFailureToHost("SensorDataHeader is from after DONE");
}
if (modifyTimestamps) {
*timeToUpdate = header->baseTimestamp;
}
}
if (header->readingCount == 0) {
sendFatalFailureToHost("SensorDataHeader has readingCount of 0");
}
if (header->reserved != 0) {
sendFatalFailureToHost("SensorDataHeader has non-zero reserved field");
}
if (mApiVersion < CHRE_API_VERSION_1_3) {
if (header->accuracy != 0) {
sendFatalFailureToHost("SensorDataHeader has non-zero reserved field");
}
} else if (header->accuracy > CHRE_SENSOR_ACCURACY_HIGH) {
sendFatalFailureToHostUint8("Sensor accuracy is not within valid range: ",
header->accuracy);
}
}
void BasicSensorTestBase::handleBiasEvent(
uint16_t eventType, const chreSensorThreeAxisData *eventData) {
uint8_t expectedSensorType = 0;
uint32_t eType = eventType;
if (eventType == CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO) {
expectedSensorType = CHRE_SENSOR_TYPE_GYROSCOPE;
} else if (eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO) {
expectedSensorType = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
} else {
sendInternalFailureToHost("Illegal eventType in handleBiasEvent", &eType);
}
if (expectedSensorType != getSensorType()) {
sendFatalFailureToHost("Unexpected bias event:", &eType);
}
sanityCheckHeader(&eventData->header, false, getEventDuration(eventData));
// TODO: Sanity check the eventData. This check is out-of-scope for
// Android N testing.
}
void BasicSensorTestBase::handleSamplingChangeEvent(
const chreSensorSamplingStatusEvent *eventData) {
if (eventData->sensorHandle != mSensorHandle) {
sendFatalFailureToHost("SamplingChangeEvent for wrong sensor handle:",
&eventData->sensorHandle);
}
if (mState == State::kFinished) {
// TODO: If we strictly define whether this event is or isn't
// generated upon being DONE with a sensor, then we can perform
// a strict check here. For now, we just let this go.
return;
}
// Passive sensor requests do not guarantee sensors will always be enabled.
// Bypass 'enabled' check for passive configurations.
if (!eventData->status.enabled) {
sendFatalFailureToHost("SamplingChangeEvent disabled the sensor.");
}
if ((mNewStatus.interval != eventData->status.interval) ||
(mNewStatus.latency != eventData->status.latency)) {
// This is from someone other than us. Let's note that so we know
// our sanity checks are invalid.
mExternalSamplingStatusChange = true;
}
}
void BasicSensorTestBase::handleSensorDataEvent(const void *eventData) {
if ((mState == State::kPreStart) || (mState == State::kPreConfigure)) {
sendFatalFailureToHost("SensorDataEvent sent too early.");
}
// Note, if mState is kFinished, we could be getting batched data which
// hadn't been delivered yet at the time we were DONE. We'll sanity
// check it, even though in theory we're done testing.
uint64_t eventDuration =
getEventDuration(static_cast<const chreSensorThreeAxisData *>(eventData));
sanityCheckHeader(static_cast<const chreSensorDataHeader *>(eventData), true,
eventDuration);
// Send to the sensor itself for any additional checks of actual data.
confirmDataIsSane(eventData);
if (mState == State::kExpectingInitialDataEvent) {
mState = State::kExpectingLastDataEvent;
} else if (mState == State::kExpectingLastDataEvent) {
finishTest();
} else if (mState != State::kFinished) {
uint32_t value = static_cast<uint32_t>(mState);
sendInternalFailureToHost("Illegal mState in handleSensorDataEvent:",
&value);
}
}
void BasicSensorTestBase::handleEvent(uint32_t senderInstanceId,
uint16_t eventType,
const void *eventData) {
if (mInMethod) {
sendFatalFailureToHost("handleEvent() invoked while already in method.");
}
mInMethod = true;
const uint16_t dataEventType =
CHRE_EVENT_SENSOR_DATA_EVENT_BASE + getSensorType();
if (senderInstanceId == mInstanceId) {
if ((eventType == kStartEvent) && (mState == State::kPreStart)) {
startTest();
}
} else if ((mState == State::kPreStart) || (mState == State::kPreConfigure)) {
unexpectedEvent(eventType);
} else if (senderInstanceId != CHRE_INSTANCE_ID) {
sendFatalFailureToHost("Unexpected senderInstanceId:", &senderInstanceId);
} else if (eventData == nullptr) {
uint32_t eType = eventType;
sendFatalFailureToHost("Got NULL eventData for event:", &eType);
} else if (eventType == dataEventType) {
handleSensorDataEvent(eventData);
} else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE) {
handleSamplingChangeEvent(
static_cast<const chreSensorSamplingStatusEvent *>(eventData));
} else if ((eventType == CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO) ||
(eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO)) {
handleBiasEvent(eventType,
static_cast<const chreSensorThreeAxisData *>(eventData));
} else {
unexpectedEvent(eventType);
}
mInMethod = false;
}
} // namespace general_test