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