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