/* * 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 #include #include "chre/core/event_loop_manager.h" #include "chre/core/host_comms_manager.h" #include "chre/platform/assert.h" #include "chre/platform/host_link.h" #include "chre/util/macros.h" namespace chre { bool HostCommsManager::sendMessageToHostFromNanoapp( Nanoapp *nanoapp, void *messageData, size_t messageSize, uint32_t messageType, uint16_t hostEndpoint, uint32_t messagePermissions, chreMessageFreeFunction *freeCallback) { 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 if (!BITMASK_HAS_VALUE(nanoapp->getAppPermissions(), messagePermissions)) { LOGE("Message perms %" PRIx32 " not subset of napp perms %" PRIx32, messagePermissions, nanoapp->getAppPermissions()); } else { MessageToHost *msgToHost = mMessagePool.allocate(); if (msgToHost == nullptr) { LOG_OOM(); } else { msgToHost->appId = nanoapp->getAppId(); msgToHost->message.wrap(static_cast(messageData), messageSize); msgToHost->toHostData.hostEndpoint = hostEndpoint; msgToHost->toHostData.messageType = messageType; msgToHost->toHostData.messagePermissions = messagePermissions; msgToHost->toHostData.appPermissions = nanoapp->getAppPermissions(); msgToHost->toHostData.nanoappFreeFunction = freeCallback; // Let the nanoapp know that it woke up the host and record it bool hostWasAwake = EventLoopManagerSingleton::get() ->getEventLoop() .getPowerControlManager() .hostIsAwake(); success = HostLink::sendMessage(msgToHost); if (!success) { mMessagePool.deallocate(msgToHost); } else if (!hostWasAwake && !mIsNanoappBlamedForWakeup) { // If message successfully sent and host was suspended before sending EventLoopManagerSingleton::get() ->getEventLoop() .handleNanoappWakeupBuckets(); mIsNanoappBlamedForWakeup = true; nanoapp->blameHostWakeup(); } } } return success; } MessageFromHost *HostCommsManager::craftNanoappMessageFromHost( uint64_t appId, uint16_t hostEndpoint, uint32_t messageType, const void *messageData, uint32_t messageSize) { MessageFromHost *msgFromHost = mMessagePool.allocate(); if (msgFromHost == nullptr) { LOG_OOM(); } else if (!msgFromHost->message.copy_array( static_cast(messageData), messageSize)) { LOGE("Couldn't allocate %" PRIu32 " bytes for message data from host " "(endpoint 0x%" PRIx16 " type %" PRIu32 ")", messageSize, hostEndpoint, messageType); mMessagePool.deallocate(msgFromHost); msgFromHost = nullptr; } else { msgFromHost->appId = appId; msgFromHost->fromHostData.messageType = messageType; msgFromHost->fromHostData.messageSize = messageSize; msgFromHost->fromHostData.message = msgFromHost->message.data(); msgFromHost->fromHostData.hostEndpoint = hostEndpoint; } return msgFromHost; } bool HostCommsManager::deliverNanoappMessageFromHost( MessageFromHost *craftedMessage) { const EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); uint32_t targetInstanceId; bool nanoappFound = false; CHRE_ASSERT_LOG(craftedMessage != nullptr, "Cannot deliver NULL pointer nanoapp message from host"); if (eventLoop.findNanoappInstanceIdByAppId(craftedMessage->appId, &targetInstanceId)) { nanoappFound = true; EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie( CHRE_EVENT_MESSAGE_FROM_HOST, &craftedMessage->fromHostData, freeMessageFromHostCallback, targetInstanceId); } return nanoappFound; } void HostCommsManager::sendMessageToNanoappFromHost(uint64_t appId, uint32_t messageType, uint16_t hostEndpoint, const void *messageData, size_t messageSize) { 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 { MessageFromHost *craftedMessage = craftNanoappMessageFromHost( appId, hostEndpoint, messageType, messageData, static_cast(messageSize)); if (craftedMessage == nullptr) { LOGE("Out of memory - rejecting message to app ID 0x%016" PRIx64 "(size %zu)", appId, messageSize); } else if (!deliverNanoappMessageFromHost(craftedMessage)) { LOGV("Deferring message; destination app ID 0x%016" PRIx64 " not found at this time", appId); auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { EventLoopManagerSingleton::get() ->getHostCommsManager() .sendDeferredMessageToNanoappFromHost( static_cast(data)); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::DeferredMessageToNanoappFromHost, craftedMessage, callback); } } } void HostCommsManager::sendDeferredMessageToNanoappFromHost( MessageFromHost *craftedMessage) { CHRE_ASSERT_LOG(craftedMessage != nullptr, "Deferred message from host is a NULL pointer"); if (!deliverNanoappMessageFromHost(craftedMessage)) { LOGE("Dropping deferred message; destination app ID 0x%016" PRIx64 " still not found", craftedMessage->appId); mMessagePool.deallocate(craftedMessage); } else { LOGD("Deferred message to app ID 0x%016" PRIx64 " delivered", craftedMessage->appId); } } void HostCommsManager::resetBlameForNanoappHostWakeup() { mIsNanoappBlamedForWakeup = false; } 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(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, void * /*extraData*/) { EventLoopManagerSingleton::get()->getHostCommsManager().freeMessageToHost( static_cast(data)); }; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::MessageToHostComplete, msgToHost, freeMsgCallback); } } void HostCommsManager::freeMessageToHost(MessageToHost *msgToHost) { if (msgToHost->toHostData.nanoappFreeFunction != nullptr) { EventLoopManagerSingleton::get()->getEventLoop().invokeMessageFreeFunction( msgToHost->appId, 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::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(data); auto *msgFromHost = reinterpret_cast(eventData); auto &hostCommsMgr = EventLoopManagerSingleton::get()->getHostCommsManager(); hostCommsMgr.mMessagePool.deallocate(msgFromHost); } } // namespace chre