普通文本  |  203行  |  6.59 KB

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