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