/*
* Copyright (C) 2016 Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Nanoapp which performs a number of operations within nanoappStart().
*
* This nanoapp is to confirm a number of CHRE methods can be invoked from
* within nanoappStart(). There are other tests which test each of these
* CHRE methods more in depth. We're just doing a sanity check that calling
* from nanoappStart() works at all.
*
* Specifically, we're testing:
* o chreHeapAlloc() and chreHeapFree()
* o chreGetInstanceId()
* o chreSendEvent() [*]
* o chreTimerSet() [*]
* o chreSensorFindDefault() and chreSensorConfigure() [*]
* o chreSendMessageToHost() [**]
*
* [*] These require nanoappHandleEvent() to be called successfully in order
* to confirm.
* [**] This is confirmed by the host receiving this message.
*
* This isn't a "general" test, so it doesn't have a standard communication
* protocol. Notably, the Host doesn't send any messages to this nanoapp.
*
* Protocol:
* Nanoapp to Host: kContinue
* Nanoapp to Host: kSuccess
*/
#include <cinttypes>
#include <chre.h>
#include <shared/send_message.h>
using nanoapp_testing::MessageType;
using nanoapp_testing::sendMessageToHost;
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
static bool gInMethod = false;
static uint32_t gInstanceId;
static uint32_t gTimerId;
static uint32_t gSensorHandle;
constexpr size_t kSelfEventStage = 0;
constexpr size_t kTimerStage = 1;
constexpr size_t kSensorStage = 2;
constexpr size_t kStageCount = 3;
constexpr uint32_t kAllFinished = (1 << kStageCount) - 1;
static uint32_t gFinishedBitmask = 0;
constexpr uint16_t kEventType = CHRE_EVENT_FIRST_USER_VALUE;
static void markSuccess(uint32_t stage) {
uint32_t finishedBit = (1 << stage);
if ((kAllFinished & finishedBit) == 0) {
sendFatalFailureToHost("markSuccess bad stage", &stage);
}
if ((gFinishedBitmask & finishedBit) == 0) {
chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
gFinishedBitmask |= finishedBit;
if (gFinishedBitmask == kAllFinished) {
sendSuccessToHost();
}
}
}
static void checkSelfEvent(uint16_t eventType, const uint32_t *eventData) {
if (eventType != kEventType) {
uint32_t e = eventType;
sendFatalFailureToHost("Event from self, bad event type:", &e);
}
if (eventData == nullptr) {
sendFatalFailureToHost("Event from self, null data");
}
if (*eventData != gInstanceId) {
sendFatalFailureToHost("Event from self, bad data:", eventData);
}
markSuccess(kSelfEventStage);
}
static void checkTimerEvent(const uint32_t *eventData) {
if (eventData == nullptr) {
sendFatalFailureToHost("TimerEvent, null data");
}
if (*eventData != gInstanceId) {
sendFatalFailureToHost("TimerEvent, bad data:", eventData);
}
markSuccess(kTimerStage);
}
static void checkSensorEvent(const void *eventData) {
const chreSensorDataHeader* header =
static_cast<const chreSensorDataHeader *>(eventData);
if (header == nullptr) {
sendFatalFailureToHost("sensorEvent, null data");
}
if (header->sensorHandle != gSensorHandle) {
sendFatalFailureToHost("sensorEvent for wrong handle",
&header->sensorHandle);
}
if (header->readingCount == 0) {
sendFatalFailureToHost("sensorEvent has readingCount of 0");
}
if ((header->reserved[0] != 0) || (header->reserved[1] != 0)) {
sendFatalFailureToHost("sensorEvent has non-zero reserved bytes");
}
markSuccess(kSensorStage);
}
extern "C" void nanoappHandleEvent(uint32_t senderInstanceId,
uint16_t eventType,
const void* eventData) {
if (gInMethod) {
sendFatalFailureToHost("CHRE reentered nanoapp");
}
gInMethod = true;
const uint32_t *intData = static_cast<const uint32_t *>(eventData);
if (senderInstanceId == gInstanceId) {
checkSelfEvent(eventType, intData);
} else if (senderInstanceId == CHRE_INSTANCE_ID) {
if (eventType == CHRE_EVENT_TIMER) {
checkTimerEvent(intData);
} else if (eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_DATA) {
checkSensorEvent(eventData);
} else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE) {
// This could have been generated when we configured the
// sensor. We just ignore it.
} else {
uint32_t e = eventType;
sendFatalFailureToHost("Unexpected event from CHRE:", &e);
}
} else {
sendFatalFailureToHost("Unexpected senderInstanceId",
&senderInstanceId);
}
gInMethod = false;
}
extern "C" bool nanoappStart(void) {
gInMethod = true;
void *ptr = chreHeapAlloc(15);
if (ptr == nullptr) {
// TODO(b/32326854): We're not able to send messages from
// nanoappStart(), so we just use chreLog() here, and make
// the user look through the logs to determine why this failed.
chreLog(CHRE_LOG_ERROR, "Unable to malloc in start");
return false;
}
gInstanceId = chreGetInstanceId();
if (gInstanceId == CHRE_INSTANCE_ID) {
chreLog(CHRE_LOG_ERROR, "Got bad instance ID in start");
return false;
}
// Send an event to ourself.
if (!chreSendEvent(kEventType, &gInstanceId, nullptr, gInstanceId)) {
chreLog(CHRE_LOG_ERROR, "Failed chreSendEvent in start");
return false;
}
// One shot timer that should trigger very quickly.
gTimerId = chreTimerSet(1, &gInstanceId, true);
if (gTimerId == CHRE_TIMER_INVALID) {
chreLog(CHRE_LOG_ERROR, "Failed chreTimerSet in start");
return false;
}
// We don't have a way to confirm the 'free' worked, we'll just look
// to see that we didn't crash. We intentionally move this 'free' to
// be not immediately after the 'alloc', and still before we're done
// calling other methods.
chreHeapFree(ptr);
// Confirm we can find and configure a sensor.
if (!chreSensorFindDefault(CHRE_SENSOR_TYPE_ACCELEROMETER,
&gSensorHandle)) {
chreLog(CHRE_LOG_ERROR, "Failed sensorFindDefault in start");
return false;
}
if (!chreSensorConfigure(gSensorHandle,
CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS,
CHRE_SENSOR_INTERVAL_DEFAULT,
CHRE_SENSOR_LATENCY_ASAP)) {
chreLog(CHRE_LOG_ERROR, "Failed sensorConfigure in start");
return false;
}
// TODO(b/32326854): Confirm we can send a message to the host.
gInMethod = false;
return true;
}
extern "C" void nanoappEnd(void) {
if (!chreSensorConfigureModeOnly(gSensorHandle,
CHRE_SENSOR_CONFIGURE_MODE_DONE)) {
sendFatalFailureToHost("Unable to configure sensor mode to DONE");
}
if (gInMethod) {
// This message won't be noticed by the host; but hopefully the
// fatal failure prevents a clean unload of the app and fails the test.
sendFatalFailureToHost("nanoappEnd called in reentrant manner");
}
}