/* * Copyright (C) 2024 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/event_loop_manager.h" #include "chre/util/dynamic_vector.h" #include "chre/util/system/message_common.h" #include "chre/util/system/message_router.h" #include "chre/util/system/napp_permissions.h" #include "chre_api/chre.h" #include "chre_api/chre/event.h" #include "pw_allocator/allocator.h" #include "pw_allocator/libc_allocator.h" #include "pw_allocator/unique_ptr.h" #include "pw_function/function.h" #include "pw_intrusive_ptr/intrusive_ptr.h" #include "gtest/gtest.h" #include "inc/test_util.h" #include "test_base.h" #include "test_event.h" #include "test_util.h" #include #include #include #include #include namespace chre::message { namespace { CREATE_CHRE_TEST_EVENT(TEST_GET_EVENT_INFO, 0); CREATE_CHRE_TEST_EVENT(TEST_OPEN_SESSION, 1); CREATE_CHRE_TEST_EVENT(TEST_OPEN_DEFAULT_SESSION, 2); CREATE_CHRE_TEST_EVENT(TEST_OPEN_SESSION_NANOAPP_TO_NANOAPP, 3); CREATE_CHRE_TEST_EVENT(TEST_CLOSE_SESSION, 4); CREATE_CHRE_TEST_EVENT(TEST_CLOSE_SESSION_NON_PARTY, 5); CREATE_CHRE_TEST_EVENT(TEST_GET_SESSION_INFO_INVALID_SESSION, 6); CREATE_CHRE_TEST_EVENT(TEST_SEND_MESSAGE, 7); CREATE_CHRE_TEST_EVENT(TEST_SEND_MESSAGE_NO_FREE_CALLBACK, 8); CREATE_CHRE_TEST_EVENT(TEST_SEND_MESSAGE_NANOAPP_TO_NANOAPP, 9); CREATE_CHRE_TEST_EVENT(TEST_PUBLISH_SERVICE, 10); CREATE_CHRE_TEST_EVENT(TEST_BAD_LEGACY_SERVICE_NAME, 11); CREATE_CHRE_TEST_EVENT(TEST_OPEN_SESSION_WITH_SERVICE, 12); CREATE_CHRE_TEST_EVENT(TEST_SUBSCRIBE_TO_READY_EVENT, 13); CREATE_CHRE_TEST_EVENT(TEST_SUBSCRIBE_TO_READY_EVENT_ALREADY_EXISTS, 14); CREATE_CHRE_TEST_EVENT(TEST_UNSUBSCRIBE_FROM_READY_EVENT, 15); CREATE_CHRE_TEST_EVENT(TEST_SUBSCRIBE_TO_SERVICE_READY_EVENT, 16); CREATE_CHRE_TEST_EVENT(TEST_UNSUBSCRIBE_FROM_SERVICE_READY_EVENT, 17); constexpr size_t kNumEndpoints = 3; constexpr size_t kMessageSize = 5; constexpr MessageHubId kOtherMessageHubId = 0xDEADBEEFBEEFDEAD; EndpointInfo kEndpointInfos[kNumEndpoints] = { EndpointInfo(/* id= */ 1, /* name= */ "endpoint1", /* version= */ 1, EndpointType::NANOAPP, CHRE_MESSAGE_PERMISSION_NONE), EndpointInfo(/* id= */ 2, /* name= */ "endpoint2", /* version= */ 10, EndpointType::HOST_NATIVE, CHRE_MESSAGE_PERMISSION_BLE), EndpointInfo(/* id= */ 3, /* name= */ "endpoint3", /* version= */ 100, EndpointType::GENERIC, CHRE_MESSAGE_PERMISSION_AUDIO)}; EndpointInfo kDynamicEndpointInfo = EndpointInfo( /* id= */ 4, /* name= */ "DynamicallyRegisteredEndpoint", /* version= */ 1, EndpointType::NANOAPP, CHRE_MESSAGE_PERMISSION_NONE); const char kServiceDescriptorForEndpoint2[] = "TEST_SERVICE.TEST"; const char kServiceDescriptorForDynamicEndpoint[] = "TEST_DYNAMIC_SERVICE"; const char kServiceDescriptorForNanoapp[] = "TEST_NANOAPP.TEST_SERVICE"; const uint64_t kLegacyServiceId = 0xDEADBEEFDEADBEEF; const uint32_t kLegacyServiceVersion = 1; const uint64_t kLegacyServiceNanoappId = 0xCAFECAFECAFECAFE; const char kLegacyServiceName[] = "chre.nanoapp_0xCAFECAFECAFECAFE.service_0xDEADBEEFDEADBEEF"; const char kBadLegacyServiceName[] = "chre.nanoapp_0xCAFECAFECAFECAFE.service_0x0123456789ABCDEF"; //! Base class for MessageHubCallbacks used in tests class MessageHubCallbackBase : public MessageRouter::MessageHubCallback { public: void forEachEndpoint( const pw::Function &function) override { for (const EndpointInfo &endpointInfo : kEndpointInfos) { if (function(endpointInfo)) { return; } } } std::optional getEndpointInfo(EndpointId endpointId) override { for (const EndpointInfo &endpointInfo : kEndpointInfos) { if (endpointInfo.id == endpointId) { return endpointInfo; } } return std::nullopt; } void onSessionOpened(const Session &session) override { bool shouldNotify = false; { std::unique_lock lock(mSessionOpenedMutex); if (mSessionId == SESSION_ID_INVALID) { return; } if (mSessionId == session.sessionId) { shouldNotify = true; mSessionId = SESSION_ID_INVALID; } } if (shouldNotify) { mSessionOpenedCondVar.notify_one(); } } std::optional getEndpointForService( const char *serviceDescriptor) override { if (serviceDescriptor != nullptr && std::strcmp(serviceDescriptor, kServiceDescriptorForEndpoint2) == 0) { return kEndpointInfos[1].id; } return std::nullopt; } bool doesEndpointHaveService(EndpointId endpointId, const char *serviceDescriptor) override { if (serviceDescriptor == nullptr) { return false; } if (endpointId == kEndpointInfos[1].id) { return std::strcmp(serviceDescriptor, kServiceDescriptorForEndpoint2) == 0; } if (endpointId == kDynamicEndpointInfo.id) { return std::strcmp(serviceDescriptor, kServiceDescriptorForDynamicEndpoint) == 0; } return false; } void forEachService( const pw::Function &function) override { if (function( kEndpointInfos[1], ServiceInfo(kServiceDescriptorForEndpoint2, /* majorVersion= */ 1, /* minorVersion= */ 0, RpcFormat::CUSTOM))) { return; } function(kDynamicEndpointInfo, ServiceInfo(kServiceDescriptorForDynamicEndpoint, /* majorVersion= */ 1, /* minorVersion= */ 0, RpcFormat::CUSTOM)); } void onHubRegistered(const MessageHubInfo & /*info*/) override {} void onHubUnregistered(MessageHubId /*id*/) override {} void onEndpointRegistered(MessageHubId messageHubId, EndpointId endpointId) override { mRegisteredEndpoints.insert(std::make_pair(messageHubId, endpointId)); } void onEndpointUnregistered(MessageHubId messageHubId, EndpointId endpointId) override { mRegisteredEndpoints.erase(std::make_pair(messageHubId, endpointId)); } bool hasEndpointBeenRegistered(MessageHubId messageHubId, EndpointId endpointId) { return mRegisteredEndpoints.find(std::make_pair( messageHubId, endpointId)) != mRegisteredEndpoints.end(); } void pw_recycle() override { delete this; } void openSessionAndWaitForOpen( const std::function &openSession) { std::unique_lock lock(mSessionOpenedMutex); mSessionId = openSession(); mSessionOpenedCondVar.wait( lock, [this]() { return mSessionId == SESSION_ID_INVALID; }); } private: std::set> mRegisteredEndpoints; std::mutex mSessionOpenedMutex; std::condition_variable mSessionOpenedCondVar; SessionId mSessionId = SESSION_ID_INVALID; }; //! MessageHubCallback that stores the data passed to onMessageReceived and //! onSessionClosed class MessageHubCallbackStoreData : public MessageHubCallbackBase { public: MessageHubCallbackStoreData(Message *message, Session *session) : mMessage(message), mSession(session), mMessageHub(nullptr) {} bool onMessageReceived(pw::UniquePtr &&data, uint32_t messageType, uint32_t messagePermissions, const Session &session, bool sentBySessionInitiator) override { if (mMessage != nullptr) { mMessage->sender = sentBySessionInitiator ? session.initiator : session.peer; mMessage->recipient = sentBySessionInitiator ? session.peer : session.initiator; mMessage->sessionId = session.sessionId; mMessage->data = std::move(data); mMessage->messageType = messageType; mMessage->messagePermissions = messagePermissions; } return true; } void onSessionClosed(const Session &session, Reason /* reason */) override { if (mSession != nullptr) { *mSession = session; } } void onSessionOpenRequest(const Session &session) override { if (mMessageHub != nullptr) { mMessageHub->onSessionOpenComplete(session.sessionId); } } void setMessageHub(MessageRouter::MessageHub *messageHub) { mMessageHub = messageHub; } private: Message *mMessage; Session *mSession; MessageRouter::MessageHub *mMessageHub; }; // Creates a message with data from 1 to messageSize pw::UniquePtr createMessageData( pw::allocator::Allocator &allocator, size_t messageSize) { pw::UniquePtr messageData = allocator.MakeUniqueArray(messageSize); EXPECT_NE(messageData.get(), nullptr); for (size_t i = 0; i < messageSize; ++i) { messageData[i] = static_cast(i + 1); } return messageData; } class ChreMessageHubTest : public TestBase {}; TEST_F(ChreMessageHubTest, NanoappsAreEndpointsToChreMessageHub) { class App : public TestNanoapp { public: App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {} }; uint64_t appId = loadNanoapp(MakeUnique()); std::optional endpointInfoForApp = MessageRouterSingleton::get()->getEndpointInfo( EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, appId); ASSERT_TRUE(endpointInfoForApp.has_value()); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); EXPECT_EQ(endpointInfoForApp->id, nanoapp->getAppId()); EXPECT_STREQ(endpointInfoForApp->name, nanoapp->getAppName()); EXPECT_EQ(endpointInfoForApp->version, nanoapp->getAppVersion()); EXPECT_EQ(endpointInfoForApp->type, EndpointType::NANOAPP); EXPECT_EQ(endpointInfoForApp->requiredPermissions, nanoapp->getAppPermissions()); } //! Nanoapp used to test getting endpoint info class EndpointInfoTestApp : public TestNanoapp { public: EndpointInfoTestApp(const TestNanoappInfo &info) : TestNanoapp(info) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); switch (event->type) { case TEST_GET_EVENT_INFO: { for (size_t i = 0; i < kNumEndpoints; ++i) { chreMsgEndpointInfo info = {}; EXPECT_TRUE(chreMsgGetEndpointInfo(kOtherMessageHubId, kEndpointInfos[i].id, &info)); EXPECT_EQ(info.hubId, kOtherMessageHubId); EXPECT_EQ(info.endpointId, kEndpointInfos[i].id); EXPECT_EQ(info.version, kEndpointInfos[i].version); EXPECT_EQ(info.type, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .toChreEndpointType(kEndpointInfos[i].type)); EXPECT_EQ(info.requiredPermissions, kEndpointInfos[i].requiredPermissions); EXPECT_STREQ(info.name, kEndpointInfos[i].name); } triggerWait(TEST_GET_EVENT_INFO); break; } } break; } default: { break; } } } }; TEST_F(ChreMessageHubTest, NanoappGetsEndpointInfo) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_GET_ENDPOINT_INFO", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test getting endpoint info sendEventToNanoappAndWait(appId, TEST_GET_EVENT_INFO, TEST_GET_EVENT_INFO); } TEST_F(ChreMessageHubTest, MultipleNanoappsAreEndpointsToChreMessageHub) { class App : public TestNanoapp { public: App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {} }; class App2 : public TestNanoapp { public: App2() : TestNanoapp(TestNanoappInfo{.name = "TEST2", .id = 0x2}) {} }; uint64_t appId = loadNanoapp(MakeUnique()); uint64_t appId2 = loadNanoapp(MakeUnique()); constexpr size_t kNumNanoapps = 2; Nanoapp *nanoapps[kNumNanoapps] = {getNanoappByAppId(appId), getNanoappByAppId(appId2)}; ASSERT_NE(nanoapps[0], nullptr); ASSERT_NE(nanoapps[1], nullptr); DynamicVector endpointInfos; EXPECT_TRUE(MessageRouterSingleton::get()->forEachEndpointOfHub( EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, [&endpointInfos](const EndpointInfo &endpointInfo) { endpointInfos.push_back(endpointInfo); return false; })); EXPECT_EQ(endpointInfos.size(), 2); // Endpoint information should be nanoapp information for (size_t i = 0; i < kNumNanoapps; ++i) { EXPECT_EQ(endpointInfos[i].id, nanoapps[i]->getAppId()); EXPECT_STREQ(endpointInfos[i].name, nanoapps[i]->getAppName()); EXPECT_EQ(endpointInfos[i].version, nanoapps[i]->getAppVersion()); EXPECT_EQ(endpointInfos[i].type, EndpointType::NANOAPP); EXPECT_EQ(endpointInfos[i].requiredPermissions, nanoapps[i]->getAppPermissions()); } } //! Nanoapp used to test sending messages from a generic endpoint to a nanoapp class MessageTestApp : public TestNanoapp { public: MessageTestApp(bool &messageReceivedAndValidated, bool &sessionClosed, const TestNanoappInfo &info) : TestNanoapp(info), mMessageReceivedAndValidated(messageReceivedAndValidated), mSessionClosed(sessionClosed) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_MSG_FROM_ENDPOINT: { auto *message = static_cast( eventData); EXPECT_EQ(message->messageType, 1); EXPECT_EQ(message->messagePermissions, 0); EXPECT_EQ(message->messageSize, kMessageSize); auto *messageData = static_cast(message->message); for (size_t i = 0; i < kMessageSize; ++i) { EXPECT_EQ(messageData[i], static_cast(i + 1)); } mMessageReceivedAndValidated = true; triggerWait(CHRE_EVENT_MSG_FROM_ENDPOINT); break; } case CHRE_EVENT_MSG_SESSION_CLOSED: { auto *session = static_cast(eventData); EXPECT_EQ(session->hubId, kOtherMessageHubId); EXPECT_EQ(session->endpointId, kEndpointInfos[0].id); mSessionClosed = true; triggerWait(CHRE_EVENT_MSG_SESSION_CLOSED); break; } default: { break; } } } bool &mMessageReceivedAndValidated; bool &mSessionClosed; }; TEST_F(ChreMessageHubTest, SendMessageToNanoapp) { constexpr uint64_t kNanoappId = 0x1234; bool messageReceivedAndValidated = false; bool sessionClosed = false; // Create the message pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator(); pw::UniquePtr messageData = createMessageData(allocator, kMessageSize); // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( messageReceivedAndValidated, sessionClosed, TestNanoappInfo{.name = "TEST1", .id = kNanoappId})); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Open the session from the other hub:1 to the nanoapp SessionId sessionId = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &messageHub]() { sessionId = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kNanoappId); EXPECT_NE(sessionId, SESSION_ID_INVALID); return sessionId; }); // Send the message to the nanoapp ASSERT_TRUE(messageHub->sendMessage(std::move(messageData), /* messageType= */ 1, /* messagePermissions= */ 0, sessionId)); testNanoapp->wait(CHRE_EVENT_MSG_FROM_ENDPOINT); EXPECT_TRUE(messageReceivedAndValidated); // Close the session EXPECT_TRUE(messageHub->closeSession(sessionId)); testNanoapp->wait(CHRE_EVENT_MSG_SESSION_CLOSED); EXPECT_TRUE(sessionClosed); } //! Nanoapp used to test sending messages from a generic endpoint to a nanoapp //! with a different permissions set class MessagePermissionTestApp : public MessageTestApp { public: MessagePermissionTestApp(bool &messageReceivedAndValidated, bool &sessionClosed, const TestNanoappInfo &info) : MessageTestApp(messageReceivedAndValidated, sessionClosed, info) {} }; TEST_F(ChreMessageHubTest, SendMessageToNanoappPermissionFailure) { constexpr uint64_t kNanoappId = 0x1234; bool messageReceivedAndValidated = false; bool sessionClosed = false; pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator(); pw::UniquePtr messageData = allocator.MakeUniqueArray(kMessageSize); for (size_t i = 0; i < kMessageSize; ++i) { messageData[i] = static_cast(i + 1); } // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( messageReceivedAndValidated, sessionClosed, TestNanoappInfo{ .name = "TEST1", .id = kNanoappId, .perms = CHRE_PERMS_BLE})); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Open the session from the other hub:1 to the nanoapp SessionId sessionId = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &messageHub]() { sessionId = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kNanoappId); EXPECT_NE(sessionId, SESSION_ID_INVALID); return sessionId; }); // Send the message to the nanoapp ASSERT_TRUE(messageHub->sendMessage( std::move(messageData), /* messageType= */ 1, /* messagePermissions= */ CHRE_PERMS_AUDIO | CHRE_PERMS_GNSS, sessionId)); // Wait for the session to close due to the permission failure testNanoapp->wait(CHRE_EVENT_MSG_SESSION_CLOSED); EXPECT_FALSE(messageReceivedAndValidated); EXPECT_TRUE(sessionClosed); } //! Nanoapp used to test opening sessions and sending messages from a nanoapp //! to a generic endpoint class SessionAndMessageTestApp : public TestNanoapp { public: SessionAndMessageTestApp(SessionId &sessionId, const TestNanoappInfo &info) : TestNanoapp(info), mSessionId(sessionId) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_MSG_SESSION_OPENED: { // Verify the session info from the event is correct auto sessionInfo = static_cast(eventData); EXPECT_EQ(sessionInfo->hubId, mToMessageHubId); EXPECT_EQ(sessionInfo->endpointId, mToEndpointId); EXPECT_STREQ(sessionInfo->serviceDescriptor, ""); EXPECT_NE(sessionInfo->sessionId, UINT16_MAX); EXPECT_EQ(sessionInfo->reason, chreMsgEndpointReason::CHRE_MSG_ENDPOINT_REASON_UNSPECIFIED); mSessionId = sessionInfo->sessionId; // Get the session info and verify it is correct struct chreMsgSessionInfo sessionInfo2; EXPECT_TRUE(chreMsgSessionGetInfo(mSessionId, &sessionInfo2)); EXPECT_EQ(sessionInfo2.hubId, mToMessageHubId); EXPECT_EQ(sessionInfo2.endpointId, mToEndpointId); EXPECT_STREQ(sessionInfo2.serviceDescriptor, ""); EXPECT_EQ(sessionInfo2.sessionId, mSessionId); EXPECT_EQ(sessionInfo2.reason, chreMsgEndpointReason::CHRE_MSG_ENDPOINT_REASON_UNSPECIFIED); triggerWait(CHRE_EVENT_MSG_SESSION_OPENED); break; } case CHRE_EVENT_MSG_SESSION_CLOSED: { // Verify the session info from the event is correct auto sessionInfo = static_cast(eventData); EXPECT_EQ(sessionInfo->hubId, mToMessageHubId); EXPECT_EQ(sessionInfo->endpointId, mToEndpointId); EXPECT_STREQ(sessionInfo->serviceDescriptor, ""); EXPECT_EQ(sessionInfo->sessionId, mSessionId); triggerWait(CHRE_EVENT_MSG_SESSION_CLOSED); break; } case CHRE_EVENT_MSG_FROM_ENDPOINT: { auto messageData = static_cast(eventData); EXPECT_EQ(messageData->messageType, 1); EXPECT_EQ(messageData->messagePermissions, CHRE_MESSAGE_PERMISSION_NONE); EXPECT_EQ(messageData->messageSize, kMessageSize); auto message = reinterpret_cast(messageData->message); for (size_t i = 0; i < kMessageSize; ++i) { EXPECT_EQ(message[i], i + 1); } EXPECT_EQ(messageData->sessionId, mSessionId); triggerWait(CHRE_EVENT_MSG_FROM_ENDPOINT); break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); switch (event->type) { case TEST_OPEN_SESSION: { // Open the session from the nanoapp to the other hub:0 mToMessageHubId = kOtherMessageHubId; mToEndpointId = kEndpointInfos[0].id; EXPECT_TRUE( chreMsgSessionOpenAsync(mToMessageHubId, mToEndpointId, /* serviceDescriptor= */ nullptr)); mSessionId = UINT16_MAX; break; } case TEST_OPEN_DEFAULT_SESSION: { // Open the default session from the nanoapp to the other hub:1 mToMessageHubId = kOtherMessageHubId; mToEndpointId = kEndpointInfos[1].id; EXPECT_TRUE( chreMsgSessionOpenAsync(CHRE_MSG_HUB_ID_ANY, mToEndpointId, /* serviceDescriptor= */ nullptr)); mSessionId = UINT16_MAX; break; } case TEST_OPEN_SESSION_NANOAPP_TO_NANOAPP: { // Open a session from the nanoapp to itself mToMessageHubId = CHRE_PLATFORM_ID; mToEndpointId = id(); EXPECT_TRUE( chreMsgSessionOpenAsync(mToMessageHubId, mToEndpointId, /* serviceDescriptor= */ nullptr)); mSessionId = UINT16_MAX; break; } case TEST_CLOSE_SESSION: { // Close the session EXPECT_TRUE(chreMsgSessionCloseAsync(mSessionId)); break; } case TEST_CLOSE_SESSION_NON_PARTY: { ASSERT_NE(event->data, nullptr); SessionId sessionId = *static_cast(event->data); // Close the session that was opened by the other nanoapp EXPECT_FALSE(chreMsgSessionCloseAsync(sessionId)); triggerWait(TEST_CLOSE_SESSION_NON_PARTY); break; } case TEST_GET_SESSION_INFO_INVALID_SESSION: { struct chreMsgSessionInfo sessionInfo; EXPECT_NE(mSessionId, SESSION_ID_INVALID); EXPECT_FALSE(chreMsgSessionGetInfo(mSessionId, &sessionInfo)); triggerWait(TEST_GET_SESSION_INFO_INVALID_SESSION); break; } case TEST_SEND_MESSAGE: { EXPECT_TRUE(chreMsgSend( reinterpret_cast(kMessage), kMessageSize, /* messageType= */ 1, mSessionId, CHRE_MESSAGE_PERMISSION_NONE, [](void *message, size_t length) { EXPECT_EQ(message, kMessage); EXPECT_EQ(length, kMessageSize); })); triggerWait(TEST_SEND_MESSAGE); break; } case TEST_SEND_MESSAGE_NO_FREE_CALLBACK: { EXPECT_TRUE(chreMsgSend( reinterpret_cast(kMessage), kMessageSize, /* messageType= */ 1, mSessionId, CHRE_MESSAGE_PERMISSION_NONE, /* freeCallback= */ [](void * /*message*/, size_t /*length*/) { })); triggerWait(TEST_SEND_MESSAGE_NO_FREE_CALLBACK); break; } case TEST_SEND_MESSAGE_NANOAPP_TO_NANOAPP: { EXPECT_TRUE(chreMsgSend( reinterpret_cast(kMessage), kMessageSize, /* messageType= */ 1, mSessionId, CHRE_MESSAGE_PERMISSION_NONE, /* freeCallback= */ [](void * /*message*/, size_t /*length*/) { })); break; } } break; } default: { break; } } } static uint8_t kMessage[kMessageSize]; SessionId &mSessionId; MessageHubId mToMessageHubId = MESSAGE_HUB_ID_INVALID; EndpointId mToEndpointId = ENDPOINT_ID_INVALID; }; uint8_t SessionAndMessageTestApp::kMessage[kMessageSize] = {1, 2, 3, 4, 5}; TEST_F(ChreMessageHubTest, NanoappOpensSessionWithGenericEndpoint) { SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Verify the other hub received the correct session information std::optional session = messageHub->getSessionWithId(sessionId); ASSERT_TRUE(session.has_value()); EXPECT_EQ(session->sessionId, sessionId); EXPECT_EQ(session->initiator.messageHubId, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId); EXPECT_EQ(session->initiator.endpointId, nanoapp->getAppId()); EXPECT_EQ(session->peer.messageHubId, kOtherMessageHubId); EXPECT_EQ(session->peer.endpointId, kEndpointInfos[0].id); testNanoapp->doActionAndWait( [&messageHub, &session]() { messageHub->closeSession(session->sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappTriesToCloseNonPartySession) { SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Load the nanoapp uint64_t appId2 = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION_NON_PARTY", .id = 0x1235})); Nanoapp *nanoapp2 = getNanoappByAppId(appId); ASSERT_NE(nanoapp2, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Now close the session and expect failure sendEventToNanoappAndWait(appId, TEST_CLOSE_SESSION_NON_PARTY, &sessionId, TEST_CLOSE_SESSION_NON_PARTY); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappOpensDefaultSessionWithGenericEndpoint) { SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_DEFAULT_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening the default session sendEventToNanoappAndWait(appId, TEST_OPEN_DEFAULT_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Verify the other hub received the correct session information std::optional session = messageHub->getSessionWithId(sessionId); ASSERT_TRUE(session.has_value()); EXPECT_EQ(session->sessionId, sessionId); EXPECT_EQ(session->initiator.messageHubId, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId); EXPECT_EQ(session->initiator.endpointId, nanoapp->getAppId()); EXPECT_EQ(session->peer.messageHubId, kOtherMessageHubId); EXPECT_EQ(session->peer.endpointId, kEndpointInfos[1].id); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappClosesSessionWithGenericEndpoint) { Session session; SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, &session); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Now close the session sendEventToNanoappAndWait(appId, TEST_CLOSE_SESSION, CHRE_EVENT_MSG_SESSION_CLOSED); // Verify the other hub received the correct session information EXPECT_EQ(session.sessionId, sessionId); EXPECT_EQ(session.initiator.messageHubId, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId); EXPECT_EQ(session.initiator.endpointId, nanoapp->getAppId()); EXPECT_EQ(session.peer.messageHubId, kOtherMessageHubId); EXPECT_EQ(session.peer.endpointId, kEndpointInfos[0].id); } TEST_F(ChreMessageHubTest, OtherHubClosesNanoappSessionWithGenericEndpoint) { Session session; SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, &session); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Now close the session and wait for the event to be processed EXPECT_TRUE(messageHub->closeSession(sessionId)); testNanoapp->wait(CHRE_EVENT_MSG_SESSION_CLOSED); // Verify the other hub received the correct session information EXPECT_EQ(session.sessionId, sessionId); EXPECT_EQ(session.initiator.messageHubId, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId); EXPECT_EQ(session.initiator.endpointId, nanoapp->getAppId()); EXPECT_EQ(session.peer.messageHubId, kOtherMessageHubId); EXPECT_EQ(session.peer.endpointId, kEndpointInfos[0].id); } TEST_F(ChreMessageHubTest, NanoappGetSessionInfoForNonPartySession) { Session session; SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hubs pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, &session); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); pw::IntrusivePtr callback2 = pw::MakeRefCounted(/* message= */ nullptr, &session); std::optional messageHub2 = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB2", kOtherMessageHubId + 1, callback2); ASSERT_TRUE(messageHub2.has_value()); callback2->setMessageHub(&(*messageHub2)); // Open a session not involving the nanoapps sessionId = messageHub->openSession( kEndpointInfos[0].id, kOtherMessageHubId + 1, kEndpointInfos[1].id); EXPECT_NE(sessionId, SESSION_ID_INVALID); // Tell the nanoapp to get the session info for our session sendEventToNanoappAndWait(appId, TEST_GET_SESSION_INFO_INVALID_SESSION, TEST_GET_SESSION_INFO_INVALID_SESSION); } TEST_F(ChreMessageHubTest, NanoappSendsMessageToGenericEndpoint) { SessionId sessionId = SESSION_ID_INVALID; Message message; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(&message, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Send the message to the other hub and verify it was received sendEventToNanoappAndWait(appId, TEST_SEND_MESSAGE, TEST_SEND_MESSAGE); EXPECT_EQ(message.data.size(), kMessageSize); for (size_t i = 0; i < kMessageSize; ++i) { EXPECT_EQ(message.data[i], static_cast(SessionAndMessageTestApp::kMessage[i])); } EXPECT_EQ(message.messageType, 1); EXPECT_EQ(message.messagePermissions, CHRE_MESSAGE_PERMISSION_NONE); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappSendsMessageWithNoFreeCallbackToGenericEndpoint) { SessionId sessionId = SESSION_ID_INVALID; Message message; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(&message, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Send the message to the other hub and verify it was received sendEventToNanoappAndWait(appId, TEST_SEND_MESSAGE_NO_FREE_CALLBACK, TEST_SEND_MESSAGE_NO_FREE_CALLBACK); EXPECT_EQ(message.data.size(), kMessageSize); for (size_t i = 0; i < kMessageSize; ++i) { EXPECT_EQ(message.data[i], static_cast(SessionAndMessageTestApp::kMessage[i])); } EXPECT_EQ(message.messageType, 1); EXPECT_EQ(message.messagePermissions, CHRE_MESSAGE_PERMISSION_NONE); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappGetsMessageFromGenericEndpoint) { SessionId sessionId = SESSION_ID_INVALID; Message message; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_OPEN_SESSION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(&message, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test opening session sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION, CHRE_EVENT_MSG_SESSION_OPENED); // Send the message to the nanoapp and verify it was received pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator(); pw::UniquePtr messageData = createMessageData(allocator, kMessageSize); EXPECT_TRUE(messageHub->sendMessage(std::move(messageData), /* messageType= */ 1, CHRE_MESSAGE_PERMISSION_NONE, sessionId)); testNanoapp->wait(CHRE_EVENT_MSG_FROM_ENDPOINT); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, NanoappSendsMessageToNanoapp) { Session session; SessionId sessionId = SESSION_ID_INVALID; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( sessionId, TestNanoappInfo{.name = "TEST_SEND_MESSAGE_NANOAPP_TO_NANOAPP", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Test opening the session to itself sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION_NANOAPP_TO_NANOAPP, CHRE_EVENT_MSG_SESSION_OPENED); // Send the message to itself sendEventToNanoappAndWait(appId, TEST_SEND_MESSAGE_NANOAPP_TO_NANOAPP, CHRE_EVENT_MSG_FROM_ENDPOINT); // Wait for the session to be closed sendEventToNanoappAndWait(appId, TEST_CLOSE_SESSION, CHRE_EVENT_MSG_SESSION_CLOSED); } //! Nanoapp used to test opening sessions with services class ServiceSessionTestApp : public TestNanoapp { public: ServiceSessionTestApp(const TestNanoappInfo &info) : TestNanoapp(info) {} bool start() override { chreNanoappRpcService serviceInfo; serviceInfo.id = kLegacyServiceId; serviceInfo.version = kLegacyServiceVersion; EXPECT_TRUE(chrePublishRpcServices(&serviceInfo, /* numServices= */ 1)); return true; } void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_MSG_SESSION_OPENED: { // Verify the session info from the event is correct auto sessionInfo = static_cast(eventData); EXPECT_EQ(sessionInfo->hubId, kOtherMessageHubId); EXPECT_EQ(sessionInfo->reason, chreMsgEndpointReason::CHRE_MSG_ENDPOINT_REASON_UNSPECIFIED); if (std::strcmp(sessionInfo->serviceDescriptor, kServiceDescriptorForEndpoint2) == 0) { EXPECT_EQ(sessionInfo->endpointId, kEndpointInfos[1].id); EXPECT_NE(sessionInfo->sessionId, UINT16_MAX); } triggerWait(CHRE_EVENT_MSG_SESSION_OPENED); break; } case CHRE_EVENT_MSG_SESSION_CLOSED: { triggerWait(CHRE_EVENT_MSG_SESSION_CLOSED); break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); switch (event->type) { case TEST_PUBLISH_SERVICE: { chreMsgServiceInfo serviceInfo; serviceInfo.majorVersion = 1; serviceInfo.minorVersion = 0; serviceInfo.serviceDescriptor = kServiceDescriptorForNanoapp; serviceInfo.serviceFormat = CHRE_MSG_ENDPOINT_SERVICE_FORMAT_CUSTOM; EXPECT_TRUE( chreMsgPublishServices(&serviceInfo, /* numServices= */ 1)); triggerWait(TEST_PUBLISH_SERVICE); break; } case TEST_BAD_LEGACY_SERVICE_NAME: { chreMsgServiceInfo serviceInfo; serviceInfo.majorVersion = 1; serviceInfo.minorVersion = 0; serviceInfo.serviceDescriptor = kBadLegacyServiceName; serviceInfo.serviceFormat = CHRE_MSG_ENDPOINT_SERVICE_FORMAT_CUSTOM; EXPECT_FALSE( chreMsgPublishServices(&serviceInfo, /* numServices= */ 1)); triggerWait(TEST_BAD_LEGACY_SERVICE_NAME); break; } case TEST_OPEN_SESSION_WITH_SERVICE: { EXPECT_TRUE(chreMsgSessionOpenAsync( kOtherMessageHubId, kEndpointInfos[1].id, kServiceDescriptorForEndpoint2)); break; } } break; } default: { break; } } } }; TEST_F(ChreMessageHubTest, OpenSessionWithNanoappService) { constexpr uint64_t kNanoappId = 0x1234; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_OPEN_SESSION_WITH_SERVICE", .id = kNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Nanoapp publishes the service sendEventToNanoappAndWait(appId, TEST_PUBLISH_SERVICE, TEST_PUBLISH_SERVICE); // Open the session from the other hub:1 to the nanoapp with the service SessionId sessionId = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &messageHub]() { sessionId = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kNanoappId, kServiceDescriptorForNanoapp); EXPECT_NE(sessionId, SESSION_ID_INVALID); return sessionId; }); // Wait for the nanoapp to receive the session open event testNanoapp->wait(CHRE_EVENT_MSG_SESSION_OPENED); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, OpenTwoSessionsWithNanoappServiceAndNoService) { constexpr uint64_t kNanoappId = 0x1234; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_OPEN_SESSION_WITH_SERVICE", .id = kNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Nanoapp publishes the service sendEventToNanoappAndWait(appId, TEST_PUBLISH_SERVICE, TEST_PUBLISH_SERVICE); // Open the session from the other hub:1 to the nanoapp with the service SessionId sessionId = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &messageHub]() { sessionId = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kNanoappId, kServiceDescriptorForNanoapp); EXPECT_NE(sessionId, SESSION_ID_INVALID); return sessionId; }); // Wait for the nanoapp to receive the session open event testNanoapp->wait(CHRE_EVENT_MSG_SESSION_OPENED); // Open the other session from the other hub:1 to the nanoapp SessionId sessionId2 = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &sessionId2, &messageHub]() { sessionId2 = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kNanoappId); EXPECT_NE(sessionId2, SESSION_ID_INVALID); EXPECT_NE(sessionId, sessionId2); return sessionId2; }); // Wait for the nanoapp to receive the session open event testNanoapp->wait(CHRE_EVENT_MSG_SESSION_OPENED); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); testNanoapp->doActionAndWait( [&messageHub, &sessionId2]() { messageHub->closeSession(sessionId2); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, OpenSessionWithNanoappLegacyService) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_OPEN_SESSION_WITH_LEGACY_SERVICE", .id = kLegacyServiceNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Open the session from the other hub:1 to the nanoapp with the service SessionId sessionId = SESSION_ID_INVALID; callback->openSessionAndWaitForOpen([&sessionId, &messageHub]() { sessionId = messageHub->openSession(kEndpointInfos[0].id, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kLegacyServiceNanoappId, kLegacyServiceName); EXPECT_NE(sessionId, SESSION_ID_INVALID); return sessionId; }); testNanoapp->doActionAndWait( [&messageHub, &sessionId]() { messageHub->closeSession(sessionId); return true; }, CHRE_EVENT_MSG_SESSION_CLOSED); } TEST_F(ChreMessageHubTest, ForEachServiceNanoappLegacyService) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_FOR_EACH_SERVICE_LEGACY_SERVICE", .id = kLegacyServiceNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Find the service MessageRouterSingleton::get()->forEachService( [&](const MessageHubInfo &hub, const EndpointInfo &endpoint, const ServiceInfo &service) { if (hub.id == EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId) { EXPECT_EQ(endpoint.id, kLegacyServiceNanoappId); EXPECT_STREQ(service.serviceDescriptor, kLegacyServiceName); EXPECT_EQ(service.majorVersion, 1); EXPECT_EQ(service.minorVersion, 0); EXPECT_EQ(service.format, RpcFormat::PW_RPC_PROTOBUF); return true; } return false; }); } TEST_F(ChreMessageHubTest, NanoappFailsToPublishLegacyServiceInNewWay) { constexpr uint64_t kNanoappId = 0x1234; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_BAD_LEGACY_SERVICE_NAME", .id = kNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Nanoapp publishes the service sendEventToNanoappAndWait(appId, TEST_BAD_LEGACY_SERVICE_NAME, TEST_BAD_LEGACY_SERVICE_NAME); } TEST_F(ChreMessageHubTest, NanoappOpensSessionWithService) { constexpr uint64_t kNanoappId = 0x1234; // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_OPEN_SESSION_WITH_SERVICE", .id = kNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Nanoapp opens the session with the service sendEventToNanoappAndWait(appId, TEST_OPEN_SESSION_WITH_SERVICE, CHRE_EVENT_MSG_SESSION_OPENED); } TEST_F(ChreMessageHubTest, NanoappUnloadUnregistersProvidedServices) { constexpr uint64_t kNanoappId = 0x1234; // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_UNLOAD_UNREGISTERS_PROVIDED_SERVICES", .id = kNanoappId})); Nanoapp *nanoapp = getNanoappByAppId(appId); callback->setMessageHub(&(*messageHub)); // Nanoapp publishes the service sendEventToNanoappAndWait(appId, TEST_PUBLISH_SERVICE, TEST_PUBLISH_SERVICE); // Get the endpoint ID for the service std::optional endpoint = MessageRouterSingleton::get()->getEndpointForService( EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kServiceDescriptorForNanoapp); EXPECT_TRUE(endpoint.has_value()); EXPECT_EQ(endpoint->messageHubId, EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId); EXPECT_EQ(endpoint->endpointId, kNanoappId); // Unload the nanoapp unloadNanoapp(appId); // Load another nanoapp. This forces this thread to wait for the finish // load nanoapp event to process, which is after the cleanup event. loadNanoapp(MakeUnique()); // The service should be gone endpoint = MessageRouterSingleton::get()->getEndpointForService( EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, kServiceDescriptorForNanoapp); EXPECT_FALSE(endpoint.has_value()); } //! Nanoapp used to test endpoint registration and ready events class EndpointRegistrationTestApp : public TestNanoapp { public: EndpointRegistrationTestApp(const TestNanoappInfo &info) : TestNanoapp(info) {} void handleEvent(uint32_t, uint16_t eventType, const void *eventData) override { switch (eventType) { case CHRE_EVENT_MSG_ENDPOINT_READY: { auto event = static_cast(eventData); EXPECT_EQ(event->hubId, kOtherMessageHubId); EXPECT_EQ(event->endpointId, mEndpointId); triggerWait(CHRE_EVENT_MSG_ENDPOINT_READY); break; } case CHRE_EVENT_MSG_SERVICE_READY: { auto event = static_cast(eventData); EXPECT_EQ(event->hubId, kOtherMessageHubId); EXPECT_EQ(event->endpointId, kDynamicEndpointInfo.id); EXPECT_STREQ(event->serviceDescriptor, kServiceDescriptorForDynamicEndpoint); triggerWait(CHRE_EVENT_MSG_SERVICE_READY); break; } case CHRE_EVENT_TEST_EVENT: { auto event = static_cast(eventData); switch (event->type) { case TEST_SUBSCRIBE_TO_READY_EVENT: { mEndpointId = kDynamicEndpointInfo.id; EXPECT_TRUE(chreMsgConfigureEndpointReadyEvents( kOtherMessageHubId, mEndpointId, /* enable= */ true)); triggerWait(TEST_SUBSCRIBE_TO_READY_EVENT); break; } case TEST_SUBSCRIBE_TO_READY_EVENT_ALREADY_EXISTS: { mEndpointId = kEndpointInfos[1].id; EXPECT_TRUE(chreMsgConfigureEndpointReadyEvents( kOtherMessageHubId, mEndpointId, /* enable= */ true)); break; } case TEST_UNSUBSCRIBE_FROM_READY_EVENT: { EXPECT_TRUE(chreMsgConfigureEndpointReadyEvents( kOtherMessageHubId, mEndpointId, /* enable= */ false)); triggerWait(TEST_UNSUBSCRIBE_FROM_READY_EVENT); break; } case TEST_SUBSCRIBE_TO_SERVICE_READY_EVENT: { EXPECT_TRUE(chreMsgConfigureServiceReadyEvents( kOtherMessageHubId, kServiceDescriptorForDynamicEndpoint, /* enable= */ true)); triggerWait(TEST_SUBSCRIBE_TO_SERVICE_READY_EVENT); break; } case TEST_UNSUBSCRIBE_FROM_SERVICE_READY_EVENT: { EXPECT_TRUE(chreMsgConfigureServiceReadyEvents( kOtherMessageHubId, kServiceDescriptorForDynamicEndpoint, /* enable= */ false)); triggerWait(TEST_UNSUBSCRIBE_FROM_SERVICE_READY_EVENT); break; } } break; } default: { break; } } } EndpointId mEndpointId = ENDPOINT_ID_INVALID; }; TEST_F(ChreMessageHubTest, NanoappSubscribesToEndpointReadyEvent) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_ENDPOINT_READY_EVENT", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test subscribing to the ready event sendEventToNanoappAndWait(appId, TEST_SUBSCRIBE_TO_READY_EVENT, TEST_SUBSCRIBE_TO_READY_EVENT); // Register the endpoint and wait for the ready event EXPECT_TRUE(messageHub->registerEndpoint(kDynamicEndpointInfo.id)); testNanoapp->wait(CHRE_EVENT_MSG_ENDPOINT_READY); // Unsubscribe from the ready event sendEventToNanoappAndWait(appId, TEST_UNSUBSCRIBE_FROM_READY_EVENT, TEST_UNSUBSCRIBE_FROM_READY_EVENT); } TEST_F(ChreMessageHubTest, NanoappSubscribesToEndpointReadyEventAlreadyExists) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_ENDPOINT_READY_EVENT", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test subscribing to the ready event - endpoint should already exist sendEventToNanoappAndWait(appId, TEST_SUBSCRIBE_TO_READY_EVENT_ALREADY_EXISTS, CHRE_EVENT_MSG_ENDPOINT_READY); // Unsubscribe from the ready event sendEventToNanoappAndWait(appId, TEST_UNSUBSCRIBE_FROM_READY_EVENT, TEST_UNSUBSCRIBE_FROM_READY_EVENT); } TEST_F(ChreMessageHubTest, NanoappSubscribesToServiceReadyEvent) { // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_SERVICE_READY_EVENT", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); TestNanoapp *testNanoapp = queryNanoapp(appId); ASSERT_NE(testNanoapp, nullptr); // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Test subscribing to the service ready event sendEventToNanoappAndWait(appId, TEST_SUBSCRIBE_TO_SERVICE_READY_EVENT, TEST_SUBSCRIBE_TO_SERVICE_READY_EVENT); // Register the endpoint and wait for the service ready event EXPECT_TRUE(messageHub->registerEndpoint(kDynamicEndpointInfo.id)); testNanoapp->wait(CHRE_EVENT_MSG_SERVICE_READY); // Unsubscribe from the service ready event sendEventToNanoappAndWait(appId, TEST_UNSUBSCRIBE_FROM_SERVICE_READY_EVENT, TEST_UNSUBSCRIBE_FROM_SERVICE_READY_EVENT); } TEST_F(ChreMessageHubTest, NanoappLoadAndUnloadAreRegisteredAndUnregistered) { // Create the other hub pw::IntrusivePtr callback = pw::MakeRefCounted(/* message= */ nullptr, /* session= */ nullptr); std::optional messageHub = MessageRouterSingleton::get()->registerMessageHub( "OTHER_TEST_HUB", kOtherMessageHubId, callback); ASSERT_TRUE(messageHub.has_value()); callback->setMessageHub(&(*messageHub)); // Load the nanoapp uint64_t appId = loadNanoapp(MakeUnique( TestNanoappInfo{.name = "TEST_NANOAPP_REGISTRATION", .id = 0x1234})); Nanoapp *nanoapp = getNanoappByAppId(appId); ASSERT_NE(nanoapp, nullptr); // The nanoapp should be registered as an endpoint EXPECT_TRUE( callback->hasEndpointBeenRegistered(EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, appId)); // Unload the nanoapp unloadNanoapp(appId); // The nanoapp should be unregistered as an endpoint EXPECT_FALSE( callback->hasEndpointBeenRegistered(EventLoopManagerSingleton::get() ->getChreMessageHubManager() .kChreMessageHubId, appId)); } } // namespace } // namespace chre::message