blob: 3ac54df8349bb6184841c6622f5367a411462008 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "chre/platform/platform_nanoapp.h"
#include "chre/core/event_loop_manager.h"
#include "chre/platform/assert.h"
#include "chre/platform/log.h"
#include "chre/platform/memory.h"
#include "chre/platform/shared/nanoapp_dso_util.h"
#include "chre/platform/shared/nanoapp_support_lib_dso.h"
#include "chre/platform/slpi/memory.h"
#include "chre/platform/slpi/power_control_util.h"
#include "chre/util/system/debug_dump.h"
#include "chre_api/chre/version.h"
#include "dlfcn.h"
#include <inttypes.h>
#include <string.h>
namespace chre {
#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) && \
defined(CHRE_SENSORS_SUPPORT_ENABLED)
namespace {
void rewriteToChreEventType(uint16_t *eventType) {
CHRE_ASSERT(eventType);
// HACK: as SEE does not support software batching in uimg via
// QCM/uQSockets, we rewrite requests for accel and uncal accel/gyro/mag
// from big image nanoapps to respective vendor types in
// chreSensorFindDefault(), which is implemented as sensor data routed
// through CM/QMI and supports batching. Rewrite sensor data arriving
// on this event type to the vanilla sensor 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);
constexpr uint16_t kUncalAccelBigImageEventType =
(CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_VENDOR_START + 6);
constexpr uint16_t kUncalGyroBigImageEventType =
(CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_VENDOR_START + 7);
constexpr uint16_t kUncalMagBigImageEventType =
(CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_VENDOR_START + 8);
constexpr uint16_t kLightBigImageEventType =
(CHRE_EVENT_SENSOR_DATA_EVENT_BASE + CHRE_SENSOR_TYPE_VENDOR_START + 9);
if (*eventType == kAccelBigImageEventType) {
*eventType = CHRE_EVENT_SENSOR_ACCELEROMETER_DATA;
} else if (*eventType == kUncalAccelBigImageEventType) {
*eventType = CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA;
} else if (*eventType == kUncalGyroBigImageEventType) {
*eventType = CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA;
} else if (*eventType == kUncalMagBigImageEventType) {
*eventType = CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA;
} else if (*eventType == kLightBigImageEventType) {
*eventType = CHRE_EVENT_SENSOR_LIGHT_DATA;
}
}
/**
* Helper function to get the sensor type of a big-image variant of a sensor.
*
* @param sensorType The sensor type to convert from.
*
* @return The sensor type of the corresponding big-image sensor, or the input
* sensor type if one does not exist.
*/
uint8_t getBigImageSensorType(uint8_t sensorType) {
switch (sensorType) {
case CHRE_SENSOR_TYPE_ACCELEROMETER:
return CHRE_SLPI_SENSOR_TYPE_BIG_IMAGE_ACCEL;
case CHRE_SENSOR_TYPE_UNCALIBRATED_ACCELEROMETER:
return CHRE_SLPI_SENSOR_TYPE_BIG_IMAGE_UNCAL_ACCEL;
case CHRE_SENSOR_TYPE_UNCALIBRATED_GYROSCOPE:
return CHRE_SLPI_SENSOR_TYPE_BIG_IMAGE_UNCAL_GYRO;
case CHRE_SENSOR_TYPE_UNCALIBRATED_GEOMAGNETIC_FIELD:
return CHRE_SLPI_SENSOR_TYPE_BIG_IMAGE_UNCAL_MAG;
case CHRE_SENSOR_TYPE_LIGHT:
return CHRE_SLPI_SENSOR_TYPE_BIG_IMAGE_LIGHT;
default:
return sensorType;
}
}
/**
* Helper function to get the handle of a big-image variant of a sensor.
*
* @param sensorHandle The sensor handle to convert from.
*
* @return The handle of the corresponding big-image sensor, or the input sensor
* handle if one does not exist.
*/
uint32_t getBigImageSensorHandle(uint32_t sensorHandle) {
Sensor *sensor =
EventLoopManagerSingleton::get()->getSensorRequestManager().getSensor(
sensorHandle);
uint8_t bigImageType = getBigImageSensorType(sensor->getSensorType());
uint32_t bigImageHandle;
EventLoopManagerSingleton::get()->getSensorRequestManager().getSensorHandle(
bigImageType, &bigImageHandle);
return bigImageHandle;
}
/**
* @return true if the given event type is a bias info event.
*/
bool isBiasEventType(uint16_t eventType) {
return eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO ||
eventType == CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_BIAS_INFO ||
eventType == CHRE_EVENT_SENSOR_GYROSCOPE_BIAS_INFO ||
eventType == CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_BIAS_INFO ||
eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO ||
eventType ==
CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_BIAS_INFO;
}
} // anonymous namespace
#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) &&
// defined(CHRE_SENSORS_SUPPORT_ENABLED)
PlatformNanoapp::~PlatformNanoapp() {
closeNanoapp();
if (mAppBinary != nullptr) {
memoryFreeBigImage(mAppBinary);
}
}
bool PlatformNanoapp::start() {
// Invoke the start entry point after successfully opening the app
if (!isUimgApp()) {
slpiForceBigImage();
}
return openNanoapp() && mAppInfo->entryPoints.start();
}
void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
const void *eventData) {
if (!isUimgApp()) {
slpiForceBigImage();
#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) && \
defined(CHRE_SENSORS_SUPPORT_ENABLED)
rewriteToChreEventType(&eventType);
#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) &&
// defined(CHRE_SENSORS_SUPPORT_ENABLED)
}
#if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) && \
defined(CHRE_SENSORS_SUPPORT_ENABLED)
// NOTE: Since SeeCalHelper does not internally differentiate calibration
// between big/micro image, convert the sensor handle to the appropriate
// one when delivering a bias info event to the nanoapp.
chreSensorThreeAxisData bias;
if (eventData != nullptr && !isUimgApp() && isBiasEventType(eventType)) {
bias = *static_cast<const chreSensorThreeAxisData *>(eventData);
bias.header.sensorHandle =
getBigImageSensorHandle(bias.header.sensorHandle);
eventData = &bias;
}
#endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) &&
// defined(CHRE_SENSORS_SUPPORT_ENABLED)
mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
}
void PlatformNanoapp::end() {
if (!isUimgApp()) {
slpiForceBigImage();
}
mAppInfo->entryPoints.end();
closeNanoapp();
}
bool PlatformNanoappBase::setAppInfo(uint64_t appId, uint32_t appVersion,
const char *appFilename) {
CHRE_ASSERT(!isLoaded());
mExpectedAppId = appId;
mExpectedAppVersion = appVersion;
size_t appFilenameLen = strlen(appFilename) + 1;
mAppFilename = static_cast<char *>(memoryAllocBigImage(appFilenameLen));
bool success = false;
if (mAppFilename == nullptr) {
LOG_OOM();
} else {
memcpy(static_cast<void *>(mAppFilename), appFilename, appFilenameLen);
success = true;
}
return success;
}
bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
uint32_t /* appFlags */,
size_t appBinaryLen) {
CHRE_ASSERT(!isLoaded());
bool success = false;
constexpr size_t kMaxAppSize = 2 * 1024 * 1024; // 2 MiB
if (appBinaryLen > kMaxAppSize) {
LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
} else {
mAppBinary = memoryAllocBigImage(appBinaryLen);
if (mAppBinary == nullptr) {
LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
appBinaryLen, appId);
} else {
mExpectedAppId = appId;
mExpectedAppVersion = appVersion;
mAppBinaryLen = appBinaryLen;
success = true;
}
}
return success;
}
bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
size_t bufferLen) {
CHRE_ASSERT(!isLoaded());
bool success = true;
if (mBytesLoaded + bufferLen > mAppBinaryLen) {
LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
bufferLen, mBytesLoaded, mAppBinaryLen);
success = false;
} else {
uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
memcpy(binaryBuffer, buffer, bufferLen);
mBytesLoaded += bufferLen;
}
return success;
}
void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
CHRE_ASSERT(!isLoaded());
mIsStatic = true;
mAppInfo = appInfo;
}
bool PlatformNanoappBase::isLoaded() const {
return (mIsStatic ||
(mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
mDsoHandle != nullptr || mAppFilename != nullptr);
}
bool PlatformNanoappBase::isUimgApp() const {
return mIsUimgApp;
}
void PlatformNanoappBase::closeNanoapp() {
if (mDsoHandle != nullptr) {
mAppInfo = nullptr;
if (dlclose(mDsoHandle) != 0) {
LOGE("dlclose failed: %s", dlerror());
}
mDsoHandle = nullptr;
}
}
bool PlatformNanoappBase::openNanoapp() {
bool success = false;
if (mIsStatic) {
success = true;
} else if (mAppBinary != nullptr) {
success = openNanoappFromBuffer();
} else if (mAppFilename != nullptr) {
success = openNanoappFromFile();
} else {
CHRE_ASSERT(false);
}
// Ensure any allocated memory hanging around is properly cleaned up.
if (!success) {
closeNanoapp();
}
// Save this flag locally since it may be referenced while the system is in
// micro-image
if (mAppInfo != nullptr) {
mIsUimgApp = mAppInfo->isTcmNanoapp;
}
return success;
}
bool PlatformNanoappBase::openNanoappFromBuffer() {
CHRE_ASSERT(mAppBinary != nullptr);
CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
// Populate a filename string (just a requirement of the dlopenbuf API)
constexpr size_t kMaxFilenameLen = 17;
char filename[kMaxFilenameLen];
snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
mDsoHandle = dlopenbuf(filename, static_cast<const char *>(mAppBinary),
static_cast<int>(mAppBinaryLen), RTLD_NOW);
memoryFreeBigImage(mAppBinary);
mAppBinary = nullptr;
return verifyNanoappInfo();
}
bool PlatformNanoappBase::openNanoappFromFile() {
CHRE_ASSERT(mAppFilename != nullptr);
CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
mDsoHandle = dlopen(mAppFilename, RTLD_NOW);
memoryFreeBigImage(mAppFilename);
mAppFilename = nullptr;
return verifyNanoappInfo();
}
bool PlatformNanoappBase::verifyNanoappInfo() {
bool success = false;
if (mDsoHandle == nullptr) {
LOGE("No nanoapp info to verify: %s", dlerror());
} else {
mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
if (mAppInfo == nullptr) {
LOGE("Failed to find app info symbol: %s", dlerror());
} else {
success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
if (!success) {
mAppInfo = nullptr;
} else {
LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64
") version 0x%" PRIx32 " (%s) uimg %d system %d",
mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
getAppVersionString(), mAppInfo->isTcmNanoapp,
mAppInfo->isSystemNanoapp);
}
}
}
return success;
}
const char *PlatformNanoappBase::getAppVersionString() const {
const char *versionString = "<undefined>";
if (mAppInfo != nullptr && mAppInfo->structMinorVersion >= 2 &&
mAppInfo->appVersionString != NULL) {
size_t appVersionStringLength = strlen(mAppInfo->appVersionString);
size_t offset = 0;
for (size_t i = 0; i < appVersionStringLength; i++) {
size_t newOffset = i + 1;
if (mAppInfo->appVersionString[i] == '@' &&
newOffset < appVersionStringLength) {
offset = newOffset;
break;
}
}
versionString = &mAppInfo->appVersionString[offset];
}
return versionString;
}
uint64_t PlatformNanoapp::getAppId() const {
return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
}
uint32_t PlatformNanoapp::getAppVersion() const {
return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
}
uint32_t PlatformNanoapp::getTargetApiVersion() const {
return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
}
const char *PlatformNanoapp::getAppName() const {
return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
}
bool PlatformNanoapp::isSystemNanoapp() const {
// Right now, we assume that system nanoapps are always static nanoapps. Since
// mAppInfo can only be null either prior to loading the app (in which case
// this function is not expected to return a valid value anyway), or when a
// dynamic nanoapp is not running, "false" is the correct return value in that
// case.
return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
}
void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
if (mAppInfo != nullptr) {
debugDump.print("%s (%s) @ %s", mAppInfo->name, mAppInfo->vendor,
getAppVersionString());
}
}
} // namespace chre