host是站在slpi chre的角度去看,指的是AP。
A collection of CHRE-related code that runs on the host processor. Note that "host" in this case refers to CHRE's perspective of host, i.e. it's the applications processor, as opposed to the Android build perspective, where host
refers to the PC making the build.
The daemon that hosts CHRE on the SLPI via FastRPC.
* Several threads are required for this functionality:
* - Main thread: blocked waiting on SIGINT/SIGTERM, and requests graceful
* shutdown of CHRE when caught
* - Monitor thread: persistently blocked in a FastRPC call to the SLPI that
* only returns when CHRE exits or the SLPI crashes
* - Reverse monitor thread: after initializing the SLPI-side monitor for this
* process, blocks on a condition variable. If this thread exits, CHRE on
* the SLPI side will be notified and shut down (this is only possible if
* this thread is not blocked in a FastRPC call).
* - Message to host (RX) thread: blocks in FastRPC call, waiting on incoming
* message from CHRE
* - Message to CHRE (TX) thread: blocks waiting on outbound queue, delivers
* messages to CHRE over FastRPC
这里只关心 RX/ TX thread
tx thread指的是有socket client he socket server连接,socket service通过TX thread接收到client的请求。
server.run("chre", true, onMessageReceivedFromClient);//意思是启动socket server:chre,并通过onMessageReceivedFromClient处理来着连接到chre的请求。
::android::chre::SocketServer server;
void SocketServer::run(const char *socketName, bool allowSocketCreation,
ClientMessageCallback clientMessageCallback) {
mClientMessageCallback = clientMessageCallback;//下面会用到mClientMessageCallback
{
mSockFd = socket_local_server(socketName,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_SEQPACKET);
}
{
int ret = listen(mSockFd, kMaxPendingConnectionRequests);
{
serviceSocket();
}
close(mSockFd);
}
}
void SocketServer::serviceSocket() {
LOGI("Ready to accept connections");
while (!sSignalReceived) {
int ret = ppoll(mPollFds, 1 + kMaxActiveClients, nullptr, &signalMask);
if (mPollFds[kListenIndex].revents & POLLIN) {
acceptClientConnection();
}
for (size_t i = 1; i <= kMaxActiveClients; i++) {
if (mPollFds[i].revents & POLLIN) {
handleClientData(mPollFds[i].fd);
}
}
}
}
void SocketServer::handleClientData(int clientSocket) {
const ClientData& clientData = mClients[clientSocket];
uint16_t clientId = clientData.clientId;
ssize_t packetSize = recv(
clientSocket, mRecvBuffer.data(), mRecvBuffer.size(), MSG_DONTWAIT);
LOGV("Got %zd byte packet from client %" PRIu16, packetSize, clientId);
mClientMessageCallback(clientId, mRecvBuffer.data(), packetSize);
}
}
mClientMessageCallback就是注册的onMessageReceivedFromClient
void onMessageReceivedFromClient(uint16_t clientId, void *data, size_t length) {
constexpr size_t kMaxPayloadSize = 1024 * 1024; // 1 MiB
// This limitation is due to FastRPC, but there's no case where we should come
// close to this limit...
static_assert(kMaxPayloadSize <= INT32_MAX,
"SLPI uses 32-bit signed integers to represent message size");
if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
LOGE("Couldn't set host client ID in message container!");
} else {
LOGV("Delivering message from host (size %zu)", length);
log_buffer(static_cast(data), length);
int ret = chre_slpi_deliver_message_from_host(
static_cast(data), static_cast(length));
}
}
只是进行了简单数据验证:mutateHostClientId,然后通过fatRPC调入slpi_chre环境另一个cpu/dsp
chre/platform/slpi/host_link.cc
/**
* 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) {
if (!HostProtocolChre::decodeMessageFromHost(
message, static_cast(messageLen))) {
LOGE("Failed to decode/handle message");
}
}
下面以两种类型的Message为例,对具体NanoApp的message和对整个chre的message
bool HostProtocolChre::decodeMessageFromHost(const void *message,
size_t messageLen) {
bool success = verifyMessage(message, messageLen);
if (!success) {
LOGE("Dropping invalid/corrupted message from host (length %zu)",
messageLen);
} else {
const fbs::MessageContainer *container = fbs::GetMessageContainer(message);
uint16_t hostClientId = container->host_addr()->client_id();
switch (container->message_type()) {
case fbs::ChreMessage::NanoappMessage: {
const auto *nanoappMsg = static_cast(
container->message());
// Required field; verifier ensures that this is not null (though it
// may be empty)
const flatbuffers::Vector *msgData = nanoappMsg->message();
HostMessageHandlers::handleNanoappMessage(
nanoappMsg->app_id(), nanoappMsg->message_type(),
nanoappMsg->host_endpoint(), msgData->data(), msgData->size());
break;
}
case fbs::ChreMessage::HubInfoRequest:
HostMessageHandlers::handleHubInfoRequest(hostClientId);
break;
case fbs::ChreMessage::NanoappListRequest:
HostMessageHandlers::handleNanoappListRequest(hostClientId);
break;
case fbs::ChreMessage::LoadNanoappRequest: {
const auto *request = static_cast(
container->message());
const flatbuffers::Vector *appBinary = request->app_binary();
HostMessageHandlers::handleLoadNanoappRequest(
hostClientId, request->transaction_id(), request->app_id(),
request->app_version(), request->target_api_version(),
appBinary->data(), appBinary->size(), request->fragment_id(),
request->total_app_size());
break;
}
}
稍作处理成message后,enqueue message到push到messagequeue: gOutboundQueue, 所有发送到host的message最后到push到messagequeue: gOutboundQueue,包括下面的具体nanoApp的发送到host的信息
void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) {
// We generate the response in the context of chre_slpi_get_message_to_host
LOGD("Got hub info request from client ID %" PRIu16, hostClientId);
enqueueMessage(PendingMessage(
PendingMessageType::HubInfoResponse, hostClientId));
}
/**
* 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;
}
chre/platform/slpi/host_link.cc
void HostMessageHandlers::handleNanoappMessage(
uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
const void *messageData, size_t messageDataLen) {
HostCommsManager& manager =
EventLoopManagerSingleton::get()->getHostCommsManager();
manager.sendMessageToNanoappFromHost(
appId, messageType, hostEndpoint, messageData, messageDataLen);
}
chre/core/host_comms_manager.cc
void HostCommsManager::sendMessageToNanoappFromHost {
deliverNanoappMessageFromHost(appId, hostEndpoint, messageType, messageData,
static_cast(messageSize),
targetInstanceId);
}
void HostCommsManager::deliverNanoappMessageFromHost(
uint64_t appId, uint16_t hostEndpoint, uint32_t messageType,
const void *messageData, uint32_t messageSize, uint32_t targetInstanceId) {
bool success = false;
MessageFromHost *msgFromHost = mMessagePool.allocate();
if (msgFromHost == nullptr) {
LOGE("Couldn't allocate message from host");
} 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);
} else {
msgFromHost->appId = appId;
msgFromHost->fromHostData.messageType = messageType;
msgFromHost->fromHostData.messageSize = static_cast(
messageSize);
msgFromHost->fromHostData.message = msgFromHost->message.data();
msgFromHost->fromHostData.hostEndpoint = hostEndpoint;
success = EventLoopManagerSingleton::get()->getEventLoop().postEvent(
CHRE_EVENT_MESSAGE_FROM_HOST, &msgFromHost->fromHostData,
freeMessageFromHostCallback, kSystemInstanceId, targetInstanceId);
}
if (!success && msgFromHost != nullptr) {
mMessagePool.deallocate(msgFromHost);
}
}
上面调用posetEvent(CHRE_EVENT_MESSAGE_FROM_HOST), 哪里处理这个event?
distributeEvent把event 分配到具体的nanoApp, 然后调用deliverEevent每个nanoApp处理event
void EventLoop::run() {
LOGI("EventLoop start");
bool havePendingEvents = false;
while (mRunning) {
// Events are delivered in two stages: first they arrive in the inbound
// event queue mEvents (potentially posted from another thread), then within
// this context these events are distributed to smaller event queues
// associated with each Nanoapp that should receive the event. Once the
// event is delivered to all interested Nanoapps, its free callback is
// invoked.
if (!havePendingEvents || !mEvents.empty()) {
if (mEvents.size() > mMaxEventPoolUsage) {
mMaxEventPoolUsage = mEvents.size();
}
// mEvents.pop() will be a blocking call if mEvents.empty()
distributeEvent(mEvents.pop());
}
havePendingEvents = deliverEvents();
mPowerControlManager.postEventLoopProcess(mEvents.size());
}
}
void EventLoop::distributeEvent(Event *event) {
for (const UniquePtr& app : mNanoapps) {
if ((event->targetInstanceId == chre::kBroadcastInstanceId
&& app->isRegisteredForBroadcastEvent(event->eventType))
|| event->targetInstanceId == app->getInstanceId()) {
app->postEvent(event);
}
}
}
bool EventLoop::deliverEvents() {
bool havePendingEvents = false;
// Do one loop of round-robin. We might want to have some kind of priority or
// time sharing in the future, but this should be good enough for now.
for (const UniquePtr& app : mNanoapps) {
if (app->hasPendingEvent()) {
havePendingEvents |= deliverNextEvent(app);
}
}
return havePendingEvents;
}
bool EventLoop::deliverNextEvent(const UniquePtr& app) {
// TODO: cleaner way to set/clear this? RAII-style?
mCurrentApp = app.get();
Event *event = app->processNextEvent();
mCurrentApp = nullptr;
if (event->isUnreferenced()) {
freeEvent(event);
}
return app->hasPendingEvent();
}
chre/core/nanoapp.cc
Event *Nanoapp::processNextEvent() {
Event *event = mEventQueue.pop();
CHRE_ASSERT_LOG(event != nullptr, "Tried delivering event, but queue empty");
if (event != nullptr) {
handleEvent(event->senderInstanceId, event->eventType, event->eventData);
}
return event;
}
/**
* @file
* The Nanoapp Support Library (NSL) that gets built with nanoapps to act as an
* intermediary to the reference CHRE implementation. It provides hooks so the
* app can be registered with the system, and also provides a layer where we can
* implement cross-version compatibility features as needed.
*/
DLL_EXPORT const struct chreNslNanoappInfo _chreNslDsoNanoappInfo = {
.magic = CHRE_NSL_NANOAPP_INFO_MAGIC,
.structMinorVersion = CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION,
.targetApiVersion = CHRE_API_VERSION,
// These values are supplied by the build environment
.vendor = NANOAPP_VENDOR_STRING,
.name = NANOAPP_NAME_STRING,
.isSystemNanoapp = 0,
.appId = NANOAPP_ID,
.appVersion = NANOAPP_VERSION,
.entryPoints = {
.start = nanoappStart,
.handleEvent = nanoappHandleEvent,
.end = nanoappEnd,
},
};
void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t evtType, const void* evtData)
{
static int waitForTimer = 0;
static int result = 1;
int success = 0;
FARF(HIGH, "App OEM got event 0x%X", (unsigned int)evtType);
switch(evtType) {
case CHRE_EVENT_MESSAGE_FROM_HOST:
{
int success = 0;
struct chreMessageFromHostData* msgIn = (struct chreMessageFromHostData*)evtData;
switch (msgIn->reservedMessageType) {
case CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE + APP_OEM_ECHO:
{
msg = chreHeapAlloc(msgIn->messageSize);
if (msg) {
memcpy(msg, msgIn->message, msgIn->messageSize);
success = chreSendMessageToHost(msg, msgIn->messageSize, msgIn->reservedMessageType, appOemMessageFreeCbk);
}
}
}
DLL_EXPORT bool chreSendMessageToHost(void *message, uint32_t messageSize,
uint32_t messageType,
chreMessageFreeFunction *freeCallback) {
return chreSendMessageToHostEndpoint(
message, static_cast(messageSize), messageType,
CHRE_HOST_ENDPOINT_BROADCAST, freeCallback);
}
DLL_EXPORT bool chreSendMessageToHostEndpoint(
void *message, size_t messageSize, uint32_t messageType,
uint16_t hostEndpoint, chreMessageFreeFunction *freeCallback) {
Nanoapp *nanoapp = EventLoopManager::validateChreApiCall(__func__);
bool success = false;
const EventLoop& eventLoop = EventLoopManagerSingleton::get()
->getEventLoop();
if (eventLoop.currentNanoappIsStopping()) {
LOGW("Rejecting message to host from app instance %" PRIu32 " because it's "
"stopping", nanoapp->getInstanceId());
} else {
auto& hostCommsManager =
EventLoopManagerSingleton::get()->getHostCommsManager();
success = hostCommsManager.sendMessageToHostFromNanoapp(
nanoapp, message, messageSize, messageType, hostEndpoint, freeCallback);
}
if (!success && freeCallback != nullptr) {
freeCallback(message, messageSize);
}
return success;
}
bool HostCommsManager::sendMessageToHostFromNanoapp(
Nanoapp *nanoapp, void *messageData, size_t messageSize,
uint32_t messageType, uint16_t hostEndpoint,
chreMessageFreeFunction *freeCallback) {
mHostLink.sendMessage(msgToHost);
}
chre/platform/slpi/host_link.cc
bool HostLink::sendMessage(const MessageToHost *message) {
return enqueueMessage(//又见上面所说的enqueueMessage
PendingMessage(PendingMessageType::NanoappMessageToHost, message));
}
RX指的是对来自slpi数据的处理过程
start_thread(&msg_to_host_thread, chre_message_to_host_thread, &server)
/**
* Entry point for the thread that receives messages sent by CHRE.
*
* @return always returns NULL
*/
static void *chre_message_to_host_thread(void *arg) {
unsigned char messageBuffer[4096];
unsigned int messageLen;
int result = 0;
auto *server = static_cast<::android::chre::SocketServer *>(arg);
while (true) {
messageLen = 0;
LOGV("Calling into chre_slpi_get_message_to_host");
result = chre_slpi_get_message_to_host(
messageBuffer, sizeof(messageBuffer), &messageLen);
LOGV("Got message from CHRE with size %u (result %d)", messageLen, result);
if (result == CHRE_FASTRPC_ERROR_SHUTTING_DOWN) {
LOGD("CHRE shutting down, exiting CHRE->Host message thread");
break;
} else if (result == CHRE_FASTRPC_SUCCESS && messageLen > 0) {
log_buffer(messageBuffer, messageLen);
uint16_t hostClientId;
fbs::ChreMessage messageType;
if (messageType == fbs::ChreMessage::LogMessage) {
parseAndEmitLogMessages(messageBuffer);
} else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
sendTimeSyncMessage();
} else if (hostClientId == chre::kHostClientIdUnspecified) {
server->sendToAllClients(messageBuffer,
static_cast(messageLen));
} else {
server->sendToClientById(messageBuffer,
static_cast(messageLen), hostClientId);
}
}
}
LOGV("Message to host thread exited");
return NULL;
}
/**
* 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) {
} else {
size_t bufferSize = static_cast(bufferLen);
PendingMessage pendingMsg = gOutboundQueue.pop();
switch (pendingMsg.type) {
}
}
这里可看到TX thread中提到的gOutboundQueue,一个push/一个pop
先看下socket client是怎样使用server:“chre”的
int main(void)
{
int ret = 1;
int config = 0;
SocketClient client;
sp callbacks = new SocketCallbacks();
if (!client.connect("chre", callbacks)) {
LOGE("Couldn't connect to socket");
goto bail;
}
}
//当 socket host有什么事件发生时,调用client注册的回调函数通知client
sp callbacks = new SocketCallbacks();
class SocketCallbacks : public SocketClient::ICallbacks,
public IChreMessageHandlers{}
SocketCallbacks继承SocketClient::ICallbacks and IChreMessageHandler
class SocketClient {
/**
* Represents the callback interface used for handling events that occur on
* the receive thread. Note that it is *not* safe to call connect(),
* connectInBackground(), or disconnect() from the context of these callbacks.
*/
class ICallbacks : public VirtualLightRefBase {
public:
/**
* Invoked from within the context of the read thread when a message is
* received on the socket.
*
* @param data Buffer containing received message data
* @param length Size of the message in bytes
*/
virtual void onMessageReceived(const void *data, size_t length) = 0;
/**
* Called when the socket is successfully (re-)connected.
*/
virtual void onConnected() {};
/**
* Called when we have failed to (re-)connect the socket after many attempts
* and are giving up.
*/
virtual void onConnectionAborted() {};
/**
* Invoked when the socket is disconnected, and this connection loss was not
* the result of an explicit call to disconnect(), i.e. the connection was
* terminated on the remote end.
*/
virtual void onDisconnected() {};
};
/**
* Calling code should provide an implementation of this interface to handle
* parsed results from decodeMessageFromChre().
*/
class IChreMessageHandlers {
public:
virtual ~IChreMessageHandlers() = default;
virtual void handleNanoappMessage(
const ::chre::fbs::NanoappMessageT& /*message*/) {};
virtual void handleHubInfoResponse(
const ::chre::fbs::HubInfoResponseT& /*response*/) {};
virtual void handleNanoappListResponse(
const ::chre::fbs::NanoappListResponseT& /*response*/) {};
virtual void handleLoadNanoappResponse(
const ::chre::fbs::LoadNanoappResponseT& /*response*/) {};
virtual void handleUnloadNanoappResponse(
const ::chre::fbs::UnloadNanoappResponseT& /*response*/) {};
virtual void handleDebugDumpData(
const ::chre::fbs::DebugDumpDataT& /*data*/) {};
virtual void handleDebugDumpResponse(
const ::chre::fbs::DebugDumpResponseT& /*response*/) {};
};
class SocketCallbacks : public SocketClient::ICallbacks,
public IChreMessageHandlers {
public:
void onMessageReceived(const void* data, size_t length) override
{
if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
LOGE("Failed to decode message");
}
}
void onConnected(void) override
{
LOGI("Socket (re)connected");
}
-----
void handleHubInfoResponse(
const ::chre::fbs::HubInfoResponseT& app_info_response) override
{
const char *name = android::chre::getStringFromByteVector(app_info_response.name);
const char *vendor = android::chre::getStringFromByteVector(app_info_response.vendor);
const char *toolchain = android::chre::getStringFromByteVector(app_info_response.toolchain);
LOGI("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
LOGI("Got hub info response:");
LOGI(" Name: '%s'", name);
LOGI(" Vendor: '%s'", vendor);
LOGI(" Toolchain: '%s'", toolchain);
LOGI(" Platform ID: 0x%016" PRIx64 " Version: 0x%08" PRIx32,
app_info_response.platform_id, app_info_response.chre_platform_version);
LOGI("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
}
}
void SocketServer::sendToAllClients(const void *data, size_t length) {
std::lock_guard lock(mClientsMutex);
int deliveredCount = 0;
for (const auto& pair : mClients) {
int clientSocket = pair.first;
uint16_t clientId = pair.second.clientId;
if (sendToClientSocket(data, length, clientSocket, clientId)) {
deliveredCount++;
}
}
}
bool SocketServer::sendToClientSocket(const void *data, size_t length,
int clientSocket, uint16_t clientId) {
ssize_t bytesSent = send(clientSocket, data, length, 0);
}
上面调用send想clientSocket发送数据,哪里接收处理数据?
在clientSocket向serverSocket connect时创建了对应的处理接收来着serverSocket的Thread
client.connect("chre", callbacks) -> SocketClient::doConnect {
mCallbacks = callbacks;
mRxThread = std::thread([this]() {
receiveThread();
});
}
void SocketClient::receiveThread() {
constexpr size_t kReceiveBufferSize = 4096;
uint8_t buffer[kReceiveBufferSize];
LOGV("Receive thread started");
while (!mGracefulShutdown && (mSockFd != INVALID_SOCKET || reconnect())) {
while (!mGracefulShutdown) {
ssize_t bytesReceived = recv(mSockFd, buffer, sizeof(buffer), 0);
mCallbacks->onMessageReceived(buffer, bytesReceived);
}
}
void onMessageReceived(const void* data, size_t length) override
{
if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
LOGE("Failed to decode message");
}
}
decodeMessageFromChre使用的处理函数是参数指针handlers执行的具体函数,而参数的内容是*this,也就是注册的具体函数
/**
* Calling code should provide an implementation of this interface to handle
* parsed results from decodeMessageFromChre().
*/
class IChreMessageHandlers {
public:
virtual ~IChreMessageHandlers() = default;
virtual void handleNanoappMessage(
const ::chre::fbs::NanoappMessageT& /*message*/) {};
virtual void handleHubInfoResponse(
const ::chre::fbs::HubInfoResponseT& /*response*/) {};
virtual void handleNanoappListResponse(
const ::chre::fbs::NanoappListResponseT& /*response*/) {};
virtual void handleLoadNanoappResponse(
const ::chre::fbs::LoadNanoappResponseT& /*response*/) {};
virtual void handleUnloadNanoappResponse(
const ::chre::fbs::UnloadNanoappResponseT& /*response*/) {};
virtual void handleDebugDumpData(
const ::chre::fbs::DebugDumpDataT& /*data*/) {};
virtual void handleDebugDumpResponse(
const ::chre::fbs::DebugDumpResponseT& /*response*/) {};
};
如 void handleNanoappMessage(
const ::chre::fbs::NanoappMessageT& app_message) override
{
int success = 1;
LOGI("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
if (app_message.message_type >= CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) {
LOGI("Got message from nanoapp 0x%" PRIx64 " to endpoint 0x%" PRIx16
" with type 0x%" PRIx32 " and length %zu", app_message.app_id, app_message.host_endpoint,
app_message.message_type, app_message.message.size());
LOGI("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
}
*this传入decodeMessageFromChre的handlers, 最终调用这里注册的handleNanoappMessage等
bool HostProtocolHost::decodeMessageFromChre(
const void *message, size_t messageLen, IChreMessageHandlers& handlers) {
bool success = verifyMessage(message, messageLen);
if (success) {
std::unique_ptr
fbs::UnPackMessageContainer(message);
fbs::ChreMessageUnion& msg = container->message;
switch (container->message.type) {
case fbs::ChreMessage::NanoappMessage:
handlers.handleNanoappMessage(*msg.AsNanoappMessage());
}