blob: b00a8c530723843f076c2b95524c66632bf2f85e [file] [log] [blame]
/*
* 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 "chre/core/host_comms_manager.h"
#include <cinttypes>
#include <cstdint>
#include <type_traits>
#include <utility>
#include "chre/core/event_loop_common.h"
#include "chre/core/event_loop_manager.h"
#include "chre/platform/assert.h"
#include "chre/platform/context.h"
#include "chre/platform/host_link.h"
#include "chre/platform/log.h"
#include "chre/target_platform/log.h"
#include "chre/util/duplicate_message_detector.h"
#include "chre/util/macros.h"
#include "chre/util/nested_data_ptr.h"
#include "chre/util/optional.h"
#include "chre_api/chre.h"
namespace chre {
namespace {
/**
* Checks if the message can be send from the nanoapp to the host.
*
* @see sendMessageToHostFromNanoapp for a description of the parameters.
*
* @return Whether the message can be send to the host.
*/
bool shouldAcceptMessageToHostFromNanoapp(Nanoapp *nanoapp, void *messageData,
size_t messageSize,
uint16_t hostEndpoint,
uint32_t messagePermissions,
bool isReliable) {
bool success = false;
if (messageSize > 0 && messageData == nullptr) {
LOGW("Rejecting malformed message (null data but non-zero size)");
} else if (messageSize > chreGetMessageToHostMaxSize()) {
LOGW("Rejecting message of size %zu bytes (max %" PRIu32 ")", messageSize,
chreGetMessageToHostMaxSize());
} else if (hostEndpoint == kHostEndpointUnspecified) {
LOGW("Rejecting message to invalid host endpoint");
} else if (isReliable && hostEndpoint == kHostEndpointBroadcast) {
LOGW("Rejecting reliable message to broadcast endpoint");
} else if (!BITMASK_HAS_VALUE(nanoapp->getAppPermissions(),
messagePermissions)) {
LOGE("Message perms %" PRIx32 " not subset of napp perms %" PRIx32,
messagePermissions, nanoapp->getAppPermissions());
} else {
success = true;
}
return success;
}
} // namespace
HostCommsManager::HostCommsManager()
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
: mDuplicateMessageDetector(kReliableMessageDuplicateDetectorTimeout),
mTransactionManager(
*this,
EventLoopManagerSingleton::get()->getEventLoop().getTimerPool(),
kReliableMessageRetryWaitTime, kReliableMessageMaxAttempts)
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
{
}
// TODO(b/346345637): rename this to align it with the message delivery status
// terminology used elsewhere, and make it return void
bool HostCommsManager::completeTransaction(
[[maybe_unused]] uint32_t transactionId,
[[maybe_unused]] uint8_t errorCode) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
uint32_t txnId = NestedDataPtr<uint32_t>(data);
uint8_t err = NestedDataPtr<uint8_t>(extraData);
EventLoopManagerSingleton::get()
->getHostCommsManager()
.handleMessageDeliveryStatusSync(txnId, err);
};
EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::ReliableMessageEvent,
NestedDataPtr<uint32_t>(transactionId), callback,
NestedDataPtr<uint8_t>(errorCode));
return true;
#else
return false;
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
}
void HostCommsManager::removeAllTransactionsFromNanoapp(
[[maybe_unused]] const Nanoapp &nanoapp) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
struct FindContext {
decltype(mTransactionManager) &transactionManager;
const Nanoapp &nanoapp;
};
// Cancel any pending outbound reliable messages. We leverage find() here as
// a forEach() method by always returning false.
auto transactionRemover = [](HostMessage *msg, void *data) {
FindContext *ctx = static_cast<FindContext *>(data);
if (msg->isReliable && !msg->fromHost &&
msg->appId == ctx->nanoapp.getAppId() &&
!ctx->transactionManager.remove(msg->messageSequenceNumber)) {
LOGE("Couldn't find transaction %" PRIu32 " at flush",
msg->messageSequenceNumber);
}
return false;
};
FindContext context{mTransactionManager, nanoapp};
mMessagePool.find(transactionRemover, &context);
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
}
void HostCommsManager::freeAllReliableMessagesFromNanoapp(
[[maybe_unused]] Nanoapp &nanoapp) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
auto reliableMessageFromNanoappMatcher = [](HostMessage *msg, void *data) {
auto *napp = static_cast<const Nanoapp *>(data);
return (msg->isReliable && !msg->fromHost &&
msg->appId == napp->getAppId());
};
MessageToHost *message;
while ((message = mMessagePool.find(reliableMessageFromNanoappMatcher,
&nanoapp)) != nullptr) {
// We don't post message delivery status to the nanoapp, since it's being
// unloaded and we don't actually know the final message delivery status –
// simply free the memory
onMessageToHostCompleteInternal(message);
}
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
}
void HostCommsManager::flushNanoappMessages(Nanoapp &nanoapp) {
// First we remove all of the outgoing reliable message transactions from the
// transaction manager, which triggers sending any pending reliable messages
removeAllTransactionsFromNanoapp(nanoapp);
// This ensures that HostLink does not reference message memory (owned the
// nanoapp) anymore, i.e. onMessageToHostComplete() is called, which lets us
// free memory for any pending reliable messages
HostLink::flushMessagesSentByNanoapp(nanoapp.getAppId());
freeAllReliableMessagesFromNanoapp(nanoapp);
}
// TODO(b/346345637): rename this to better reflect its true meaning, which is
// that HostLink doesn't reference the memory anymore
void HostCommsManager::onMessageToHostComplete(const MessageToHost *message) {
// We do not call onMessageToHostCompleteInternal for reliable messages
// until the completion callback is called.
if (message != nullptr && !message->isReliable) {
onMessageToHostCompleteInternal(message);
}
}
void HostCommsManager::resetBlameForNanoappHostWakeup() {
mIsNanoappBlamedForWakeup = false;
}
bool HostCommsManager::sendMessageToHostFromNanoapp(
Nanoapp *nanoapp, void *messageData, size_t messageSize,
uint32_t messageType, uint16_t hostEndpoint, uint32_t messagePermissions,
chreMessageFreeFunction *freeCallback, bool isReliable,
const void *cookie) {
if (!shouldAcceptMessageToHostFromNanoapp(nanoapp, messageData, messageSize,
hostEndpoint, messagePermissions,
isReliable)) {
return false;
}
MessageToHost *msgToHost = mMessagePool.allocate();
if (msgToHost == nullptr) {
LOG_OOM();
return false;
}
msgToHost->appId = nanoapp->getAppId();
msgToHost->message.wrap(static_cast<uint8_t *>(messageData), messageSize);
msgToHost->toHostData.hostEndpoint = hostEndpoint;
msgToHost->toHostData.messageType = messageType;
msgToHost->toHostData.messagePermissions = messagePermissions;
msgToHost->toHostData.appPermissions = nanoapp->getAppPermissions();
msgToHost->toHostData.nanoappFreeFunction = freeCallback;
msgToHost->isReliable = isReliable;
msgToHost->cookie = cookie;
msgToHost->fromHost = false;
bool success = false;
if (isReliable) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
success = mTransactionManager.add(nanoapp->getInstanceId(),
&msgToHost->messageSequenceNumber);
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
} else {
success = doSendMessageToHostFromNanoapp(nanoapp, msgToHost);
}
if (!success) {
mMessagePool.deallocate(msgToHost);
}
return success;
}
void HostCommsManager::sendMessageToNanoappFromHost(
uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
const void *messageData, size_t messageSize, bool isReliable,
uint32_t messageSequenceNumber) {
std::pair<chreError, MessageFromHost *> output =
validateAndCraftMessageFromHostToNanoapp(
appId, messageType, hostEndpoint, messageData, messageSize,
isReliable, messageSequenceNumber);
chreError error = output.first;
MessageFromHost *craftedMessage = output.second;
if (error == CHRE_ERROR_NONE) {
auto callback = [](uint16_t /*type*/, void *data, void* /* extraData */) {
MessageFromHost *craftedMessage = static_cast<MessageFromHost *>(data);
EventLoopManagerSingleton::get()
->getHostCommsManager()
.deliverNanoappMessageFromHost(craftedMessage);
};
if (!EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::DeferredMessageToNanoappFromHost,
craftedMessage, callback)) {
LOGE("Failed to defer callback to send message to nanoapp from host");
error = CHRE_ERROR_BUSY;
}
}
if (error != CHRE_ERROR_NONE) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
if (isReliable) {
sendMessageDeliveryStatus(messageSequenceNumber, error);
}
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
if (craftedMessage != nullptr) {
mMessagePool.deallocate(craftedMessage);
}
}
}
MessageFromHost *HostCommsManager::craftNanoappMessageFromHost(
uint64_t appId, uint16_t hostEndpoint, uint32_t messageType,
const void *messageData, uint32_t messageSize, bool isReliable,
uint32_t messageSequenceNumber) {
MessageFromHost *msgFromHost = mMessagePool.allocate();
if (msgFromHost == nullptr) {
LOG_OOM();
} 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);
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;
msgFromHost->isReliable = isReliable;
msgFromHost->messageSequenceNumber = messageSequenceNumber;
msgFromHost->fromHost = true;
}
return msgFromHost;
}
/**
* Checks if the message can be send to the nanoapp from the host. Crafts
* the message to the nanoapp.
*
* @see sendMessageToNanoappFromHost for a description of the parameters.
*
* @return the error code and the crafted message
*/
std::pair<chreError, MessageFromHost *>
HostCommsManager::validateAndCraftMessageFromHostToNanoapp(
uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
const void *messageData, size_t messageSize, bool isReliable,
uint32_t messageSequenceNumber) {
chreError error = CHRE_ERROR_NONE;
MessageFromHost *craftedMessage = nullptr;
if (hostEndpoint == kHostEndpointBroadcast) {
LOGE("Received invalid message from host from broadcast endpoint");
error = CHRE_ERROR_INVALID_ARGUMENT;
} 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);
error = CHRE_ERROR_INVALID_ARGUMENT;
} else {
craftedMessage = craftNanoappMessageFromHost(
appId, hostEndpoint, messageType, messageData,
static_cast<uint32_t>(messageSize), isReliable,
messageSequenceNumber);
if (craftedMessage == nullptr) {
LOGE("Out of memory - rejecting message to app ID 0x%016" PRIx64
"(size %zu)",
appId, messageSize);
error = CHRE_ERROR_NO_MEMORY;
}
}
return std::make_pair(error, craftedMessage);
}
void HostCommsManager::deliverNanoappMessageFromHost(
MessageFromHost *craftedMessage) {
CHRE_ASSERT_LOG(craftedMessage != nullptr,
"Cannot deliver NULL pointer nanoapp message from host");
Optional<chreError> error;
uint16_t targetInstanceId;
bool foundNanoapp = EventLoopManagerSingleton::get()
->getEventLoop()
.findNanoappInstanceIdByAppId(craftedMessage->appId,
&targetInstanceId);
bool shouldDeliverMessage = !craftedMessage->isReliable ||
shouldSendReliableMessageToNanoapp(
craftedMessage->messageSequenceNumber,
craftedMessage->fromHostData.hostEndpoint);
if (!foundNanoapp) {
error = CHRE_ERROR_DESTINATION_NOT_FOUND;
} else if (shouldDeliverMessage) {
EventLoopManagerSingleton::get()->getEventLoop().deliverEventSync(
targetInstanceId, CHRE_EVENT_MESSAGE_FROM_HOST,
&craftedMessage->fromHostData);
error = CHRE_ERROR_NONE;
}
if (craftedMessage->isReliable && error.has_value()) {
handleDuplicateAndSendMessageDeliveryStatus(
craftedMessage->messageSequenceNumber,
craftedMessage->fromHostData.hostEndpoint, error.value());
}
mMessagePool.deallocate(craftedMessage);
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
mDuplicateMessageDetector.removeOldEntries();
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
}
bool HostCommsManager::doSendMessageToHostFromNanoapp(
Nanoapp *nanoapp, MessageToHost *msgToHost) {
bool hostWasAwake = EventLoopManagerSingleton::get()
->getEventLoop()
.getPowerControlManager()
.hostIsAwake();
bool wokeHost = !hostWasAwake && !mIsNanoappBlamedForWakeup;
msgToHost->toHostData.wokeHost = wokeHost;
if (!HostLink::sendMessage(msgToHost)) {
return false;
}
if (wokeHost) {
EventLoopManagerSingleton::get()
->getEventLoop()
.handleNanoappWakeupBuckets();
mIsNanoappBlamedForWakeup = true;
nanoapp->blameHostWakeup();
}
nanoapp->blameHostMessageSent();
return true;
}
MessageToHost *HostCommsManager::findMessageToHostBySeq(
uint32_t messageSequenceNumber) {
return mMessagePool.find(
[](HostMessage *inputMessage, void *data) {
NestedDataPtr<uint32_t> targetMessageSequenceNumber(data);
return inputMessage->isReliable && !inputMessage->fromHost &&
inputMessage->messageSequenceNumber ==
targetMessageSequenceNumber;
},
NestedDataPtr<uint32_t>(messageSequenceNumber));
}
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());
}
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
if (msgToHost->isReliable) {
mTransactionManager.remove(msgToHost->messageSequenceNumber);
}
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
mMessagePool.deallocate(msgToHost);
}
void HostCommsManager::onTransactionAttempt(uint32_t messageSequenceNumber,
uint16_t nanoappInstanceId) {
MessageToHost *message = findMessageToHostBySeq(messageSequenceNumber);
Nanoapp *nanoapp =
EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
nanoappInstanceId);
if (message == nullptr || nanoapp == nullptr) {
LOGE("Attempted to send reliable message %" PRIu32 " from nanoapp %" PRIu16
" but couldn't find:%s%s",
messageSequenceNumber, nanoappInstanceId,
(message == nullptr) ? " msg" : "",
(nanoapp == nullptr) ? " napp" : "");
} else {
bool success = doSendMessageToHostFromNanoapp(nanoapp, message);
LOGD("Attempted to send reliable message %" PRIu32 " from nanoapp %" PRIu16
" with success: %s",
messageSequenceNumber, nanoappInstanceId, success ? "true" : "false");
}
}
void HostCommsManager::onTransactionFailure(uint32_t messageSequenceNumber,
uint16_t nanoappInstanceId) {
LOGE("Reliable message %" PRIu32 " from nanoapp %" PRIu16 " timed out",
messageSequenceNumber, nanoappInstanceId);
handleMessageDeliveryStatusSync(messageSequenceNumber, CHRE_ERROR_TIMEOUT);
}
void HostCommsManager::handleDuplicateAndSendMessageDeliveryStatus(
[[maybe_unused]] uint32_t messageSequenceNumber,
[[maybe_unused]] uint16_t hostEndpoint,
[[maybe_unused]] chreError error) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
bool success = mDuplicateMessageDetector.findAndSetError(
messageSequenceNumber, hostEndpoint, error);
if (!success) {
LOGW("Failed to set error for message with message sequence number: %"
PRIu32 " and host endpoint: 0x%" PRIx16,
messageSequenceNumber,
hostEndpoint);
}
sendMessageDeliveryStatus(messageSequenceNumber, error);
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
}
void HostCommsManager::handleMessageDeliveryStatusSync(
uint32_t messageSequenceNumber, uint8_t errorCode) {
EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop();
uint16_t nanoappInstanceId;
MessageToHost *message = findMessageToHostBySeq(messageSequenceNumber);
if (message == nullptr) {
LOGW("Got message delivery status for unexpected seq %" PRIu32,
messageSequenceNumber);
} else if (!eventLoop.findNanoappInstanceIdByAppId(message->appId,
&nanoappInstanceId)) {
// Expected if we unloaded the nanoapp while a message was in flight
LOGW("Got message delivery status seq %" PRIu32
" but couldn't find nanoapp 0x%" PRIx64,
messageSequenceNumber, message->appId);
} else {
chreAsyncResult asyncResult = {};
asyncResult.success = errorCode == CHRE_ERROR_NONE;
asyncResult.errorCode = errorCode;
asyncResult.cookie = message->cookie;
onMessageToHostCompleteInternal(message);
eventLoop.deliverEventSync(
nanoappInstanceId, CHRE_EVENT_RELIABLE_MSG_ASYNC_RESULT, &asyncResult);
}
}
void HostCommsManager::onMessageToHostCompleteInternal(
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);
// TODO(b/346345637): add an assertion that HostLink does not own the memory,
// which is technically possible if a reliable message timed out before it
// was released
// 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 if (inEventLoopThread()) {
// If we're already within the event loop context, it is safe to call the
// free callback synchronously.
freeMessageToHost(msgToHost);
} else {
auto freeMsgCallback = [](uint16_t /*type*/, void *data,
void * /*extraData*/) {
EventLoopManagerSingleton::get()->getHostCommsManager().freeMessageToHost(
static_cast<MessageToHost *>(data));
};
if (!EventLoopManagerSingleton::get()->deferCallback(
SystemCallbackType::MessageToHostComplete, msgToHost,
freeMsgCallback)) {
freeMessageToHost(static_cast<MessageToHost *>(msgToHost));
}
}
}
bool HostCommsManager::shouldSendReliableMessageToNanoapp(
[[maybe_unused]] uint32_t messageSequenceNumber,
[[maybe_unused]] uint16_t hostEndpoint) {
#ifdef CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
bool isDuplicate;
Optional<chreError> pastError = mDuplicateMessageDetector.findOrAdd(
messageSequenceNumber, hostEndpoint, &isDuplicate);
if (isDuplicate) {
bool isTransientFailure =
pastError.has_value() && (pastError.value() == CHRE_ERROR_BUSY ||
pastError.value() == CHRE_ERROR_TRANSIENT);
LOGW("Duplicate message with message sequence number: %" PRIu32
" and host endpoint: 0x%" PRIx16 " was detected. %s",
messageSequenceNumber, hostEndpoint,
isTransientFailure ? "Retrying." : "Not sending message to nanoapp.");
if (!isTransientFailure) {
if (pastError.has_value()) {
sendMessageDeliveryStatus(messageSequenceNumber, pastError.value());
}
return false;
}
}
#endif // CHRE_RELIABLE_MESSAGE_SUPPORT_ENABLED
return true;
}
} // namespace chre