xref: /aosp_15_r20/system/chre/test/simulation/chre_message_hub_test.cc (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cstdint>
18 #include <cstring>
19 #include <optional>
20 
21 #include "chre/core/event_loop_manager.h"
22 #include "chre/util/dynamic_vector.h"
23 #include "chre/util/system/message_common.h"
24 #include "chre/util/system/message_router.h"
25 #include "chre/util/system/napp_permissions.h"
26 #include "chre_api/chre/event.h"
27 
28 #include "pw_allocator/allocator.h"
29 #include "pw_allocator/libc_allocator.h"
30 #include "pw_allocator/unique_ptr.h"
31 #include "pw_function/function.h"
32 
33 #include "gtest/gtest.h"
34 #include "inc/test_util.h"
35 #include "test_base.h"
36 #include "test_util.h"
37 
38 namespace chre::message {
39 namespace {
40 
41 constexpr size_t kNumEndpoints = 3;
42 constexpr size_t kMessageSize = 5;
43 constexpr MessageHubId kOtherMessageHubId = 0xDEADBEEFBEEFDEAD;
44 
45 EndpointInfo kEndpointInfos[kNumEndpoints] = {
46     EndpointInfo(/* id= */ 1, /* name= */ "endpoint1", /* version= */ 1,
47                  EndpointType::NANOAPP, CHRE_MESSAGE_PERMISSION_NONE),
48     EndpointInfo(/* id= */ 2, /* name= */ "endpoint2", /* version= */ 10,
49                  EndpointType::HOST_NATIVE, CHRE_MESSAGE_PERMISSION_BLE),
50     EndpointInfo(/* id= */ 3, /* name= */ "endpoint3", /* version= */ 100,
51                  EndpointType::GENERIC, CHRE_MESSAGE_PERMISSION_AUDIO)};
52 
53 //! Base class for MessageHubCallbacks used in tests
54 class MessageHubCallbackBase : public MessageRouter::MessageHubCallback {
55  public:
forEachEndpoint(const pw::Function<bool (const EndpointInfo &)> & function)56   void forEachEndpoint(
57       const pw::Function<bool(const EndpointInfo &)> &function) override {
58     for (const EndpointInfo &endpointInfo : kEndpointInfos) {
59       if (function(endpointInfo)) {
60         return;
61       }
62     }
63   }
64 
getEndpointInfo(EndpointId endpointId)65   std::optional<EndpointInfo> getEndpointInfo(EndpointId endpointId) override {
66     for (const EndpointInfo &endpointInfo : kEndpointInfos) {
67       if (endpointInfo.id == endpointId) {
68         return endpointInfo;
69       }
70     }
71     return std::nullopt;
72   }
73 };
74 
75 //! MessageHubCallback that stores the data passed to onMessageReceived and
76 //! onSessionClosed
77 class MessageHubCallbackStoreData : public MessageHubCallbackBase {
78  public:
MessageHubCallbackStoreData(Message * message,Session * session)79   MessageHubCallbackStoreData(Message *message, Session *session)
80       : mMessage(message), mSession(session) {}
81 
onMessageReceived(pw::UniquePtr<std::byte[]> && data,size_t length,uint32_t messageType,uint32_t messagePermissions,const Session & session,bool sentBySessionInitiator)82   bool onMessageReceived(pw::UniquePtr<std::byte[]> &&data, size_t length,
83                          uint32_t messageType, uint32_t messagePermissions,
84                          const Session &session,
85                          bool sentBySessionInitiator) override {
86     if (mMessage != nullptr) {
87       mMessage->sender =
88           sentBySessionInitiator ? session.initiator : session.peer;
89       mMessage->recipient =
90           sentBySessionInitiator ? session.peer : session.initiator;
91       mMessage->sessionId = session.sessionId;
92       mMessage->data = std::move(data);
93       mMessage->length = length;
94       mMessage->messageType = messageType;
95       mMessage->messagePermissions = messagePermissions;
96     }
97     return true;
98   }
99 
onSessionClosed(const Session & session)100   void onSessionClosed(const Session &session) override {
101     if (mSession != nullptr) {
102       *mSession = session;
103     }
104   }
105 
106  private:
107   Message *mMessage;
108   Session *mSession;
109 };
110 
111 class ChreMessageHubTest : public TestBase {};
112 
TEST_F(ChreMessageHubTest,MessageRouterNanoappsAreEndpointsToChreMessageHub)113 TEST_F(ChreMessageHubTest, MessageRouterNanoappsAreEndpointsToChreMessageHub) {
114   class App : public TestNanoapp {
115    public:
116     App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {}
117   };
118 
119   uint64_t appId = loadNanoapp(MakeUnique<App>());
120 
121   std::optional<EndpointInfo> endpointInfoForApp =
122       MessageRouterSingleton::get()->getEndpointInfo(
123           EventLoopManagerSingleton::get()
124               ->getChreMessageHubManager()
125               .kChreMessageHubId,
126           appId);
127   ASSERT_TRUE(endpointInfoForApp.has_value());
128 
129   Nanoapp *nanoapp = getNanoappByAppId(appId);
130   ASSERT_NE(nanoapp, nullptr);
131 
132   EXPECT_EQ(endpointInfoForApp->id, nanoapp->getAppId());
133   EXPECT_STREQ(endpointInfoForApp->name, nanoapp->getAppName());
134   EXPECT_EQ(endpointInfoForApp->version, nanoapp->getAppVersion());
135   EXPECT_EQ(endpointInfoForApp->type, EndpointType::NANOAPP);
136   EXPECT_EQ(endpointInfoForApp->requiredPermissions,
137             nanoapp->getAppPermissions());
138 }
139 
TEST_F(ChreMessageHubTest,MessageRouterMultipleNanoappsAreEndpointsToChreMessageHub)140 TEST_F(ChreMessageHubTest,
141        MessageRouterMultipleNanoappsAreEndpointsToChreMessageHub) {
142   class App : public TestNanoapp {
143    public:
144     App() : TestNanoapp(TestNanoappInfo{.name = "TEST1", .id = 0x1234}) {}
145   };
146 
147   class App2 : public TestNanoapp {
148    public:
149     App2() : TestNanoapp(TestNanoappInfo{.name = "TEST2", .id = 0x2}) {}
150   };
151 
152   uint64_t appId = loadNanoapp(MakeUnique<App>());
153   uint64_t appId2 = loadNanoapp(MakeUnique<App2>());
154   constexpr size_t kNumNanoapps = 2;
155   Nanoapp *nanoapps[kNumNanoapps] = {getNanoappByAppId(appId),
156                                      getNanoappByAppId(appId2)};
157   ASSERT_NE(nanoapps[0], nullptr);
158   ASSERT_NE(nanoapps[1], nullptr);
159 
160   DynamicVector<EndpointInfo> endpointInfos;
161   EXPECT_TRUE(MessageRouterSingleton::get()->forEachEndpointOfHub(
162       EventLoopManagerSingleton::get()
163            ->getChreMessageHubManager()
164            .kChreMessageHubId,
165       [&endpointInfos](const EndpointInfo &endpointInfo) {
166         endpointInfos.push_back(endpointInfo);
167         return false;
168       }));
169   EXPECT_EQ(endpointInfos.size(), 2);
170 
171   // Endpoint information should be nanoapp information
172   for (size_t i = 0; i < kNumNanoapps; ++i) {
173     EXPECT_EQ(endpointInfos[i].id, nanoapps[i]->getAppId());
174     EXPECT_STREQ(endpointInfos[i].name, nanoapps[i]->getAppName());
175     EXPECT_EQ(endpointInfos[i].version, nanoapps[i]->getAppVersion());
176     EXPECT_EQ(endpointInfos[i].type, EndpointType::NANOAPP);
177     EXPECT_EQ(endpointInfos[i].requiredPermissions,
178               nanoapps[i]->getAppPermissions());
179   }
180 }
181 
182 //! Nanoapp used to test sending messages from a generic endpoint to a nanoapp
183 class MessageTestApp : public TestNanoapp {
184  public:
MessageTestApp(std::mutex & mutex,std::condition_variable & condVar,bool & messageReceivedAndValidated,bool & sessionClosed,const TestNanoappInfo & info)185   MessageTestApp(std::mutex &mutex, std::condition_variable &condVar,
186                  bool &messageReceivedAndValidated, bool &sessionClosed,
187                  const TestNanoappInfo &info)
188       : TestNanoapp(info),
189         mMutex(mutex),
190         mCondVar(condVar),
191         mMessageReceivedAndValidated(messageReceivedAndValidated),
192         mSessionClosed(sessionClosed) {}
193 
handleEvent(uint32_t,uint16_t eventType,const void * eventData)194   void handleEvent(uint32_t, uint16_t eventType,
195                    const void *eventData) override {
196     switch (eventType) {
197       case CHRE_EVENT_MESSAGE_FROM_ENDPOINT: {
198         {
199           std::unique_lock<std::mutex> lock(mMutex);
200           auto *message =
201               static_cast<const struct chreMessageFromEndpointData *>(
202                   eventData);
203           EXPECT_EQ(message->messageType, 1);
204           EXPECT_EQ(message->messagePermissions, 0);
205           EXPECT_EQ(message->messageSize, kMessageSize);
206 
207           auto *messageData = static_cast<const std::byte *>(message->message);
208           for (size_t i = 0; i < kMessageSize; ++i) {
209             EXPECT_EQ(messageData[i], static_cast<std::byte>(i + 1));
210           }
211           mMessageReceivedAndValidated = true;
212         }
213         mCondVar.notify_one();
214         break;
215       }
216       case CHRE_EVENT_ENDPOINT_SESSION_CLOSED: {
217         {
218           std::unique_lock<std::mutex> lock(mMutex);
219           auto *session =
220               static_cast<const struct chreEndpointSessionClosedData *>(
221                   eventData);
222           EXPECT_EQ(session->hubId, kOtherMessageHubId);
223           EXPECT_EQ(session->endpointId, kEndpointInfos[0].id);
224           mSessionClosed = true;
225         }
226         mCondVar.notify_one();
227         break;
228       }
229       default: {
230         break;
231       }
232     }
233   }
234 
235   std::mutex &mMutex;
236   std::condition_variable &mCondVar;
237   bool &mMessageReceivedAndValidated;
238   bool &mSessionClosed;
239 };
240 
TEST_F(ChreMessageHubTest,MessageRouterSendMessageToNanoapp)241 TEST_F(ChreMessageHubTest, MessageRouterSendMessageToNanoapp) {
242   constexpr uint64_t kNanoappId = 0x1234;
243   std::mutex mutex;
244   std::condition_variable condVar;
245   bool messageReceivedAndValidated = false;
246   bool sessionClosed = false;
247 
248   pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator();
249   pw::UniquePtr<std::byte[]> messageData =
250       allocator.MakeUniqueArray<std::byte>(kMessageSize);
251   for (size_t i = 0; i < kMessageSize; ++i) {
252     messageData[i] = static_cast<std::byte>(i + 1);
253   }
254 
255   // Load the nanoapp
256   uint64_t appId = loadNanoapp(MakeUnique<MessageTestApp>(
257       mutex, condVar, messageReceivedAndValidated, sessionClosed,
258       TestNanoappInfo{.name = "TEST1", .id = kNanoappId}));
259 
260   // Create the other hub
261   MessageHubCallbackStoreData callback(/* message= */ nullptr,
262                                        /* session= */ nullptr);
263   std::optional<MessageRouter::MessageHub> messageHub =
264       MessageRouterSingleton::get()->registerMessageHub(
265           "OTHER_TEST_HUB", kOtherMessageHubId, callback);
266   ASSERT_TRUE(messageHub.has_value());
267 
268   // Open the session from the other hub:1 to the nanoapp
269   SessionId sessionId =
270       messageHub->openSession(kEndpointInfos[0].id,
271                               EventLoopManagerSingleton::get()
272                                    ->getChreMessageHubManager()
273                                    .kChreMessageHubId,
274                               kNanoappId);
275   EXPECT_NE(sessionId, SESSION_ID_INVALID);
276 
277   // Send the message to the nanoapp
278   std::unique_lock<std::mutex> lock(mutex);
279   ASSERT_TRUE(messageHub->sendMessage(std::move(messageData), kMessageSize,
280                                       /* messageType= */ 1,
281                                       /* messagePermissions= */ 0, sessionId));
282   condVar.wait(lock);
283   EXPECT_TRUE(messageReceivedAndValidated);
284 
285   // Close the session
286   EXPECT_TRUE(messageHub->closeSession(sessionId));
287   condVar.wait(lock);
288   EXPECT_TRUE(sessionClosed);
289 }
290 
291 class MessagePermissionTestApp : public MessageTestApp {
292  public:
MessagePermissionTestApp(std::mutex & mutex,std::condition_variable & condVar,bool & messageReceivedAndValidated,bool & sessionClosed,const TestNanoappInfo & info)293   MessagePermissionTestApp(std::mutex &mutex, std::condition_variable &condVar,
294                            bool &messageReceivedAndValidated,
295                            bool &sessionClosed, const TestNanoappInfo &info)
296       : MessageTestApp(mutex, condVar, messageReceivedAndValidated,
297                        sessionClosed, info) {}
298 };
299 
TEST_F(ChreMessageHubTest,MessageRouterSendMessageToNanoappPermissionFailure)300 TEST_F(ChreMessageHubTest, MessageRouterSendMessageToNanoappPermissionFailure) {
301   CREATE_CHRE_TEST_EVENT(TRIGGER_COND_VAR, 0);
302 
303   constexpr uint64_t kNanoappId = 0x1234;
304   std::mutex mutex;
305   std::condition_variable condVar;
306   bool messageReceivedAndValidated = false;
307   bool sessionClosed = false;
308 
309   pw::allocator::LibCAllocator allocator = pw::allocator::GetLibCAllocator();
310   pw::UniquePtr<std::byte[]> messageData =
311       allocator.MakeUniqueArray<std::byte>(kMessageSize);
312   for (size_t i = 0; i < kMessageSize; ++i) {
313     messageData[i] = static_cast<std::byte>(i + 1);
314   }
315 
316   // Load the nanoapp
317   uint64_t appId = loadNanoapp(MakeUnique<MessagePermissionTestApp>(
318       mutex, condVar, messageReceivedAndValidated, sessionClosed,
319       TestNanoappInfo{
320           .name = "TEST1", .id = kNanoappId, .perms = CHRE_PERMS_BLE}));
321 
322   // Create the other hub
323   MessageHubCallbackStoreData callback(/* message= */ nullptr,
324                                        /* session= */ nullptr);
325   std::optional<MessageRouter::MessageHub> messageHub =
326       MessageRouterSingleton::get()->registerMessageHub(
327           "OTHER_TEST_HUB", kOtherMessageHubId, callback);
328   ASSERT_TRUE(messageHub.has_value());
329 
330   // Open the session from the other hub:1 to the nanoapp
331   SessionId sessionId =
332       messageHub->openSession(kEndpointInfos[0].id,
333                               EventLoopManagerSingleton::get()
334                                    ->getChreMessageHubManager()
335                                    .kChreMessageHubId,
336                               kNanoappId);
337   EXPECT_NE(sessionId, SESSION_ID_INVALID);
338 
339   // Send the message to the nanoapp
340   std::unique_lock<std::mutex> lock(mutex);
341   ASSERT_TRUE(messageHub->sendMessage(
342       std::move(messageData), kMessageSize,
343       /* messageType= */ 1,
344       /* messagePermissions= */ CHRE_PERMS_AUDIO | CHRE_PERMS_GNSS, sessionId));
345 
346   // Send the trigger cond var event, which will be handled after the
347   // CHRE message from endpoint event (if it is sent erroneously). If the
348   // message event is not sent, this event will unlock the condition variable.
349   // If the message event is sent, the condition variable will be unlocked
350   // after the message event is processed, setting the
351   // messageReceivedAndValidated variable to true, which will fail the test.
352   sendEventToNanoapp(appId, TRIGGER_COND_VAR);
353   condVar.wait(lock);
354   EXPECT_FALSE(messageReceivedAndValidated);
355   EXPECT_TRUE(sessionClosed);
356 }
357 
358 }  // namespace
359 }  // namespace chre::message
360