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