xref: /aosp_15_r20/system/chre/chpp/test/app_notification_test.cpp (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2023 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 <gtest/gtest.h>
18 
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23 #include <cstring>
24 #include <thread>
25 
26 #include "chpp/app.h"
27 #include "chpp/clients.h"
28 #include "chpp/clients/discovery.h"
29 #include "chpp/macros.h"
30 #include "chpp/notifier.h"
31 #include "chpp/platform/platform_link.h"
32 #include "chpp/platform/utils.h"
33 #include "chpp/services.h"
34 #include "chpp/transport.h"
35 #include "chre/util/enum.h"
36 #include "chre/util/time.h"
37 
38 namespace chre {
39 
40 namespace {
41 
42 constexpr uint64_t kResetWaitTimeMs = 5000;
43 constexpr uint64_t kDiscoveryWaitTimeMs = 5000;
44 
workThread(void * transportState)45 void *workThread(void *transportState) {
46   ChppTransportState *state = static_cast<ChppTransportState *>(transportState);
47 
48   auto linkContext =
49       static_cast<struct ChppLinuxLinkState *>(state->linkContext);
50 
51   pthread_setname_np(pthread_self(), linkContext->workThreadName);
52 
53   chppWorkThreadStart(state);
54 
55   return nullptr;
56 }
57 
58 #define TEST_UUID                                                           \
59   {                                                                         \
60     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
61         0x00, 0x00, 0x00, 0x12                                              \
62   }
63 
64 enum class Commands : uint16_t {
65   kServiceNotification,
66   kClientNotification,
67 };
68 
69 constexpr uint16_t kNumCommands = 1;
70 
71 struct ClientState {
72   struct ChppEndpointState chppClientState;
73   struct ChppOutgoingRequestState outReqStates[kNumCommands];
74   bool serviceNotificationStatus;
75   struct ChppNotifier notifier;
76 };
77 
78 bool clientInit(void *clientState, uint8_t handle,
79                 struct ChppVersion serviceVersion);
80 void clientDeinit(void *clientState);
81 enum ChppAppErrorCode clientDispatchNotification(void *clientState,
82                                                  uint8_t *buf, size_t len);
83 constexpr struct ChppClient kClient = {
84     .descriptor.uuid = TEST_UUID,
85     .descriptor.version.major = 1,
86     .descriptor.version.minor = 0,
87     .descriptor.version.patch = 0,
88     .resetNotifierFunctionPtr = nullptr,
89     .matchNotifierFunctionPtr = nullptr,
90     .responseDispatchFunctionPtr = nullptr,
91     .notificationDispatchFunctionPtr = &clientDispatchNotification,
92     .initFunctionPtr = &clientInit,
93     .deinitFunctionPtr = &clientDeinit,
94     .outReqCount = kNumCommands,
95     .minLength = sizeof(struct ChppAppHeader),
96 };
97 
98 // Called when a notification from a service is received.
clientDispatchNotification(void * clientState,uint8_t * buf,size_t len)99 enum ChppAppErrorCode clientDispatchNotification(void *clientState,
100                                                  uint8_t *buf, size_t len) {
101   auto state = static_cast<struct ClientState *>(clientState);
102 
103   // The response is composed of the app header only.
104   if (len != sizeof(ChppAppHeader)) {
105     return CHPP_APP_ERROR_NONE;
106   }
107 
108   auto notification = reinterpret_cast<struct ChppAppHeader *>(buf);
109 
110   switch (notification->command) {
111     case asBaseType(Commands::kServiceNotification):
112       state->serviceNotificationStatus =
113           notification->error == CHPP_APP_ERROR_NONE;
114       chppNotifierSignal(&state->notifier, 1 /*signal*/);
115       return CHPP_APP_ERROR_NONE;
116 
117     default:
118       return CHPP_APP_ERROR_NONE;
119   }
120 }
121 
clientInit(void * clientState,uint8_t handle,struct ChppVersion serviceVersion)122 bool clientInit(void *clientState, uint8_t handle,
123                 struct ChppVersion serviceVersion) {
124   UNUSED_VAR(serviceVersion);
125   auto state = static_cast<struct ClientState *>(clientState);
126   state->chppClientState.openState = CHPP_OPEN_STATE_OPENED;
127   chppClientInit(&state->chppClientState, handle);
128   return true;
129 }
130 
clientDeinit(void * clientState)131 void clientDeinit(void *clientState) {
132   auto state = static_cast<struct ClientState *>(clientState);
133   chppClientDeinit(&state->chppClientState);
134   state->chppClientState.openState = CHPP_OPEN_STATE_CLOSED;
135 }
136 
137 // Service
138 struct ServiceState {
139   struct ChppEndpointState chppServiceState;
140   struct ChppIncomingRequestState inReqStates[kNumCommands];
141   bool clientNotificationStatus;
142   struct ChppNotifier notifier;
143 };
144 
145 // Called when a notification from a client is received.
serviceDispatchNotification(void * serviceState,uint8_t * buf,size_t len)146 enum ChppAppErrorCode serviceDispatchNotification(void *serviceState,
147                                                   uint8_t *buf, size_t len) {
148   auto state = static_cast<struct ServiceState *>(serviceState);
149 
150   // The response is composed of the app header only.
151   if (len != sizeof(ChppAppHeader)) {
152     return CHPP_APP_ERROR_NONE;
153   }
154 
155   auto notification = reinterpret_cast<struct ChppAppHeader *>(buf);
156 
157   switch (notification->command) {
158     case asBaseType(Commands::kClientNotification):
159       state->clientNotificationStatus =
160           notification->error == CHPP_APP_ERROR_NONE;
161       chppNotifierSignal(&state->notifier, 1 /*signal*/);
162       return CHPP_APP_ERROR_NONE;
163 
164     default:
165       return CHPP_APP_ERROR_NONE;
166   }
167 }
168 
169 const struct ChppService kService = {
170     .descriptor.uuid = TEST_UUID,
171     .descriptor.name = "Test",
172     .descriptor.version.major = 1,
173     .descriptor.version.minor = 0,
174     .descriptor.version.patch = 0,
175     .resetNotifierFunctionPtr = nullptr,
176     .requestDispatchFunctionPtr = nullptr,
177     .notificationDispatchFunctionPtr = &serviceDispatchNotification,
178     .minLength = sizeof(struct ChppAppHeader),
179 };
180 
181 // Test notifications.
182 class AppNotificationTest : public testing::Test {
183  protected:
SetUp()184   void SetUp() {
185     chppClearTotalAllocBytes();
186     chppNotifierInit(&mClientState.notifier);
187     chppNotifierInit(&mServiceState.notifier);
188     memset(&mClientLinkContext, 0, sizeof(mClientLinkContext));
189     memset(&mServiceLinkContext, 0, sizeof(mServiceLinkContext));
190 
191     mServiceLinkContext.linkThreadName = "Host Link";
192     mServiceLinkContext.workThreadName = "Host worker";
193     mServiceLinkContext.isLinkActive = true;
194     mServiceLinkContext.remoteLinkState = &mClientLinkContext;
195     mServiceLinkContext.rxInRemoteEndpointWorker = false;
196 
197     mClientLinkContext.linkThreadName = "CHRE Link";
198     mClientLinkContext.workThreadName = "CHRE worker";
199     mClientLinkContext.isLinkActive = true;
200     mClientLinkContext.remoteLinkState = &mServiceLinkContext;
201     mClientLinkContext.rxInRemoteEndpointWorker = false;
202 
203     // No default clients/services.
204     struct ChppClientServiceSet set;
205     memset(&set, 0, sizeof(set));
206 
207     const struct ChppLinkApi *linkApi = getLinuxLinkApi();
208 
209     // Init client side.
210     chppTransportInit(&mClientTransportContext, &mClientAppContext,
211                       &mClientLinkContext, linkApi);
212     chppAppInitWithClientServiceSet(&mClientAppContext,
213                                     &mClientTransportContext, set);
214 
215     // Init service side.
216     chppTransportInit(&mServiceTransportContext, &mServiceAppContext,
217                       &mServiceLinkContext, linkApi);
218     chppAppInitWithClientServiceSet(&mServiceAppContext,
219                                     &mServiceTransportContext, set);
220 
221     BringUpClient();
222     std::this_thread::sleep_for(std::chrono::milliseconds(450));
223     BringUpService();
224     mClientLinkContext.linkEstablished = true;
225     mServiceLinkContext.linkEstablished = true;
226 
227     EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportContext,
228                                                   kResetWaitTimeMs));
229     EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportContext,
230                                                   kResetWaitTimeMs));
231     EXPECT_TRUE(
232         chppWaitForDiscoveryComplete(&mClientAppContext, kDiscoveryWaitTimeMs));
233     EXPECT_TRUE(chppWaitForDiscoveryComplete(&mServiceAppContext,
234                                              kDiscoveryWaitTimeMs));
235   }
236 
BringUpClient()237   void BringUpClient() {
238     memset(&mClientState, 0, sizeof(mClientState));
239     chppRegisterClient(&mClientAppContext, &mClientState,
240                        &mClientState.chppClientState,
241                        &mClientState.outReqStates[0], &kClient);
242 
243     pthread_create(&mClientWorkThread, NULL, workThread,
244                    &mClientTransportContext);
245   }
246 
BringUpService()247   void BringUpService() {
248     memset(&mServiceState, 0, sizeof(mServiceState));
249     chppRegisterService(&mServiceAppContext, &mServiceState,
250                         &mServiceState.chppServiceState, NULL /*outReqStates*/,
251                         &kService);
252 
253     pthread_create(&mServiceWorkThread, NULL, workThread,
254                    &mServiceTransportContext);
255   }
256 
TearDown()257   void TearDown() {
258     chppNotifierDeinit(&mClientState.notifier);
259     chppNotifierDeinit(&mServiceState.notifier);
260     chppWorkThreadStop(&mClientTransportContext);
261     chppWorkThreadStop(&mServiceTransportContext);
262     pthread_join(mClientWorkThread, NULL);
263     pthread_join(mServiceWorkThread, NULL);
264 
265     // Deinit client side.
266     chppAppDeinit(&mClientAppContext);
267     chppTransportDeinit(&mClientTransportContext);
268 
269     // Deinit service side.
270     chppAppDeinit(&mServiceAppContext);
271     chppTransportDeinit(&mServiceTransportContext);
272 
273     EXPECT_EQ(chppGetTotalAllocBytes(), 0);
274   }
275 
276   // Client side.
277   ChppLinuxLinkState mClientLinkContext = {};
278   ChppTransportState mClientTransportContext = {};
279   ChppAppState mClientAppContext = {};
280   pthread_t mClientWorkThread;
281   ClientState mClientState;
282 
283   // Service side
284   ChppLinuxLinkState mServiceLinkContext = {};
285   ChppTransportState mServiceTransportContext = {};
286   ChppAppState mServiceAppContext = {};
287   pthread_t mServiceWorkThread;
288   ServiceState mServiceState;
289 };
290 
TEST_F(AppNotificationTest,serviceSendANotificationToClient)291 TEST_F(AppNotificationTest, serviceSendANotificationToClient) {
292   // Send a notification.
293   constexpr size_t notificationLen = sizeof(struct ChppAppHeader);
294 
295   struct ChppAppHeader *notification =
296       chppAllocServiceNotification(notificationLen);
297   ASSERT_NE(notification, nullptr);
298   notification->command = asBaseType(Commands::kServiceNotification);
299   notification->handle = mServiceState.chppServiceState.handle;
300 
301   mClientState.serviceNotificationStatus = false;
302 
303   EXPECT_TRUE(chppEnqueueTxDatagramOrFail(&mServiceTransportContext,
304                                           notification, notificationLen));
305 
306   chppNotifierWait(&mClientState.notifier);
307 
308   EXPECT_TRUE(mClientState.serviceNotificationStatus);
309 }
310 
TEST_F(AppNotificationTest,clientSendANotificationToService)311 TEST_F(AppNotificationTest, clientSendANotificationToService) {
312   // Send a notification.
313   constexpr size_t notificationLen = sizeof(struct ChppAppHeader);
314 
315   struct ChppAppHeader *notification =
316       chppAllocClientNotification(notificationLen);
317   ASSERT_NE(notification, nullptr);
318   notification->command = asBaseType(Commands::kClientNotification);
319   notification->handle = mClientState.chppClientState.handle;
320 
321   mServiceState.clientNotificationStatus = false;
322 
323   EXPECT_TRUE(chppEnqueueTxDatagramOrFail(&mClientTransportContext,
324                                           notification, notificationLen));
325 
326   chppNotifierWait(&mServiceState.notifier);
327 
328   EXPECT_TRUE(mServiceState.clientNotificationStatus);
329 }
330 
331 }  // namespace
332 
333 }  // namespace chre