| /* |
| * 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/platform/assert.h" |
| #include "chre/platform/log.h" |
| #include "chre/platform/memory.h" |
| #include "chre/platform/shared/nanoapp_support_lib_dso.h" |
| #include "chre_api/chre/version.h" |
| |
| #include "dlfcn.h" |
| |
| #include <inttypes.h> |
| #include <string.h> |
| |
| namespace chre { |
| |
| namespace { |
| |
| /** |
| * Performs sanity checks on the app info structure included in a dynamically |
| * loaded nanoapp. |
| * |
| * @param expectedAppId |
| * @param expectedAppVersion |
| * @param appInfo |
| * |
| * @return true if validation was successful |
| */ |
| bool validateAppInfo(uint64_t expectedAppId, uint32_t expectedAppVersion, |
| const struct chreNslNanoappInfo *appInfo) { |
| uint32_t ourApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION(chreGetApiVersion()); |
| uint32_t targetApiMajorVersion = CHRE_EXTRACT_MAJOR_VERSION( |
| appInfo->targetApiVersion); |
| |
| bool success = false; |
| if (appInfo->magic != CHRE_NSL_NANOAPP_INFO_MAGIC) { |
| LOGE("Invalid app info magic: got 0x%08" PRIx32 " expected 0x%08" PRIx32, |
| appInfo->magic, CHRE_NSL_NANOAPP_INFO_MAGIC); |
| } else if (appInfo->appId == 0) { |
| LOGE("Rejecting invalid app ID 0"); |
| } else if (expectedAppId != appInfo->appId) { |
| LOGE("Expected app ID (0x%016" PRIx64 ") doesn't match internal one (0x%016" |
| PRIx64 ")", expectedAppId, appInfo->appId); |
| } else if (expectedAppVersion != appInfo->appVersion) { |
| LOGE("Expected app version (0x%" PRIx32 ") doesn't match internal one (0x%" |
| PRIx32 ")", expectedAppVersion, appInfo->appVersion); |
| } else if (targetApiMajorVersion != ourApiMajorVersion) { |
| LOGE("App targets a different major API version (%" PRIu32 ") than what we " |
| "provide (%" PRIu32 ")", targetApiMajorVersion, ourApiMajorVersion); |
| } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) { |
| LOGE("App name is too long"); |
| } else if (strlen(appInfo->name) > CHRE_NSL_DSO_NANOAPP_STRING_MAX_LEN) { |
| LOGE("App vendor is too long"); |
| } else { |
| success = true; |
| } |
| |
| return success; |
| } |
| |
| } // anonymous namespace |
| |
| PlatformNanoapp::~PlatformNanoapp() { |
| closeNanoapp(); |
| if (mAppBinary != nullptr) { |
| memoryFree(mAppBinary); |
| } |
| } |
| |
| bool PlatformNanoapp::start() { |
| // Invoke the start entry point after successfully opening the app |
| return (mIsStatic || openNanoapp()) ? mAppInfo->entryPoints.start() : false; |
| } |
| |
| void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, |
| uint16_t eventType, |
| const void *eventData) { |
| mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData); |
| } |
| |
| void PlatformNanoapp::end() { |
| mAppInfo->entryPoints.end(); |
| closeNanoapp(); |
| } |
| |
| bool PlatformNanoappBase::loadFromBuffer(uint64_t appId, uint32_t appVersion, |
| const void *appBinary, |
| 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 = memoryAlloc(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; |
| memcpy(mAppBinary, appBinary, appBinaryLen); |
| success = true; |
| } |
| } |
| |
| return success; |
| } |
| |
| void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) { |
| CHRE_ASSERT(!isLoaded()); |
| mIsStatic = true; |
| mAppInfo = appInfo; |
| } |
| |
| bool PlatformNanoappBase::isLoaded() const { |
| return (mIsStatic || mAppBinary != nullptr); |
| } |
| |
| void PlatformNanoappBase::closeNanoapp() { |
| if (mDsoHandle != nullptr) { |
| if (dlclose(mDsoHandle) != 0) { |
| const char *name = (mAppInfo != nullptr) ? mAppInfo->name : "unknown"; |
| LOGE("dlclose of %s failed: %s", name, dlerror()); |
| } |
| mDsoHandle = nullptr; |
| } |
| } |
| |
| bool PlatformNanoappBase::openNanoapp() { |
| bool success = false; |
| |
| // 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); |
| |
| CHRE_ASSERT(mAppBinary != nullptr); |
| CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp"); |
| mDsoHandle = dlopenbuf( |
| filename, static_cast<const char *>(mAppBinary), |
| static_cast<int>(mAppBinaryLen), RTLD_NOW); |
| if (mDsoHandle == nullptr) { |
| LOGE("Failed to load nanoapp: %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, mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion); |
| } |
| } |
| } |
| |
| return success; |
| } |
| |
| 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; |
| } |
| |
| } // namespace chre |