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