| /* |
| * 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 |