/*
* Copyright (C) 2016 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 <shared/send_message.h>
#include <inttypes.h>
#include <shared/abort.h>
#include <shared/dumb_allocator.h>
#include <shared/nano_endian.h>
#include <shared/nano_string.h>
#include <chre.h>
namespace nanoapp_testing {
constexpr size_t kAllocSize = 128;
static DumbAllocator<kAllocSize, 4> gDumbAlloc;
static void freeDumbAllocMessage(void *message, size_t messageSize) {
if (messageSize > kAllocSize) {
uint32_t localSize = uint32_t(messageSize);
sendFatalFailureToHost("freeDumbAllocMessage given oversized message:",
&localSize);
}
if (!gDumbAlloc.free(message)) {
uint32_t localPtr =
reinterpret_cast<size_t>(message) & UINT32_C(0xFFFFFFFF);
sendFatalFailureToHost("freeDumbAllocMessage given bad pointer:",
&localPtr);
}
}
static void freeHeapMessage(void *message, size_t /* messageSize */) {
if (gDumbAlloc.contains(message)) {
uint32_t localPtr =
reinterpret_cast<size_t>(message) & UINT32_C(0xFFFFFFFF);
sendFatalFailureToHost("freeHeapMessage given DumbAlloc pointer:",
&localPtr);
}
chreHeapFree(message);
}
static void fatalError() {
// Attempt to send a context-less failure message, in the hopes that
// might get through.
chreSendMessageToHost(nullptr, 0,
static_cast<uint32_t>(MessageType::kFailure),
nullptr);
// Whether or not that made it through, unambigiously fail this test
// by aborting.
nanoapp_testing::abort();
}
// TODO(b/32114261): Remove this method.
static bool needToPrependMessageType() {
// TODO: When we have a new API that properly send the messageType,
// this method should get the API version and return appropriately.
// Eventually we should remove this hacky method.
return true;
}
static void *getMessageMemory(size_t *size, bool *dumbAlloc) {
if (needToPrependMessageType()) {
*size += sizeof(uint32_t);
}
void *ret = gDumbAlloc.alloc(*size);
if (ret != nullptr) {
*dumbAlloc = true;
} else {
// Not expected, but possible if the CHRE is lagging in freeing
// these messages, or if we're sending a huge message.
*dumbAlloc = false;
ret = chreHeapAlloc(static_cast<uint32_t>(*size));
if (ret == nullptr) {
fatalError();
}
}
return ret;
}
// TODO(b/32114261): Remove this method.
static void *prependMessageType(MessageType messageType, void *memory) {
if (!needToPrependMessageType()) {
return memory;
}
uint32_t type = nanoapp_testing::hostToLittleEndian(
static_cast<uint32_t>(messageType));
memcpy(memory, &type, sizeof(type));
uint8_t *ptr = static_cast<uint8_t*>(memory);
ptr += sizeof(type);
return ptr;
}
static void internalSendMessage(MessageType messageType, void *data,
size_t dataSize, bool dumbAlloc) {
// Note that if the CHRE implementation occasionally drops a message
// here, then tests will become flaky. For now, we consider that to
// be a flaky CHRE implementation which should fail testing.
if (!chreSendMessageToHostEndpoint(data, dataSize,
static_cast<uint32_t>(messageType),
CHRE_HOST_ENDPOINT_BROADCAST,
dumbAlloc ? freeDumbAllocMessage :
freeHeapMessage)) {
fatalError();
}
}
void sendMessageToHost(MessageType messageType, const void *data,
size_t dataSize) {
if ((dataSize == 0) && (data != nullptr)) {
sendInternalFailureToHost("Bad sendMessageToHost args");
}
bool dumbAlloc = true;
size_t fullMessageSize = dataSize;
void *myMessageBase = getMessageMemory(&fullMessageSize, &dumbAlloc);
void *ptr = prependMessageType(messageType, myMessageBase);
memcpy(ptr, data, dataSize);
internalSendMessage(messageType, myMessageBase, fullMessageSize, dumbAlloc);
}
void sendStringToHost(MessageType messageType, const char *message,
const uint32_t *value) {
if (message == nullptr) {
sendInternalFailureToHost("sendStringToHost 'message' is NULL");
}
bool dumbAlloc = true;
const size_t messageStrlen = strlen(message);
size_t myMessageLen = messageStrlen;
if (value != nullptr) {
myMessageLen += kUint32ToHexAsciiBufferMinLen;
}
// Add null terminator
myMessageLen++;
size_t fullMessageLen = myMessageLen;
char *fullMessage =
static_cast<char*>(getMessageMemory(&fullMessageLen, &dumbAlloc));
char *ptr = static_cast<char*>(prependMessageType(messageType,
fullMessage));
memcpy(ptr, message, messageStrlen);
ptr += messageStrlen;
if (value != nullptr) {
uint32ToHexAscii(
ptr, fullMessageLen - static_cast<size_t>(ptr - fullMessage), *value);
}
// Add the terminator.
fullMessage[fullMessageLen - 1] = '\0';
internalSendMessage(messageType, fullMessage, fullMessageLen, dumbAlloc);
}
// Before we abort the nanoapp, we also put this message in the chreLog().
// We have no assurance our message will make it to the Host (not required
// for CHRE implementations), but this will at least make sure our message
// hits the log.
static void logFatalMessage(const char *message, const uint32_t *value) {
if (value != nullptr) {
chreLog(CHRE_LOG_ERROR, "TEST ABORT: %s0x%08" PRIX32, message, *value);
} else {
chreLog(CHRE_LOG_ERROR, "TEST ABORT: %s", message);
}
}
void sendFatalFailureToHost(const char *message, const uint32_t *value,
AbortBlame reason) {
sendFailureToHost(message, value);
logFatalMessage(message, value);
nanoapp_testing::abort(reason);
}
void sendInternalFailureToHost(const char *message, const uint32_t *value,
AbortBlame reason) {
sendStringToHost(MessageType::kInternalFailure, message, value);
logFatalMessage(message, value);
nanoapp_testing::abort(reason);
}
} // namespace nanoapp_testing