You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
381 lines
14 KiB
381 lines
14 KiB
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "request_manager.h"
|
|
|
|
#include "chre/util/macros.h"
|
|
#include "chre/util/nanoapp/audio.h"
|
|
#include "chre/util/nested_data_ptr.h"
|
|
#include "generated/chre_power_test_generated.h"
|
|
|
|
namespace chre {
|
|
namespace {
|
|
|
|
//! List of all sensor types that can be interacted with from the nanoapp.
|
|
constexpr uint8_t kAllSensorTypes[] = {
|
|
CHRE_SENSOR_TYPE_ACCELEROMETER,
|
|
CHRE_SENSOR_TYPE_GYROSCOPE,
|
|
CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE,
|
|
CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD,
|
|
CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD,
|
|
CHRE_SENSOR_TYPE_PRESSURE,
|
|
CHRE_SENSOR_TYPE_LIGHT,
|
|
CHRE_SENSOR_TYPE_PROXIMITY,
|
|
CHRE_SENSOR_TYPE_STEP_DETECT,
|
|
CHRE_SENSOR_TYPE_STEP_COUNTER,
|
|
CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER,
|
|
CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE,
|
|
CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE,
|
|
CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE,
|
|
CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT,
|
|
CHRE_SENSOR_TYPE_STATIONARY_DETECT,
|
|
};
|
|
|
|
/**
|
|
* Retrieve the configure mode for the given sensor type.
|
|
*
|
|
* @param sensorType The type of the sensor
|
|
* @return The sensor configure mode for the given sensor type
|
|
*/
|
|
chreSensorConfigureMode getModeForSensorType(uint8_t sensorType) {
|
|
chreSensorConfigureMode mode;
|
|
switch (sensorType) {
|
|
case CHRE_SENSOR_TYPE_ACCELEROMETER:
|
|
case CHRE_SENSOR_TYPE_GYROSCOPE:
|
|
case CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE:
|
|
case CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD:
|
|
case CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD:
|
|
case CHRE_SENSOR_TYPE_PRESSURE:
|
|
case CHRE_SENSOR_TYPE_LIGHT:
|
|
case CHRE_SENSOR_TYPE_PROXIMITY:
|
|
case CHRE_SENSOR_TYPE_STEP_DETECT:
|
|
case CHRE_SENSOR_TYPE_STEP_COUNTER:
|
|
case CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER:
|
|
case CHRE_SENSOR_TYPE_ACCELEROMETER_TEMPERATURE:
|
|
case CHRE_SENSOR_TYPE_GYROSCOPE_TEMPERATURE:
|
|
case CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD_TEMPERATURE:
|
|
mode = CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS;
|
|
break;
|
|
case CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT:
|
|
case CHRE_SENSOR_TYPE_STATIONARY_DETECT:
|
|
mode = CHRE_SENSOR_CONFIGURE_MODE_ONE_SHOT;
|
|
break;
|
|
default:
|
|
LOGE("Mode requested for unhandled sensor type %" PRIu8
|
|
" defaulting to continuous",
|
|
sensorType);
|
|
mode = CHRE_SENSOR_CONFIGURE_MODE_CONTINUOUS;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
/**
|
|
* Verifies a given message from the host is a valid message to the nanoapp.
|
|
*
|
|
* @param hostMessage message being delivered from the host
|
|
* @param verifiedMessage if verification is successful, contains the decoded
|
|
* message from the host. Otherwise, is uninitialized.
|
|
* @return true if the message was verified to be a valid.
|
|
*/
|
|
template <class MessageClass>
|
|
bool verifyMessage(const chreMessageFromHostData &hostMessage,
|
|
const MessageClass **verifiedMessage) {
|
|
flatbuffers::Verifier verifier(
|
|
static_cast<const uint8_t *>(hostMessage.message),
|
|
hostMessage.messageSize);
|
|
bool verified = verifier.VerifyBuffer<MessageClass>(nullptr);
|
|
if (verified) {
|
|
*verifiedMessage = flatbuffers::GetRoot<MessageClass>(hostMessage.message);
|
|
} else {
|
|
LOGE("Failed to verify %s message from host",
|
|
power_test::EnumNameMessageType(
|
|
static_cast<power_test::MessageType>(hostMessage.messageType)));
|
|
}
|
|
return verified;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
using power_test::AudioRequestMessage;
|
|
using power_test::BreakItMessage;
|
|
using power_test::CellQueryMessage;
|
|
using power_test::GnssLocationMessage;
|
|
using power_test::GnssMeasurementMessage;
|
|
using power_test::MessageType;
|
|
using power_test::NanoappResponseMessage;
|
|
using power_test::SensorRequestMessage;
|
|
using power_test::TimerMessage;
|
|
using power_test::WifiScanMessage;
|
|
|
|
bool RequestManager::requestTimer(bool enable, TimerType type,
|
|
Nanoseconds delay) {
|
|
bool success = false;
|
|
if (enable) {
|
|
// Stop previous request if active.
|
|
chreTimerCancel(mTimerIds[type]);
|
|
mTimerIds[type] = CHRE_TIMER_INVALID;
|
|
|
|
// Set a timer for the new request.
|
|
NestedDataPtr<TimerType> timerType(type);
|
|
uint32_t timerId =
|
|
chreTimerSet(delay.toRawNanoseconds(), timerType, false /* oneShot */);
|
|
if (timerId != CHRE_TIMER_INVALID) {
|
|
success = true;
|
|
mTimerIds[type] = timerId;
|
|
}
|
|
} else {
|
|
success = chreTimerCancel(mTimerIds[type]);
|
|
mTimerIds[type] = CHRE_TIMER_INVALID;
|
|
}
|
|
LOGI("RequestTimer success %d, enable %d, type %d, delay %" PRIu64, success,
|
|
enable, type, delay.toRawNanoseconds());
|
|
return success;
|
|
}
|
|
|
|
void RequestManager::wifiTimerCallback() const {
|
|
struct chreWifiScanParams params = {};
|
|
params.scanType = mWifiScanType;
|
|
params.radioChainPref = mWifiRadioChain;
|
|
params.channelSet = mWifiChannelSet;
|
|
bool success = chreWifiRequestScanAsync(¶ms, nullptr /*cookie*/);
|
|
LOGI("Requested WiFi - success %d, scanType %" PRIu8 " radioChain %" PRIu8
|
|
" channelSet %" PRIu8,
|
|
success, params.scanType, params.radioChainPref, params.channelSet);
|
|
}
|
|
|
|
bool RequestManager::requestGnssLocation(
|
|
bool enable, uint32_t scanIntervalMillis,
|
|
uint32_t minTimeToNextFixMillis) const {
|
|
bool success;
|
|
if (enable) {
|
|
success = chreGnssLocationSessionStartAsync(
|
|
scanIntervalMillis, minTimeToNextFixMillis, nullptr /* cookie */);
|
|
} else {
|
|
success = chreGnssLocationSessionStopAsync(nullptr /* cookie */);
|
|
}
|
|
LOGI("RequestGnss success %d, enable %d, scanIntervalMillis %" PRIu32
|
|
" minTimeToNextFixMillis %" PRIu32,
|
|
success, enable, scanIntervalMillis, minTimeToNextFixMillis);
|
|
return success;
|
|
}
|
|
|
|
bool RequestManager::requestGnssMeasurement(bool enable,
|
|
uint32_t intervalMillis) const {
|
|
bool success;
|
|
if (enable) {
|
|
success = chreGnssMeasurementSessionStartAsync(intervalMillis,
|
|
nullptr /* cookie */);
|
|
} else {
|
|
success = chreGnssMeasurementSessionStopAsync(nullptr /* cookie */);
|
|
}
|
|
LOGI("RequestGnssMeasurement success %d, enable %d, intervalMillis %" PRIu32,
|
|
success, enable, intervalMillis);
|
|
return success;
|
|
}
|
|
|
|
void RequestManager::cellTimerCallback() const {
|
|
bool success = chreWwanGetCellInfoAsync(nullptr /* cookie */);
|
|
LOGI("Requested Cell - success %d", success);
|
|
}
|
|
|
|
bool RequestManager::requestAudio(bool enable,
|
|
uint64_t bufferDurationNs) const {
|
|
bool success;
|
|
|
|
if (enable) {
|
|
// Only request audio data from the first source
|
|
// TODO: Request audio data from all available sources (or allow configuring
|
|
// which source to sample from)
|
|
success = chreAudioConfigureSource(0 /* handle */, true /* enable */,
|
|
bufferDurationNs, bufferDurationNs);
|
|
} else {
|
|
success = chreAudioConfigureSource(0 /* handle */, false /* enable */,
|
|
0 /* bufferDuration */,
|
|
0 /* deliveryInterval */);
|
|
}
|
|
LOGI("RequestAudio success %d, enable %d, bufferDurationNs %" PRIu64, success,
|
|
enable, bufferDurationNs);
|
|
return success;
|
|
}
|
|
|
|
bool RequestManager::requestSensor(bool enable, uint8_t sensorType,
|
|
uint64_t samplingIntervalNs,
|
|
uint64_t latencyNs) const {
|
|
uint32_t sensorHandle;
|
|
bool success = chreSensorFindDefault(sensorType, &sensorHandle);
|
|
|
|
if (success) {
|
|
if (enable) {
|
|
success =
|
|
chreSensorConfigure(sensorHandle, getModeForSensorType(sensorType),
|
|
samplingIntervalNs, latencyNs);
|
|
} else {
|
|
success = chreSensorConfigureModeOnly(sensorHandle,
|
|
CHRE_SENSOR_CONFIGURE_MODE_DONE);
|
|
}
|
|
}
|
|
|
|
LOGI("RequestSensor success %d, enable %d, sensorType %" PRIu8
|
|
" samplingIntervalNs %" PRIu64 " latencyNs %" PRIu64,
|
|
success, enable, sensorType, samplingIntervalNs, latencyNs);
|
|
return success;
|
|
}
|
|
|
|
bool RequestManager::requestAllSensors(bool enable) const {
|
|
bool success = true;
|
|
uint32_t sensorHandle;
|
|
struct chreSensorInfo sensorInfo;
|
|
for (uint8_t i = 0; i < ARRAY_SIZE(kAllSensorTypes); i++) {
|
|
success &= chreSensorFindDefault(kAllSensorTypes[i], &sensorHandle) &&
|
|
chreGetSensorInfo(sensorHandle, &sensorInfo) &&
|
|
requestSensor(enable, kAllSensorTypes[i], sensorInfo.minInterval,
|
|
CHRE_SENSOR_LATENCY_ASAP);
|
|
}
|
|
|
|
LOGI("requestAllSensors success %d enable %d", success, enable);
|
|
return success;
|
|
}
|
|
|
|
bool RequestManager::requestAudioAtFastestRate(bool enable) const {
|
|
struct chreAudioSource audioSource;
|
|
bool success = chreAudioGetSource(0 /* handle */, &audioSource);
|
|
if (success) {
|
|
LOGI("Found audio source '%s' with %" PRIu32 "Hz %s data", audioSource.name,
|
|
audioSource.sampleRate,
|
|
chre::getChreAudioFormatString(audioSource.format));
|
|
LOGI(" buffer duration: [%" PRIu64 "ns, %" PRIu64 "ns]",
|
|
audioSource.minBufferDuration, audioSource.maxBufferDuration);
|
|
success &= requestAudio(enable, audioSource.minBufferDuration);
|
|
}
|
|
|
|
LOGI("requestAudioAtFastestRate success %d enable %d", success, enable);
|
|
return success;
|
|
}
|
|
|
|
bool RequestManager::requestBreakIt(bool enable) {
|
|
bool success = requestTimer(enable, TimerType::WIFI, Seconds(1));
|
|
success &= requestGnssLocation(enable, chre::kOneSecondInNanoseconds,
|
|
0 /* minTimeToNextFixMillis */);
|
|
success &= requestTimer(enable, TimerType::CELL, Seconds(1));
|
|
success &= requestAudioAtFastestRate(enable);
|
|
success &= requestAllSensors(enable);
|
|
LOGI("RequestBreakIt success %d enable %d", success, enable);
|
|
return success;
|
|
}
|
|
|
|
void RequestManager::handleTimerEvent(const void *cookie) const {
|
|
if (cookie != nullptr) {
|
|
NestedDataPtr<TimerType> timerType(const_cast<void *>(cookie));
|
|
switch (timerType.data) {
|
|
case TimerType::WAKEUP:
|
|
LOGI("Received a wakeup timer event");
|
|
break;
|
|
case TimerType::WIFI:
|
|
wifiTimerCallback();
|
|
break;
|
|
case TimerType::CELL:
|
|
cellTimerCallback();
|
|
break;
|
|
default:
|
|
LOGE("Invalid timer type received %d", timerType.data);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RequestManager::handleMessageFromHost(
|
|
const chreMessageFromHostData &hostMessage) {
|
|
bool success = false;
|
|
if (hostMessage.message == nullptr) {
|
|
LOGE("Host message from %" PRIu16 " has empty message",
|
|
hostMessage.hostEndpoint);
|
|
} else {
|
|
switch (static_cast<MessageType>(hostMessage.messageType)) {
|
|
case MessageType::TIMER_TEST: {
|
|
const TimerMessage *msg;
|
|
if (verifyMessage<TimerMessage>(hostMessage, &msg)) {
|
|
success = requestTimer(msg->enable(), TimerType::WAKEUP,
|
|
Nanoseconds(msg->wakeup_interval_ns()));
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::WIFI_SCAN_TEST: {
|
|
const WifiScanMessage *msg;
|
|
if (verifyMessage<WifiScanMessage>(hostMessage, &msg)) {
|
|
mWifiScanType = static_cast<uint8_t>(msg->scan_type());
|
|
mWifiRadioChain = static_cast<uint8_t>(msg->radio_chain());
|
|
mWifiChannelSet = static_cast<uint8_t>(msg->channel_set());
|
|
success = requestTimer(msg->enable(), TimerType::WIFI,
|
|
Nanoseconds(msg->scan_interval_ns()));
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::GNSS_LOCATION_TEST: {
|
|
const GnssLocationMessage *msg;
|
|
if (verifyMessage<GnssLocationMessage>(hostMessage, &msg)) {
|
|
success =
|
|
requestGnssLocation(msg->enable(), msg->scan_interval_millis(),
|
|
msg->min_time_to_next_fix_millis());
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::CELL_QUERY_TEST: {
|
|
const CellQueryMessage *msg;
|
|
if (verifyMessage<CellQueryMessage>(hostMessage, &msg)) {
|
|
success = requestTimer(msg->enable(), TimerType::CELL,
|
|
Nanoseconds(msg->query_interval_ns()));
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::AUDIO_REQUEST_TEST: {
|
|
const AudioRequestMessage *msg;
|
|
if (verifyMessage<AudioRequestMessage>(hostMessage, &msg)) {
|
|
success = requestAudio(msg->enable(), msg->buffer_duration_ns());
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::SENSOR_REQUEST_TEST: {
|
|
const SensorRequestMessage *msg;
|
|
if (verifyMessage<SensorRequestMessage>(hostMessage, &msg)) {
|
|
success =
|
|
requestSensor(msg->enable(), static_cast<uint8_t>(msg->sensor()),
|
|
msg->sampling_interval_ns(), msg->latency_ns());
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::BREAK_IT_TEST: {
|
|
const BreakItMessage *msg;
|
|
if (verifyMessage<BreakItMessage>(hostMessage, &msg)) {
|
|
success = requestBreakIt(msg->enable());
|
|
}
|
|
break;
|
|
}
|
|
case MessageType::GNSS_MEASUREMENT_TEST: {
|
|
const GnssMeasurementMessage *msg;
|
|
if (verifyMessage<GnssMeasurementMessage>(hostMessage, &msg)) {
|
|
success =
|
|
requestGnssMeasurement(msg->enable(), msg->min_interval_millis());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LOGE("Received unknown host message %" PRIu32, hostMessage.messageType);
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
} // namespace chre
|