/* * 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) 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); 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; } } /** * 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. */ SensorType getBigImageSensorType(SensorType sensorType) { switch (sensorType) { case SensorType::Accelerometer: return SensorType::VendorType3; case SensorType::UncalibratedAccelerometer: return SensorType::VendorType6; case SensorType::UncalibratedGyroscope: return SensorType::VendorType7; case SensorType::UncalibratedGeomagneticField: return SensorType::VendorType8; 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) { SensorType sensorType = getSensorTypeFromSensorHandle(sensorHandle); sensorType = getBigImageSensorType(sensorType); return getSensorHandleFromSensorType(sensorType); } /** * @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) 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) rewriteToChreEventType(&eventType); #endif // defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_ENABLED) } #if defined(CHRE_SLPI_SEE) && defined(CHRE_SLPI_UIMG_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) 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, 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; } 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(char *buffer, size_t *bufferPos, size_t bufferSize) const { if (mAppInfo != nullptr) { debugDumpPrint(buffer, bufferPos, bufferSize, " %s: vendor=\"%s\" commit=\"%s\"", mAppInfo->name, mAppInfo->vendor, getAppVersionString()); } } } // namespace chre