/*
* 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 <cinttypes>
#include <type_traits>
#include "chre/core/event_loop_manager.h"
#include "chre/core/host_comms_manager.h"
#include "chre/platform/assert.h"
#include "chre/platform/context.h"
#include "chre/platform/host_link.h"
namespace chre {
constexpr uint32_t kMessageToHostReservedFieldValue = UINT32_MAX;
bool HostCommsManager::sendMessageToHostFromCurrentNanoapp(
void *messageData, size_t messageSize, uint32_t messageType,
uint16_t hostEndpoint, chreMessageFreeFunction *freeCallback) {
EventLoop *eventLoop = chre::getCurrentEventLoop();
CHRE_ASSERT(eventLoop);
Nanoapp *currentApp = eventLoop->getCurrentNanoapp();
CHRE_ASSERT(currentApp);
bool success = false;
if (messageSize > 0 && messageData == nullptr) {
LOGW("Rejecting malformed message (null data but non-zero size)");
} else if (messageSize > CHRE_MESSAGE_TO_HOST_MAX_SIZE) {
LOGW("Rejecting message of size %zu bytes (max %d)",
messageSize, CHRE_MESSAGE_TO_HOST_MAX_SIZE);
} else if (hostEndpoint == kHostEndpointUnspecified) {
LOGW("Rejecting message to invalid host endpoint");
} else {
MessageToHost *msgToHost = mMessagePool.allocate();
if (msgToHost == nullptr) {
LOGE("Couldn't allocate message to host");
} else {
msgToHost->appId = currentApp->getAppId();
msgToHost->message.wrap(static_cast<uint8_t *>(messageData), messageSize);
msgToHost->toHostData.hostEndpoint = hostEndpoint;
msgToHost->toHostData.messageType = messageType;
msgToHost->toHostData.nanoappFreeFunction = freeCallback;
// Populate a special value to help disambiguate message direction when
// debugging
msgToHost->toHostData.reserved = kMessageToHostReservedFieldValue;
success = mHostLink.sendMessage(msgToHost);
if (!success) {
freeMessageToHost(msgToHost);
}
}
}
return success;
}
void HostCommsManager::deliverNanoappMessageFromHost(
uint64_t appId, uint16_t hostEndpoint, uint32_t messageType,
const void *messageData, uint32_t messageSize, EventLoop *targetEventLoop,
uint32_t targetInstanceId) {
CHRE_ASSERT(targetEventLoop != nullptr);
bool success = false;
MessageFromHost *msgFromHost = mMessagePool.allocate();
if (msgFromHost == nullptr) {
LOGE("Couldn't allocate message from host");
} else if (!msgFromHost->message.copy_array(
static_cast<const uint8_t *>(messageData), messageSize)) {
LOGE("Couldn't allocate %" PRIu32 " bytes for message data from host "
"(endpoint 0x%" PRIx16 " type %" PRIu32 ")", messageSize,
hostEndpoint, messageType);
} else {
msgFromHost->appId = appId;
msgFromHost->fromHostData.messageType = messageType;
msgFromHost->fromHostData.messageSize = static_cast<uint32_t>(
messageSize);
msgFromHost->fromHostData.message = msgFromHost->message.data();
msgFromHost->fromHostData.hostEndpoint = hostEndpoint;
success = targetEventLoop->postEvent(
CHRE_EVENT_MESSAGE_FROM_HOST, &msgFromHost->fromHostData,
freeMessageFromHostCallback, kSystemInstanceId, targetInstanceId);
}
if (!success && msgFromHost != nullptr) {
mMessagePool.deallocate(msgFromHost);
}
}
void HostCommsManager::sendMessageToNanoappFromHost(
uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
const void *messageData, size_t messageSize) {
EventLoopManager *eventLoopMgr = EventLoopManagerSingleton::get();
EventLoop *targetEventLoop;
uint32_t targetInstanceId;
if (hostEndpoint == kHostEndpointBroadcast) {
LOGE("Received invalid message from host from broadcast endpoint");
} else if (messageSize > ((UINT32_MAX))) {
// The current CHRE API uses uint32_t to represent the message size in
// struct chreMessageFromHostData. We don't expect to ever need to exceed
// this, but the check ensures we're on the up and up.
LOGE("Rejecting message of size %zu (too big)", messageSize);
} else if (!eventLoopMgr->findNanoappInstanceIdByAppId(appId,
&targetInstanceId,
&targetEventLoop)) {
LOGE("Dropping message; destination app ID 0x%016" PRIx64 " not found",
appId);
} else {
deliverNanoappMessageFromHost(appId, hostEndpoint, messageType, messageData,
static_cast<uint32_t>(messageSize),
targetEventLoop, targetInstanceId);
}
}
void HostCommsManager::onMessageToHostComplete(const MessageToHost *message) {
// Removing const on message since we own the memory and will deallocate it;
// the caller (HostLink) only gets a const pointer
auto *msgToHost = const_cast<MessageToHost *>(message);
// If there's no free callback, we can free the message right away as the
// message pool is thread-safe; otherwise, we need to do it from within the
// EventLoop context.
if (msgToHost->toHostData.nanoappFreeFunction == nullptr) {
mMessagePool.deallocate(msgToHost);
} else {
auto freeMsgCallback = [](uint16_t /*type*/, void *data) {
EventLoopManagerSingleton::get()->getHostCommsManager().freeMessageToHost(
static_cast<MessageToHost *>(data));
};
bool eventPosted = EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::MessageToHostComplete, msgToHost, freeMsgCallback);
// If this assert/log triggers, we're leaking resources
// TODO: should have reserved space in event queue to prevent nanoapps from
// negatively impacting system functionality
CHRE_ASSERT_LOG(eventPosted, "Couldn't defer callback to clean up message "
"to host!");
}
}
void HostCommsManager::freeMessageToHost(MessageToHost *msgToHost) {
if (msgToHost->toHostData.nanoappFreeFunction != nullptr) {
msgToHost->toHostData.nanoappFreeFunction(msgToHost->message.data(),
msgToHost->message.size());
}
mMessagePool.deallocate(msgToHost);
}
void HostCommsManager::freeMessageFromHostCallback(uint16_t /*type*/,
void *data) {
// We pass the chreMessageFromHostData structure to the nanoapp as the event's
// data pointer, but we need to return to the enclosing HostMessage pointer.
// As long as HostMessage is standard-layout, and fromHostData is the first
// field, we can convert between these two pointers via reinterpret_cast.
// These static assertions ensure this assumption is held.
static_assert(std::is_standard_layout<HostMessage>::value,
"HostMessage* is derived from HostMessage::fromHostData*, "
"therefore it must be standard layout");
static_assert(offsetof(MessageFromHost, fromHostData) == 0,
"fromHostData must be the first field in HostMessage");
auto *eventData = static_cast<chreMessageFromHostData *>(data);
auto *msgFromHost = reinterpret_cast<MessageFromHost *>(eventData);
auto& hostCommsMgr = EventLoopManagerSingleton::get()->getHostCommsManager();
hostCommsMgr.mMessagePool.deallocate(msgFromHost);
}
} // namespace chre