/* * 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. */ #ifndef FARF_MEDIUM #define FARF_MEDIUM 1 #endif #include "HAP_farf.h" #include "timer.h" #include "chre/core/event_loop_manager.h" #include "chre/core/host_comms_manager.h" #include "chre/core/settings.h" #include "chre/platform/fatal_error.h" #include "chre/platform/log.h" #include "chre/platform/memory.h" #include "chre/platform/shared/host_protocol_chre.h" #include "chre/platform/shared/nanoapp_load_manager.h" #ifdef CHRE_USE_BUFFERED_LOGGING #include "chre/platform/shared/log_buffer_manager.h" #endif #include "chre/platform/slpi/fastrpc.h" #include "chre/platform/slpi/power_control_util.h" #include "chre/platform/slpi/system_time.h" #include "chre/platform/system_time.h" #include "chre/platform/system_timer.h" #include "chre/util/fixed_size_blocking_queue.h" #include "chre/util/flatbuffers/helpers.h" #include "chre/util/macros.h" #include "chre/util/nested_data_ptr.h" #include "chre/util/unique_ptr.h" #include "chre_api/chre/version.h" #include #include namespace chre { namespace { constexpr size_t kOutboundQueueSize = 32; //! The last time a time sync request message has been sent. //! TODO: Make this a member of HostLinkBase Nanoseconds gLastTimeSyncRequestNanos(0); struct LoadNanoappCallbackData { uint64_t appId; uint32_t transactionId; uint16_t hostClientId; UniquePtr nanoapp; uint32_t fragmentId; }; struct NanoappListData { ChreFlatBufferBuilder *builder; DynamicVector nanoappEntries; uint16_t hostClientId; }; enum class PendingMessageType { Shutdown, NanoappMessageToHost, HubInfoResponse, NanoappListResponse, LoadNanoappResponse, UnloadNanoappResponse, DebugDumpData, DebugDumpResponse, TimeSyncRequest, LowPowerMicAccessRequest, LowPowerMicAccessRelease, EncodedLogMessage, SelfTestResponse, }; struct PendingMessage { PendingMessage(PendingMessageType msgType, uint16_t hostClientId) { type = msgType; data.hostClientId = hostClientId; } PendingMessage(PendingMessageType msgType, const MessageToHost *msgToHost = nullptr) { type = msgType; data.msgToHost = msgToHost; } PendingMessage(PendingMessageType msgType, ChreFlatBufferBuilder *builder) { type = msgType; data.builder = builder; } PendingMessageType type; union { const MessageToHost *msgToHost; uint16_t hostClientId; ChreFlatBufferBuilder *builder; } data; }; struct UnloadNanoappCallbackData { uint64_t appId; uint32_t transactionId; uint16_t hostClientId; bool allowSystemNanoappUnload; }; /** * @see buildAndEnqueueMessage() */ typedef void(MessageBuilderFunction)(ChreFlatBufferBuilder &builder, void *cookie); FixedSizeBlockingQueue gOutboundQueue; int copyToHostBuffer(const ChreFlatBufferBuilder &builder, unsigned char *buffer, size_t bufferSize, unsigned int *messageLen) { uint8_t *data = builder.GetBufferPointer(); size_t size = builder.GetSize(); int result; if (size > bufferSize) { LOGE("Encoded structure size %zu too big for host buffer %zu; dropping", size, bufferSize); result = CHRE_FASTRPC_ERROR; } else { memcpy(buffer, data, size); *messageLen = size; result = CHRE_FASTRPC_SUCCESS; } return result; } /** * Wrapper function to enqueue a message on the outbound message queue. All * outgoing message to the host must be called through this function. * * @param message The message to send to host. * * @return true if the message was successfully added to the queue. */ bool enqueueMessage(PendingMessage message) { // Vote for big image temporarily when waking up the main thread waiting for // the message bool voteSuccess = slpiForceBigImage(); bool success = gOutboundQueue.push(message); // Remove the vote only if we successfully made a big image transition if (voteSuccess) { slpiRemoveBigImageVote(); } return success; } /** * Helper function that takes care of the boilerplate for allocating a * ChreFlatBufferBuilder on the heap and adding it to the outbound message * queue. * * @param msgType Identifies the message while in the outboud queue * @param initialBufferSize Number of bytes to reserve when first allocating the * ChreFlatBufferBuilder * @param buildMsgFunc Synchronous callback used to encode the FlatBuffer * message. Will not be invoked if allocation fails. * @param cookie Opaque pointer that will be passed through to buildMsgFunc * * @return true if the message was successfully added to the queue */ bool buildAndEnqueueMessage(PendingMessageType msgType, size_t initialBufferSize, MessageBuilderFunction *msgBuilder, void *cookie) { bool pushed = false; auto builder = MakeUnique(initialBufferSize); if (builder.isNull()) { LOGE("Couldn't allocate memory for message type %d", static_cast(msgType)); } else { msgBuilder(*builder, cookie); // TODO: if this fails, ideally we should block for some timeout until // there's space in the queue if (!enqueueMessage(PendingMessage(msgType, builder.get()))) { LOGE("Couldn't push message type %d to outbound queue", static_cast(msgType)); } else { builder.release(); pushed = true; } } return pushed; } /** * FlatBuffer message builder callback used with handleNanoappListRequest() */ void buildNanoappListResponse(ChreFlatBufferBuilder &builder, void *cookie) { auto nanoappAdderCallback = [](const Nanoapp *nanoapp, void *data) { auto *cbData = static_cast(data); HostProtocolChre::addNanoappListEntry( *(cbData->builder), cbData->nanoappEntries, nanoapp->getAppId(), nanoapp->getAppVersion(), true /*enabled*/, nanoapp->isSystemNanoapp(), nanoapp->getAppPermissions()); }; // Add a NanoappListEntry to the FlatBuffer for each nanoapp auto *cbData = static_cast(cookie); cbData->builder = &builder; EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); eventLoop.forEachNanoapp(nanoappAdderCallback, cbData); HostProtocolChre::finishNanoappListResponse(builder, cbData->nanoappEntries, cbData->hostClientId); } void finishLoadingNanoappCallback(SystemCallbackType /*type*/, UniquePtr &&data) { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { auto *cbData = static_cast(cookie); EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); bool success = cbData->nanoapp->isLoaded() && eventLoop.startNanoapp(cbData->nanoapp); HostProtocolChre::encodeLoadNanoappResponse(builder, cbData->hostClientId, cbData->transactionId, success, cbData->fragmentId); }; constexpr size_t kInitialBufferSize = 48; buildAndEnqueueMessage(PendingMessageType::LoadNanoappResponse, kInitialBufferSize, msgBuilder, data.get()); } void handleUnloadNanoappCallback(SystemCallbackType /*type*/, UniquePtr &&data) { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { auto *cbData = static_cast(cookie); bool success = false; uint32_t instanceId; EventLoop &eventLoop = EventLoopManagerSingleton::get()->getEventLoop(); if (!eventLoop.findNanoappInstanceIdByAppId(cbData->appId, &instanceId)) { LOGE("Couldn't unload app ID 0x%016" PRIx64 ": not found", cbData->appId); } else { success = eventLoop.unloadNanoapp(instanceId, cbData->allowSystemNanoappUnload); } HostProtocolChre::encodeUnloadNanoappResponse( builder, cbData->hostClientId, cbData->transactionId, success); }; constexpr size_t kInitialBufferSize = 52; buildAndEnqueueMessage(PendingMessageType::UnloadNanoappResponse, kInitialBufferSize, msgBuilder, data.get()); } int generateMessageToHost(const MessageToHost *msgToHost, unsigned char *buffer, size_t bufferSize, unsigned int *messageLen) { // TODO: ideally we'd construct our flatbuffer directly in the // host-supplied buffer constexpr size_t kFixedSizePortion = 80; ChreFlatBufferBuilder builder(msgToHost->message.size() + kFixedSizePortion); HostProtocolChre::encodeNanoappMessage( builder, msgToHost->appId, msgToHost->toHostData.messageType, msgToHost->toHostData.hostEndpoint, msgToHost->message.data(), msgToHost->message.size(), msgToHost->toHostData.appPermissions, msgToHost->toHostData.messagePermissions); int result = copyToHostBuffer(builder, buffer, bufferSize, messageLen); auto &hostCommsManager = EventLoopManagerSingleton::get()->getHostCommsManager(); hostCommsManager.onMessageToHostComplete(msgToHost); return result; } int generateHubInfoResponse(uint16_t hostClientId, unsigned char *buffer, size_t bufferSize, unsigned int *messageLen) { constexpr size_t kInitialBufferSize = 192; constexpr char kHubName[] = "CHRE on SLPI"; constexpr char kVendor[] = "Google"; constexpr char kToolchain[] = "Hexagon Tools 8.x (clang " STRINGIFY(__clang_major__) "." STRINGIFY( __clang_minor__) "." STRINGIFY(__clang_patchlevel__) ")"; constexpr uint32_t kLegacyPlatformVersion = 0; constexpr uint32_t kLegacyToolchainVersion = ((__clang_major__ & 0xFF) << 24) | ((__clang_minor__ & 0xFF) << 16) | (__clang_patchlevel__ & 0xFFFF); constexpr float kPeakMips = 350; constexpr float kStoppedPower = 0; constexpr float kSleepPower = 1; constexpr float kPeakPower = 15; // Note that this may execute prior to EventLoopManager::lateInit() completing ChreFlatBufferBuilder builder(kInitialBufferSize); HostProtocolChre::encodeHubInfoResponse( builder, kHubName, kVendor, kToolchain, kLegacyPlatformVersion, kLegacyToolchainVersion, kPeakMips, kStoppedPower, kSleepPower, kPeakPower, CHRE_MESSAGE_TO_HOST_MAX_SIZE, chreGetPlatformId(), chreGetVersion(), hostClientId); return copyToHostBuffer(builder, buffer, bufferSize, messageLen); } int generateMessageFromBuilder(ChreFlatBufferBuilder *builder, unsigned char *buffer, size_t bufferSize, unsigned int *messageLen, bool isEncodedLogMessage) { CHRE_ASSERT(builder != nullptr); int result = copyToHostBuffer(*builder, buffer, bufferSize, messageLen); #ifdef CHRE_USE_BUFFERED_LOGGING if (isEncodedLogMessage && LogBufferManagerSingleton::isInitialized()) { LogBufferManagerSingleton::get()->onLogsSentToHost(); } #else UNUSED_VAR(isEncodedLogMessage); #endif builder->~ChreFlatBufferBuilder(); memoryFree(builder); return result; } void sendDebugDumpData(uint16_t hostClientId, const char *debugStr, size_t debugStrSize) { struct DebugDumpMessageData { uint16_t hostClientId; const char *debugStr; size_t debugStrSize; }; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { const auto *data = static_cast(cookie); HostProtocolChre::encodeDebugDumpData(builder, data->hostClientId, data->debugStr, data->debugStrSize); }; constexpr size_t kFixedSizePortion = 52; DebugDumpMessageData data; data.hostClientId = hostClientId; data.debugStr = debugStr; data.debugStrSize = debugStrSize; buildAndEnqueueMessage(PendingMessageType::DebugDumpData, kFixedSizePortion + debugStrSize, msgBuilder, &data); } void sendDebugDumpResponse(uint16_t hostClientId, bool success, uint32_t dataCount) { struct DebugDumpResponseData { uint16_t hostClientId; bool success; uint32_t dataCount; }; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { const auto *data = static_cast(cookie); HostProtocolChre::encodeDebugDumpResponse(builder, data->hostClientId, data->success, data->dataCount); }; constexpr size_t kInitialSize = 52; DebugDumpResponseData data; data.hostClientId = hostClientId; data.success = success; data.dataCount = dataCount; buildAndEnqueueMessage(PendingMessageType::DebugDumpResponse, kInitialSize, msgBuilder, &data); } void sendSelfTestResponse(uint16_t hostClientId, bool success) { struct SelfTestResponseData { uint16_t hostClientId; bool success; }; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { const auto *data = static_cast(cookie); HostProtocolChre::encodeSelfTestResponse(builder, data->hostClientId, data->success); }; constexpr size_t kInitialSize = 52; SelfTestResponseData data; data.hostClientId = hostClientId; data.success = success; buildAndEnqueueMessage(PendingMessageType::SelfTestResponse, kInitialSize, msgBuilder, &data); } void sendFragmentResponse(uint16_t hostClientId, uint32_t transactionId, uint32_t fragmentId, bool success) { struct FragmentedLoadInfoResponse { uint16_t hostClientId; uint32_t transactionId; uint32_t fragmentId; bool success; }; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { auto *cbData = static_cast(cookie); HostProtocolChre::encodeLoadNanoappResponse( builder, cbData->hostClientId, cbData->transactionId, cbData->success, cbData->fragmentId); }; FragmentedLoadInfoResponse response = { .hostClientId = hostClientId, .transactionId = transactionId, .fragmentId = fragmentId, .success = success, }; constexpr size_t kInitialBufferSize = 48; buildAndEnqueueMessage(PendingMessageType::LoadNanoappResponse, kInitialBufferSize, msgBuilder, &response); } /** * Sends a request to the host for a time sync message. */ void sendTimeSyncRequest() { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { HostProtocolChre::encodeTimeSyncRequest(builder); }; constexpr size_t kInitialSize = 52; buildAndEnqueueMessage(PendingMessageType::TimeSyncRequest, kInitialSize, msgBuilder, nullptr); gLastTimeSyncRequestNanos = SystemTime::getMonotonicTime(); } void setTimeSyncRequestTimer(Nanoseconds delay) { static SystemTimer sTimeSyncRequestTimer; static bool sTimeSyncRequestTimerInitialized = false; // Check for timer init since this method might be called before CHRE // init is called. if (!sTimeSyncRequestTimerInitialized) { if (!sTimeSyncRequestTimer.init()) { FATAL_ERROR("Failed to initialize time sync request timer."); } else { sTimeSyncRequestTimerInitialized = true; } } if (sTimeSyncRequestTimer.isActive()) { sTimeSyncRequestTimer.cancel(); } auto callback = [](void * /* data */) { sendTimeSyncRequest(); }; if (!sTimeSyncRequestTimer.set(callback, nullptr /* data */, delay)) { LOGE("Failed to set time sync request timer."); } } /** * Helper function that prepares a nanoapp that can be loaded into the system * from a file stored on disk. * * @param hostClientId the ID of client that originated this transaction * @param transactionId the ID of the transaction * @param appId the ID of the app to load * @param appVersion the version of the app to load * @param targetApiVersion the API version this nanoapp is targeted for * @param appFilename Null-terminated ASCII string containing the file name that * contains the app binary to be loaded. * * @return A valid pointer to a nanoapp that can be loaded into the system. A * nullptr if the preparation process fails. */ UniquePtr handleLoadNanoappFile(uint16_t hostClientId, uint32_t transactionId, uint64_t appId, uint32_t appVersion, uint32_t targetApiVersion, const char *appFilename) { LOGD("Load nanoapp request for app ID 0x%016" PRIx64 " ver 0x%" PRIx32 " target API 0x%08" PRIx32 " (txnId %" PRIu32 " client %" PRIu16 ")", appId, appVersion, targetApiVersion, transactionId, hostClientId); auto nanoapp = MakeUnique(); if (nanoapp.isNull()) { LOG_OOM(); } else if (!nanoapp->setAppInfo(appId, appVersion, appFilename, targetApiVersion) || !nanoapp->isLoaded()) { nanoapp.reset(nullptr); } return nanoapp; } /** * Helper function that prepares a nanoapp that can be loaded into the system * from a buffer sent over in 1 or more fragments. * * @param hostClientId the ID of client that originated this transaction * @param transactionId the ID of the transaction * @param appId the ID of the app to load * @param appVersion the version of the app to load * @param appFlags The flags provided by the app being loaded * @param targetApiVersion the API version this nanoapp is targeted for * @param buffer the nanoapp binary data. May be only part of the nanoapp's * binary if it's being sent over multiple fragments * @param bufferLen the size of buffer in bytes * @param fragmentId the identifier indicating which fragment is being loaded * @param appBinaryLen the full size of the nanoapp binary to be loaded * * @return A valid pointer to a nanoapp that can be loaded into the system. A * nullptr if the preparation process fails. */ UniquePtr handleLoadNanoappData(uint16_t hostClientId, uint32_t transactionId, uint64_t appId, uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion, const void *buffer, size_t bufferLen, uint32_t fragmentId, size_t appBinaryLen) { static NanoappLoadManager sLoadManager; bool success = true; if (fragmentId == 0 || fragmentId == 1) { // first fragment size_t totalAppBinaryLen = (fragmentId == 0) ? bufferLen : appBinaryLen; LOGD("Load nanoapp request for app ID 0x%016" PRIx64 " ver 0x%" PRIx32 " flags 0x%" PRIx32 " target API 0x%08" PRIx32 " size %zu (txnId %" PRIu32 " client %" PRIu16 ")", appId, appVersion, appFlags, targetApiVersion, totalAppBinaryLen, transactionId, hostClientId); if (sLoadManager.hasPendingLoadTransaction()) { FragmentedLoadInfo info = sLoadManager.getTransactionInfo(); sendFragmentResponse(info.hostClientId, info.transactionId, 0 /* fragmentId */, false /* success */); sLoadManager.markFailure(); } success = sLoadManager.prepareForLoad(hostClientId, transactionId, appId, appVersion, appFlags, totalAppBinaryLen, targetApiVersion); } success &= sLoadManager.copyNanoappFragment( hostClientId, transactionId, (fragmentId == 0) ? 1 : fragmentId, buffer, bufferLen); UniquePtr nanoapp; if (!sLoadManager.isLoadComplete()) { sendFragmentResponse(hostClientId, transactionId, fragmentId, success); } else { nanoapp = sLoadManager.releaseNanoapp(); } return nanoapp; } /** * FastRPC method invoked by the host to block on messages * * @param buffer Output buffer to populate with message data * @param bufferLen Size of the buffer, in bytes * @param messageLen Output parameter to populate with the size of the message * in bytes upon success * * @return 0 on success, nonzero on failure */ extern "C" int chre_slpi_get_message_to_host(unsigned char *buffer, int bufferLen, unsigned int *messageLen) { CHRE_ASSERT(buffer != nullptr); CHRE_ASSERT(bufferLen > 0); CHRE_ASSERT(messageLen != nullptr); int result = CHRE_FASTRPC_ERROR; if (bufferLen <= 0 || buffer == nullptr || messageLen == nullptr) { // Note that we can't use regular logs here as they can result in sending // a message, leading to an infinite loop if the error is persistent FARF(FATAL, "Invalid buffer size %d or bad pointers (buf %d len %d)", bufferLen, (buffer == nullptr), (messageLen == nullptr)); } else { size_t bufferSize = static_cast(bufferLen); PendingMessage pendingMsg = gOutboundQueue.pop(); switch (pendingMsg.type) { case PendingMessageType::Shutdown: result = CHRE_FASTRPC_ERROR_SHUTTING_DOWN; break; case PendingMessageType::NanoappMessageToHost: result = generateMessageToHost(pendingMsg.data.msgToHost, buffer, bufferSize, messageLen); break; case PendingMessageType::HubInfoResponse: result = generateHubInfoResponse(pendingMsg.data.hostClientId, buffer, bufferSize, messageLen); break; case PendingMessageType::NanoappListResponse: case PendingMessageType::LoadNanoappResponse: case PendingMessageType::UnloadNanoappResponse: case PendingMessageType::DebugDumpData: case PendingMessageType::DebugDumpResponse: case PendingMessageType::TimeSyncRequest: case PendingMessageType::LowPowerMicAccessRequest: case PendingMessageType::LowPowerMicAccessRelease: case PendingMessageType::EncodedLogMessage: case PendingMessageType::SelfTestResponse: result = generateMessageFromBuilder( pendingMsg.data.builder, buffer, bufferSize, messageLen, pendingMsg.type == PendingMessageType::EncodedLogMessage); break; default: CHRE_ASSERT_LOG(false, "Unexpected pending message type"); } } // Opportunistically send a time sync message (1 hour period threshold) constexpr Seconds kOpportunisticTimeSyncPeriod = Seconds(60 * 60 * 1); if (SystemTime::getMonotonicTime() > gLastTimeSyncRequestNanos + kOpportunisticTimeSyncPeriod) { sendTimeSyncRequest(); } return result; } /** * FastRPC method invoked by the host to send a message to the system * * @param buffer * @param size * * @return 0 on success, nonzero on failure */ extern "C" int chre_slpi_deliver_message_from_host(const unsigned char *message, int messageLen) { CHRE_ASSERT(message != nullptr); CHRE_ASSERT(messageLen > 0); int result = CHRE_FASTRPC_ERROR; if (message == nullptr || messageLen <= 0) { LOGE("Got null or invalid size (%d) message from host", messageLen); } else if (!HostProtocolChre::decodeMessageFromHost( message, static_cast(messageLen))) { LOGE("Failed to decode/handle message"); } else { result = CHRE_FASTRPC_SUCCESS; } return result; } } // anonymous namespace void sendDebugDumpResultToHost(uint16_t hostClientId, const char *debugStr, size_t debugStrSize, bool complete, uint32_t dataCount) { if (debugStrSize > 0) { sendDebugDumpData(hostClientId, debugStr, debugStrSize); } if (complete) { sendDebugDumpResponse(hostClientId, true /*success*/, dataCount); } } void HostLink::flushMessagesSentByNanoapp(uint64_t /*appId*/) { // TODO: this is not completely safe since it's timer-based, but should work // well enough for the initial implementation. To be fully safe, we'd need // some synchronization with the thread that runs // chre_slpi_get_message_to_host(), e.g. a mutex that is held by that thread // prior to calling pop() and only released after onMessageToHostComplete // would've been called. If we acquire that mutex here, and hold it while // purging any messages sent by the nanoapp in the queue, we can be certain // that onMessageToHostComplete will not be called after this function returns // for messages sent by that nanoapp flushOutboundQueue(); // One extra sleep to try to ensure that any messages popped just before // checking empty() are fully processed before we return constexpr time_timetick_type kFinalDelayUsec = 10000; timer_sleep(kFinalDelayUsec, T_USEC, true /* non_deferrable */); } bool HostLink::sendMessage(const MessageToHost *message) { return enqueueMessage( PendingMessage(PendingMessageType::NanoappMessageToHost, message)); } bool HostLinkBase::flushOutboundQueue() { int waitCount = 5; FARF(MEDIUM, "Draining message queue"); while (!gOutboundQueue.empty() && waitCount-- > 0) { timer_sleep(kPollingIntervalUsec, T_USEC, true /* non_deferrable */); } return (waitCount >= 0); } void HostLinkBase::sendLogMessage(const uint8_t *logMessage, size_t logMessageSize) { struct LogMessageData { const uint8_t *logMsg; size_t logMsgSize; }; LogMessageData logMessageData; logMessageData.logMsg = logMessage; logMessageData.logMsgSize = logMessageSize; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { const auto *data = static_cast(cookie); HostProtocolChre::encodeLogMessages(builder, data->logMsg, data->logMsgSize); }; constexpr size_t kInitialSize = 128; buildAndEnqueueMessage(PendingMessageType::EncodedLogMessage, kInitialSize, msgBuilder, &logMessageData); } void HostLinkBase::sendLogMessageV2(const uint8_t *logMessage, size_t logMessageSize, uint32_t numLogsDropped) { struct LogMessageData { const uint8_t *logMsg; size_t logMsgSize; uint32_t numLogsDropped; }; LogMessageData logMessageData{logMessage, logMessageSize, numLogsDropped}; auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { const auto *data = static_cast(cookie); HostProtocolChre::encodeLogMessagesV2( builder, data->logMsg, data->logMsgSize, data->numLogsDropped); }; constexpr size_t kInitialSize = 128; buildAndEnqueueMessage(PendingMessageType::EncodedLogMessage, kInitialSize, msgBuilder, &logMessageData); } void HostLinkBase::shutdown() { // Push a null message so the blocking call in chre_slpi_get_message_to_host() // returns and the host can exit cleanly. If the queue is full, try again to // avoid getting stuck (no other new messages should be entering the queue at // this time). Don't wait too long as the host-side binary may have died in // a state where it's not blocked in chre_slpi_get_message_to_host(). int retryCount = 5; FARF(MEDIUM, "Shutting down host link"); while (!enqueueMessage(PendingMessage(PendingMessageType::Shutdown)) && --retryCount > 0) { timer_sleep(kPollingIntervalUsec, T_USEC, true /* non_deferrable */); } if (retryCount <= 0) { // Don't use LOGE, as it may involve trying to send a message FARF(ERROR, "No room in outbound queue for shutdown message and host not " "draining queue!"); } else { // We were able to push the shutdown message. Wait for the queue to // completely flush before returning. if (!flushOutboundQueue()) { FARF(ERROR, "Host took too long to drain outbound queue; exiting anyway"); } else { FARF(MEDIUM, "Finished draining queue"); } } } void sendAudioRequest() { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { HostProtocolChre::encodeLowPowerMicAccessRequest(builder); }; constexpr size_t kInitialSize = 32; buildAndEnqueueMessage(PendingMessageType::LowPowerMicAccessRequest, kInitialSize, msgBuilder, nullptr); } void sendAudioRelease() { auto msgBuilder = [](ChreFlatBufferBuilder &builder, void *cookie) { HostProtocolChre::encodeLowPowerMicAccessRelease(builder); }; constexpr size_t kInitialSize = 32; buildAndEnqueueMessage(PendingMessageType::LowPowerMicAccessRelease, kInitialSize, msgBuilder, nullptr); } void HostMessageHandlers::handleNanoappMessage(uint64_t appId, uint32_t messageType, uint16_t hostEndpoint, const void *messageData, size_t messageDataLen) { LOGD("Parsed nanoapp message from host: app ID 0x%016" PRIx64 ", endpoint " "0x%" PRIx16 ", msgType %" PRIu32 ", payload size %zu", appId, hostEndpoint, messageType, messageDataLen); HostCommsManager &manager = EventLoopManagerSingleton::get()->getHostCommsManager(); manager.sendMessageToNanoappFromHost(appId, messageType, hostEndpoint, messageData, messageDataLen); } void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) { // We generate the response in the context of chre_slpi_get_message_to_host LOGD("Hub info request from client ID %" PRIu16, hostClientId); enqueueMessage( PendingMessage(PendingMessageType::HubInfoResponse, hostClientId)); } void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) { auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) { uint16_t cbHostClientId = NestedDataPtr(data); NanoappListData cbData = {}; cbData.hostClientId = cbHostClientId; size_t expectedNanoappCount = EventLoopManagerSingleton::get()->getEventLoop().getNanoappCount(); if (!cbData.nanoappEntries.reserve(expectedNanoappCount)) { LOG_OOM(); } else { constexpr size_t kFixedOverhead = 48; constexpr size_t kPerNanoappSize = 32; size_t initialBufferSize = (kFixedOverhead + expectedNanoappCount * kPerNanoappSize); buildAndEnqueueMessage(PendingMessageType::NanoappListResponse, initialBufferSize, buildNanoappListResponse, &cbData); } }; LOGD("Nanoapp list request from client ID %" PRIu16, hostClientId); EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::NanoappListResponse, NestedDataPtr(hostClientId), callback); } void HostMessageHandlers::handleLoadNanoappRequest( uint16_t hostClientId, uint32_t transactionId, uint64_t appId, uint32_t appVersion, uint32_t appFlags, uint32_t targetApiVersion, const void *buffer, size_t bufferLen, const char *appFileName, uint32_t fragmentId, size_t appBinaryLen, bool respondBeforeStart) { UniquePtr pendingNanoapp; if (appFileName != nullptr) { pendingNanoapp = handleLoadNanoappFile(hostClientId, transactionId, appId, appVersion, targetApiVersion, appFileName); } else { pendingNanoapp = handleLoadNanoappData( hostClientId, transactionId, appId, appVersion, appFlags, targetApiVersion, buffer, bufferLen, fragmentId, appBinaryLen); } if (!pendingNanoapp.isNull()) { auto cbData = MakeUnique(); if (cbData.isNull()) { LOG_OOM(); } else { cbData->transactionId = transactionId; cbData->hostClientId = hostClientId; cbData->appId = appId; cbData->fragmentId = fragmentId; cbData->nanoapp = std::move(pendingNanoapp); // Note that if this fails, we'll generate the error response in // the normal deferred callback EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::FinishLoadingNanoapp, std::move(cbData), finishLoadingNanoappCallback); } } } void HostMessageHandlers::handleUnloadNanoappRequest( uint16_t hostClientId, uint32_t transactionId, uint64_t appId, bool allowSystemNanoappUnload) { LOGD("Unload nanoapp request (txnID %" PRIu32 ") for appId 0x%016" PRIx64 " system %d", transactionId, appId, allowSystemNanoappUnload); auto cbData = MakeUnique(); if (cbData == nullptr) { LOG_OOM(); } else { cbData->appId = appId; cbData->transactionId = transactionId; cbData->hostClientId = hostClientId; cbData->allowSystemNanoappUnload = allowSystemNanoappUnload; EventLoopManagerSingleton::get()->deferCallback( SystemCallbackType::HandleUnloadNanoapp, std::move(cbData), handleUnloadNanoappCallback); } } void HostMessageHandlers::handleTimeSyncMessage(int64_t offset) { setEstimatedHostTimeOffset(offset); // Schedule a time sync request since offset may drift constexpr Seconds kClockDriftTimeSyncPeriod = Seconds(60 * 60 * 6); // 6 hours setTimeSyncRequestTimer(kClockDriftTimeSyncPeriod); } void HostMessageHandlers::handleDebugDumpRequest(uint16_t hostClientId) { if (!chre::EventLoopManagerSingleton::get() ->getDebugDumpManager() .onDebugDumpRequested(hostClientId)) { LOGE("Couldn't trigger debug dump process"); sendDebugDumpResponse(hostClientId, false /*success*/, 0 /*dataCount*/); } } void HostMessageHandlers::handleSettingChangeMessage(fbs::Setting setting, fbs::SettingState state) { Setting chreSetting; SettingState chreSettingState; if (HostProtocolChre::getSettingFromFbs(setting, &chreSetting) && HostProtocolChre::getSettingStateFromFbs(state, &chreSettingState)) { postSettingChange(chreSetting, chreSettingState); } } void HostMessageHandlers::handleSelfTestRequest(uint16_t hostClientId) { // TODO(b/182201569): Run test bool success = true; sendSelfTestResponse(hostClientId, success); } } // namespace chre