blob: a605a403d4e4551ad9c65611be01fd6ef3261cf4 [file] [log] [blame]
/*
* Copyright (C) 2020 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/host_link.h"
#include "chre/core/event_loop_manager.h"
#include "chre/core/host_comms_manager.h"
#include "chre/core/settings.h"
#include "chre/platform/aoc/system_time.h"
#include "chre/platform/shared/host_protocol_chre.h"
#include "chre/platform/shared/nanoapp_load_manager.h"
#include "chre/util/fixed_size_blocking_queue.h"
#include "chre/util/flatbuffers/helpers.h"
#include "chre_api/chre/version.h"
namespace chre {
Nanoseconds HostLinkBase::mLastTimeSyncRequestNanos(0);
namespace {
//! Used to pass the client ID through the user data pointer in deferCallback
union HostClientIdCallbackData {
uint16_t hostClientId;
void *ptr;
};
static_assert(sizeof(uint16_t) <= sizeof(void *),
"Pointer must at least fit a u16 for passing the host client ID");
struct LoadNanoappCallbackData {
uint64_t appId;
uint32_t transactionId;
uint16_t hostClientId;
UniquePtr<Nanoapp> nanoapp;
uint32_t fragmentId;
};
struct UnloadNanoappCallbackData {
uint64_t appId;
uint32_t transactionId;
uint16_t hostClientId;
bool allowSystemNanoappUnload;
};
/** Helper function used to retrieve the HostLink from a static context. */
inline HostLink &getHostLink() {
return EventLoopManagerSingleton::get()->getHostCommsManager().getHostLink();
}
void setTimeSyncRequestTimer(Nanoseconds delay) {
static TimerHandle sHandle;
static bool sHandleInitialized;
if (sHandleInitialized) {
EventLoopManagerSingleton::get()->cancelDelayedCallback(sHandle);
}
auto callback = [](uint16_t /* eventType */, void * /* data */) {
HostLinkBase::sendTimeSyncRequest();
};
sHandle = EventLoopManagerSingleton::get()->setDelayedCallback(
SystemCallbackType::TimerSyncRequest, nullptr /* data */, callback,
delay);
sHandleInitialized = true;
}
bool getSettingFromFbs(fbs::Setting setting, Setting *chreSetting) {
bool success = true;
switch (setting) {
case fbs::Setting::LOCATION:
*chreSetting = Setting::LOCATION;
break;
default:
LOGE("Unknown setting %" PRIu8, setting);
success = false;
}
return success;
}
bool getSettingStateFromFbs(fbs::SettingState state,
SettingState *chreSettingState) {
bool success = true;
switch (state) {
case fbs::SettingState::DISABLED:
*chreSettingState = SettingState::DISABLED;
break;
case fbs::SettingState::ENABLED:
*chreSettingState = SettingState::ENABLED;
break;
default:
LOGE("Unknown state %" PRIu8, state);
success = false;
}
return success;
}
void sendDebugDumpData(uint16_t hostClientId, const char *debugStr,
size_t debugStrSize) {
constexpr size_t kFixedSizePortion = 52;
ChreFlatBufferBuilder builder(kFixedSizePortion + debugStrSize);
HostProtocolChre::encodeDebugDumpData(builder, hostClientId, debugStr,
debugStrSize);
getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
}
void sendDebugDumpResponse(uint16_t hostClientId, bool success,
uint32_t dataCount) {
constexpr size_t kFixedSizePortion = 52;
ChreFlatBufferBuilder builder(kFixedSizePortion);
HostProtocolChre::encodeDebugDumpResponse(builder, hostClientId, success,
dataCount);
getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
}
void constructNanoappListCallback(uint16_t /*eventType*/, void *deferCbData) {
HostClientIdCallbackData clientIdCbData;
clientIdCbData.ptr = deferCbData;
struct NanoappListData {
ChreFlatBufferBuilder *builder;
DynamicVector<NanoappListEntryOffset> nanoappEntries;
};
NanoappListData cbData;
EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
size_t expectedNanoappCount = eventLoop.getNanoappCount();
if (!cbData.nanoappEntries.reserve(expectedNanoappCount)) {
LOG_OOM();
} else {
constexpr size_t kFixedOverhead = 48;
constexpr size_t kPerNanoappSize = 32;
size_t initialBufferSize =
(kFixedOverhead + expectedNanoappCount * kPerNanoappSize);
ChreFlatBufferBuilder builder(initialBufferSize);
cbData.builder = &builder;
auto nanoappAdderCallback = [](const Nanoapp *nanoapp, void *data) {
auto *listData = static_cast<NanoappListData *>(data);
HostProtocolChre::addNanoappListEntry(
*(listData->builder), listData->nanoappEntries, nanoapp->getAppId(),
nanoapp->getAppVersion(), true /*enabled*/,
nanoapp->isSystemNanoapp());
};
eventLoop.forEachNanoapp(nanoappAdderCallback, &cbData);
HostProtocolChre::finishNanoappListResponse(builder, cbData.nanoappEntries,
clientIdCbData.hostClientId);
getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
}
}
void sendFragmentResponse(uint16_t hostClientId, uint32_t transactionId,
uint32_t fragmentId, bool success) {
constexpr size_t kInitialBufferSize = 52;
ChreFlatBufferBuilder builder(kInitialBufferSize);
HostProtocolChre::encodeLoadNanoappResponse(
builder, hostClientId, transactionId, success, fragmentId);
if (!getHostLink().send(builder.GetBufferPointer(), builder.GetSize())) {
LOGE(
"Failed to send fragment response for HostClientID: %x FragmentID: %x"
"transactionID: 0x%x",
hostClientId, fragmentId, transactionId);
}
}
void finishLoadingNanoappCallback(uint16_t /*eventType*/, void *data) {
UniquePtr<LoadNanoappCallbackData> cbData(
static_cast<LoadNanoappCallbackData *>(data));
constexpr size_t kInitialBufferSize = 48;
ChreFlatBufferBuilder builder(kInitialBufferSize);
CHRE_ASSERT(cbData != nullptr);
LOGD("Finished loading nanoapp cb on fragment ID: %u", cbData->fragmentId);
EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
bool success =
cbData->nanoapp->isLoaded() && eventLoop.startNanoapp(cbData->nanoapp);
sendFragmentResponse(cbData->hostClientId, cbData->transactionId,
cbData->fragmentId, success);
}
void handleUnloadNanoappCallback(uint16_t /*eventType*/, void *data) {
auto *cbData = static_cast<UnloadNanoappCallbackData *>(data);
bool success = false;
uint32_t instanceId;
EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
if (!eventLoop.findNanoappInstanceIdByAppId(cbData->appId, &instanceId)) {
LOGE("Couldn't unload app ID 0x%016" PRIx64 ": not found", cbData->appId);
} else {
success =
eventLoop.unloadNanoapp(instanceId, cbData->allowSystemNanoappUnload);
}
constexpr size_t kInitialBufferSize = 52;
ChreFlatBufferBuilder builder(kInitialBufferSize);
HostProtocolChre::encodeUnloadNanoappResponse(builder, cbData->hostClientId,
cbData->transactionId, success);
if (!getHostLink().send(builder.GetBufferPointer(), builder.GetSize())) {
LOGE("Failed to send unload response to host: %x transactionID: 0x%x",
cbData->hostClientId, cbData->transactionId);
}
memoryFree(data);
}
} // anonymous namespace
void sendDebugDumpResultToHost(uint16_t hostClientId, const char *debugStr,
size_t debugStrSize, bool complete,
uint32_t dataCount) {
if (debugStrSize > 0) {
sendDebugDumpData(hostClientId, debugStr, debugStrSize);
}
if (complete) {
sendDebugDumpResponse(hostClientId, true /*success*/, dataCount);
}
}
void HostLink::flushMessagesSentByNanoapp(uint64_t /* appId */) {
// TODO: Implement this once USF supports this (b/149318001)
}
bool HostLink::sendMessage(const MessageToHost *message) {
constexpr size_t kFixedReserveSize = 80;
ChreFlatBufferBuilder builder(message->message.size() + kFixedReserveSize);
HostProtocolChre::encodeNanoappMessage(
builder, message->appId, message->toHostData.messageType,
message->toHostData.hostEndpoint, message->message.data(),
message->message.size());
return getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
}
void HostLink::sendLogMessage(const char *logMessage, size_t logMessageSize) {
constexpr size_t kInitialSize = 128;
ChreFlatBufferBuilder builder(logMessageSize + kInitialSize);
HostProtocolChre::encodeLogMessages(builder, logMessage, logMessageSize);
getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
}
HostLinkBase::HostLinkBase() {
usf::UsfErr err = usf::UsfTransportMgr::SetMsgHandler(
usf::UsfMsgType::UsfMsgType_CHRE, handleUsfMessage, nullptr);
if (err == usf::kErrNone) {
err = usf::UsfWorkMgr::CreateWorker(&mWorker);
}
if (err != usf::kErrNone) {
FATAL_ERROR("Failed to initialize CHRE transport");
}
}
HostLinkBase::~HostLinkBase() {
mWorker->Stop();
mWorker.reset();
}
void HostLinkBase::init(usf::UsfTransport *transport) {
if (mTransportHandle == nullptr) {
if (transport == nullptr) {
FATAL_ERROR("Null transport at init, cannot send out messages!");
}
mTransportHandle = transport;
}
}
bool HostLinkBase::send(uint8_t *data, size_t dataLen) {
usf::UsfErr err = usf::kErrFailed;
if (mTransportHandle != nullptr) {
usf::UsfTxMsg msg;
msg.SetMsgType(usf::UsfMsgType_CHRE);
msg.SetData(data, dataLen);
err = mTransportHandle->SendMsg(&msg);
}
return (usf::kErrNone == err);
}
usf::UsfErr HostLinkBase::handleUsfMessage(usf::UsfTransport *transport,
const usf::UsfMsg *msg,
void * /* arg */) {
usf::UsfErr rc = usf::kErrFailed;
getHostLink().init(transport);
if (msg == nullptr) {
rc = usf::kErrInvalid;
} else {
usf::UsfMsgType msgType = msg->msg_type();
if (msgType == usf::UsfMsgType_CHRE) {
auto *dataVector = msg->data();
if (dataVector != nullptr) {
size_t dataLen = dataVector->size();
struct MessageData {
uint64_t dataLen;
void *data;
};
// handleUsfMessage isn't single-threaded. Make a copy and process
// on a worker thread to ensure CHRE only processes incoming messages
// on a single thread. CHRE's thread isn't used since several messages
// can be processed without posting to it.
//
// TODO(b/163592230): Improve USF messaging interface to remove extra
// data copies and keep work single-threaded.
MessageData data;
data.dataLen = dataLen;
data.data = memoryAlloc(dataLen);
if (data.data == nullptr) {
LOG_OOM();
rc = usf::kErrAllocation;
} else {
memcpy(data.data, dataVector->data(), dataLen);
auto callback = [](void *data) {
MessageData *dataInfo = static_cast<MessageData *>(data);
HostProtocolChre::decodeMessageFromHost(dataInfo->data,
dataInfo->dataLen);
memoryFree(dataInfo->data);
// USF owns the passed in data pointer and will free it after the
// callback returns.
};
rc =
getHostLink().getWorker()->Enqueue(callback, &data, sizeof(data));
}
}
} else {
rc = usf::kErrInvalid;
}
}
// Opportunistically send a time sync message (1 hour period threshold)
constexpr Seconds kOpportunisticTimeSyncPeriod = Seconds(60 * 60 * 1);
if (SystemTime::getMonotonicTime() >
mLastTimeSyncRequestNanos + kOpportunisticTimeSyncPeriod) {
sendTimeSyncRequest();
}
return rc;
}
void HostLinkBase::sendTimeSyncRequest() {
constexpr size_t kInitialSize = 52;
ChreFlatBufferBuilder builder(kInitialSize);
HostProtocolChre::encodeTimeSyncRequest(builder);
getHostLink().send(builder.GetBufferPointer(), builder.GetSize());
mLastTimeSyncRequestNanos = SystemTime::getMonotonicTime();
}
void HostMessageHandlers::handleDebugDumpRequest(uint16_t hostClientId) {
chre::EventLoopManagerSingleton::get()
->getDebugDumpManager()
.onDebugDumpRequested(hostClientId);
}
void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) {
constexpr size_t kInitialBufferSize = 192;
constexpr char kHubName[] = "CHRE on AoC";
constexpr char kVendor[] = "Google";
constexpr char kToolchain[] =
"Clang " STRINGIFY(__clang_major__) "." STRINGIFY(
__clang_minor__) "." STRINGIFY(__clang_patchlevel__) ")";
constexpr uint32_t kLegacyPlatformVersion = 0;
constexpr uint32_t kLegacyToolchainVersion =
((__clang_major__ & 0xFF) << 24) | ((__clang_minor__ & 0xFF) << 16) |
(__clang_patchlevel__ & 0xFFFF);
constexpr float kPeakMips = 800;
// TODO: The following need to be updated when we've measured the
// indicated power levels on the AoC
constexpr float kStoppedPower = 0;
constexpr float kSleepPower = 0;
constexpr float kPeakPower = 0;
ChreFlatBufferBuilder builder(kInitialBufferSize);
HostProtocolChre::encodeHubInfoResponse(
builder, kHubName, kVendor, kToolchain, kLegacyPlatformVersion,
kLegacyToolchainVersion, kPeakMips, kStoppedPower, kSleepPower,
kPeakPower, CHRE_MESSAGE_TO_HOST_MAX_SIZE, chreGetPlatformId(),
chreGetVersion(), hostClientId);
if (!getHostLink().send(builder.GetBufferPointer(), builder.GetSize())) {
LOGE("Failed to send Hub Info Response message");
}
}
void HostMessageHandlers::handleLoadNanoappRequest(
uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
uint32_t appVersion, uint32_t targetApiVersion, const void *buffer,
size_t bufferLen, const char *appFileName, uint32_t fragmentId,
size_t appBinaryLen) {
bool success = true;
static NanoappLoadManager sLoadManager;
if (fragmentId == 0 || fragmentId == 1) {
size_t totalAppBinaryLen = (fragmentId == 0) ? bufferLen : appBinaryLen;
LOGD("Load nanoapp request for app ID 0x%016" PRIx64 " ver 0x%" PRIx32
" target API 0x%08" PRIx32 " size %zu (txnId %" PRIu32
" client %" PRIu16 ")",
appId, appVersion, targetApiVersion, totalAppBinaryLen, transactionId,
hostClientId);
if (sLoadManager.hasPendingLoadTransaction()) {
FragmentedLoadInfo info = sLoadManager.getTransactionInfo();
sendFragmentResponse(info.hostClientId, info.transactionId,
0 /* fragmentId */, false /* success */);
sLoadManager.markFailure();
}
success = sLoadManager.prepareForLoad(hostClientId, transactionId, appId,
appVersion, totalAppBinaryLen);
}
if (success) {
success = sLoadManager.copyNanoappFragment(
hostClientId, transactionId, (fragmentId == 0) ? 1 : fragmentId, buffer,
bufferLen);
} else {
LOGE("Failed to prepare for load");
}
if (sLoadManager.isLoadComplete()) {
LOGD("Load manager load complete...");
auto cbData = MakeUnique<LoadNanoappCallbackData>();
if (cbData.isNull()) {
LOG_OOM();
} else {
cbData->transactionId = transactionId;
cbData->hostClientId = hostClientId;
cbData->appId = appId;
cbData->fragmentId = fragmentId;
cbData->nanoapp = sLoadManager.releaseNanoapp();
// Note that if this fails, we'll generate the error response in
// the normal deferred callback
EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::FinishLoadingNanoapp, cbData.release(),
finishLoadingNanoappCallback);
}
} else {
// send a response for this fragment
sendFragmentResponse(hostClientId, transactionId, fragmentId, success);
}
}
void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) {
LOGD("Nanoapp list request from client ID %" PRIu16, hostClientId);
HostClientIdCallbackData cbData = {};
cbData.hostClientId = hostClientId;
EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::NanoappListResponse, cbData.ptr,
constructNanoappListCallback);
}
void HostMessageHandlers::handleNanoappMessage(uint64_t appId,
uint32_t messageType,
uint16_t hostEndpoint,
const void *messageData,
size_t messageDataLen) {
LOGD("Parsed nanoapp message from host: app ID 0x%016" PRIx64
" endpoint 0x%" PRIx16 " msgType %" PRIu32 " payload size %zu",
appId, hostEndpoint, messageType, messageDataLen);
HostCommsManager &hostCommsManager =
EventLoopManagerSingleton::get()->getHostCommsManager();
hostCommsManager.sendMessageToNanoappFromHost(
appId, messageType, hostEndpoint, messageData, messageDataLen);
}
void HostMessageHandlers::handleSettingChangeMessage(fbs::Setting setting,
fbs::SettingState state) {
Setting chreSetting;
SettingState chreSettingState;
if (getSettingFromFbs(setting, &chreSetting) &&
getSettingStateFromFbs(state, &chreSettingState)) {
postSettingChange(chreSetting, chreSettingState);
}
}
void HostMessageHandlers::handleTimeSyncMessage(int64_t offset) {
setEstimatedHostTimeOffset(offset);
// Schedule a time sync request since offset may drift
constexpr Seconds kClockDriftTimeSyncPeriod =
Seconds(60 * 60 * 6); // 6 hours
setTimeSyncRequestTimer(kClockDriftTimeSyncPeriod);
}
void HostMessageHandlers::handleUnloadNanoappRequest(
uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
bool allowSystemNanoappUnload) {
LOGD("Unload nanoapp request (txnID %" PRIu32 ") for appId 0x%016" PRIx64
" system %d",
transactionId, appId, allowSystemNanoappUnload);
auto *cbData = memoryAlloc<UnloadNanoappCallbackData>();
if (cbData == nullptr) {
LOG_OOM();
} else {
cbData->appId = appId;
cbData->transactionId = transactionId;
cbData->hostClientId = hostClientId;
cbData->allowSystemNanoappUnload = allowSystemNanoappUnload;
EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::HandleUnloadNanoapp, cbData,
handleUnloadNanoappCallback);
}
}
} // namespace chre