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 namespace {
40
41 constexpr uint64_t kResetWaitTimeMs = 5000;
42 constexpr uint64_t kDiscoveryWaitTimeMs = 5000;
43
workThread(void * transportState)44 void *workThread(void *transportState) {
45 ChppTransportState *state = static_cast<ChppTransportState *>(transportState);
46
47 auto linkContext =
48 static_cast<struct ChppLinuxLinkState *>(state->linkContext);
49
50 pthread_setname_np(pthread_self(), linkContext->workThreadName);
51
52 chppWorkThreadStart(state);
53
54 return nullptr;
55 }
56
57 #define TEST_UUID \
58 { \
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
60 0x00, 0x00, 0x00, 0x12 \
61 }
62
63 enum class Commands : uint16_t {
64 kOk,
65 kError,
66 kTimeout,
67 // Number of request, must stay last
68 kNumCommands,
69 };
70
71 constexpr uint16_t kNumCommands = asBaseType(Commands::kNumCommands);
72
73 // Common code for the client and the service.
74
75 struct CommonState {
76 bool okResponseStatus;
77 bool errorResponseStatus;
78 bool timeoutResponseStatus;
79 struct ChppNotifier notifier;
80 };
81
dispatchResponse(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqStates,struct CommonState * common,struct ChppAppHeader * response,size_t len)82 enum ChppAppErrorCode dispatchResponse(
83 struct ChppAppState *appState,
84 struct ChppOutgoingRequestState *outReqStates, struct CommonState *common,
85 struct ChppAppHeader *response, size_t len) {
86 // The response is composed of the app header only.
87 if (len != sizeof(ChppAppHeader)) {
88 return CHPP_APP_ERROR_NONE;
89 }
90
91 switch (response->command) {
92 case asBaseType(Commands::kOk):
93 // The response for the kOk command should have a CHPP_APP_ERROR_NONE
94 // error.
95 common->okResponseStatus = chppTimestampIncomingResponse(
96 appState, &outReqStates[asBaseType(Commands::kOk)], response);
97
98 common->okResponseStatus &= response->error == CHPP_APP_ERROR_NONE;
99 return CHPP_APP_ERROR_NONE;
100
101 case asBaseType(Commands::kError):
102 // The response for the kError command should have a
103 // CHPP_APP_ERROR_UNSPECIFIED error.
104 common->errorResponseStatus = chppTimestampIncomingResponse(
105 appState, &outReqStates[asBaseType(Commands::kError)], response);
106
107 common->errorResponseStatus &=
108 response->error == CHPP_APP_ERROR_UNSPECIFIED;
109 return CHPP_APP_ERROR_NONE;
110
111 case asBaseType(Commands::kTimeout):
112 // The response for the kTimeout command should have a
113 // CHPP_APP_ERROR_TIMEOUT error. That response is generated by the app
114 // layer.
115 common->timeoutResponseStatus = chppTimestampIncomingResponse(
116 appState, &outReqStates[asBaseType(Commands::kTimeout)], response);
117
118 common->timeoutResponseStatus &=
119 response->error == CHPP_APP_ERROR_TIMEOUT;
120 chppNotifierSignal(&common->notifier, 1 /*signal*/);
121 return CHPP_APP_ERROR_NONE;
122
123 default:
124 return CHPP_APP_ERROR_NONE;
125 }
126 }
127
dispatchRequest(struct ChppAppState * appState,struct ChppIncomingRequestState * inReqStates,struct ChppAppHeader * request,size_t len)128 enum ChppAppErrorCode dispatchRequest(
129 struct ChppAppState *appState, struct ChppIncomingRequestState *inReqStates,
130 struct ChppAppHeader *request, size_t len) {
131 // The request is composed of the app header only.
132 if (len != sizeof(ChppAppHeader)) {
133 return CHPP_APP_ERROR_NONE;
134 }
135
136 switch (request->command) {
137 case asBaseType(Commands::kOk): {
138 // Return a response for the kOk command.
139 chppTimestampIncomingRequest(&inReqStates[asBaseType(Commands::kOk)],
140 request);
141
142 struct ChppAppHeader *response =
143 chppAllocResponse(request, sizeof(ChppAppHeader));
144
145 chppSendTimestampedResponseOrFail(appState,
146 &inReqStates[asBaseType(Commands::kOk)],
147 response, sizeof(ChppAppHeader));
148 return CHPP_APP_ERROR_NONE;
149 }
150 case asBaseType(Commands::kError): {
151 // Return a response with a CHPP_APP_ERROR_UNSPECIFIED error on kError
152 // command.
153 return CHPP_APP_ERROR_UNSPECIFIED;
154 }
155
156 case asBaseType(Commands::kTimeout): {
157 // Do not send a response on kTimeout for the remote endpoint to timeout.
158 chppTimestampIncomingRequest(&inReqStates[asBaseType(Commands::kError)],
159 request);
160
161 return CHPP_APP_ERROR_NONE;
162 }
163
164 default:
165 return CHPP_APP_ERROR_NONE;
166 }
167 }
168
169 // Client specific code.
170 struct ClientState {
171 struct ChppEndpointState chppClientState;
172 struct ChppOutgoingRequestState outReqStates[kNumCommands];
173 struct ChppIncomingRequestState inReqStates[kNumCommands];
174 struct CommonState common;
175 };
176
177 bool clientInit(void *clientState, uint8_t handle,
178 struct ChppVersion serviceVersion);
179 void clientDeinit(void *clientState);
180 enum ChppAppErrorCode clientDispatchResponse(void *clientState, uint8_t *buf,
181 size_t len);
182 enum ChppAppErrorCode clientDispatchRequest(void *clientState, uint8_t *buf,
183 size_t len);
184
185 constexpr struct ChppClient kClient = {
186 .descriptor.uuid = TEST_UUID,
187 .descriptor.version.major = 1,
188 .descriptor.version.minor = 0,
189 .descriptor.version.patch = 0,
190 .resetNotifierFunctionPtr = nullptr,
191 .matchNotifierFunctionPtr = nullptr,
192 .responseDispatchFunctionPtr = &clientDispatchResponse,
193 .notificationDispatchFunctionPtr = nullptr,
194 .requestDispatchFunctionPtr = &clientDispatchRequest,
195 .initFunctionPtr = &clientInit,
196 .deinitFunctionPtr = &clientDeinit,
197 .outReqCount = kNumCommands,
198 .minLength = sizeof(struct ChppAppHeader),
199 };
200
201 // Called when a response is received from the service.
clientDispatchResponse(void * clientState,uint8_t * buf,size_t len)202 enum ChppAppErrorCode clientDispatchResponse(void *clientState, uint8_t *buf,
203 size_t len) {
204 CHPP_NOT_NULL(clientState);
205
206 auto state = static_cast<struct ClientState *>(clientState);
207
208 return dispatchResponse(state->chppClientState.appContext,
209 state->outReqStates, &state->common,
210 reinterpret_cast<struct ChppAppHeader *>(buf), len);
211 }
212
213 // Called when a request is received from the service.
clientDispatchRequest(void * clientState,uint8_t * buf,size_t len)214 enum ChppAppErrorCode clientDispatchRequest(void *clientState, uint8_t *buf,
215 size_t len) {
216 auto request = reinterpret_cast<struct ChppAppHeader *>(buf);
217 auto state = static_cast<struct ClientState *>(clientState);
218
219 return dispatchRequest(state->chppClientState.appContext, state->inReqStates,
220 request, len);
221 }
222
clientInit(void * clientState,uint8_t handle,struct ChppVersion serviceVersion)223 bool clientInit(void *clientState, uint8_t handle,
224 struct ChppVersion serviceVersion) {
225 UNUSED_VAR(serviceVersion);
226 auto state = static_cast<struct ClientState *>(clientState);
227 state->chppClientState.openState = CHPP_OPEN_STATE_OPENED;
228 chppClientInit(&state->chppClientState, handle);
229 return true;
230 }
231
clientDeinit(void * clientState)232 void clientDeinit(void *clientState) {
233 auto state = static_cast<struct ClientState *>(clientState);
234 chppClientDeinit(&state->chppClientState);
235 state->chppClientState.openState = CHPP_OPEN_STATE_CLOSED;
236 }
237
238 // Service specific code.
239
240 struct ServiceState {
241 struct ChppEndpointState chppServiceState;
242 struct ChppOutgoingRequestState outReqStates[kNumCommands];
243 struct ChppIncomingRequestState inReqStates[kNumCommands];
244 struct CommonState common;
245 };
246
247 // Called when a request is received from the client.
serviceDispatchRequest(void * serviceState,uint8_t * buf,size_t len)248 enum ChppAppErrorCode serviceDispatchRequest(void *serviceState, uint8_t *buf,
249 size_t len) {
250 auto request = reinterpret_cast<struct ChppAppHeader *>(buf);
251 auto state = static_cast<struct ServiceState *>(serviceState);
252
253 return dispatchRequest(state->chppServiceState.appContext, state->inReqStates,
254 request, len);
255 }
256
257 // Called when a response is received from the client.
serviceDispatchResponse(void * serviceState,uint8_t * buf,size_t len)258 enum ChppAppErrorCode serviceDispatchResponse(void *serviceState, uint8_t *buf,
259 size_t len) {
260 CHPP_NOT_NULL(serviceState);
261
262 auto state = static_cast<struct ServiceState *>(serviceState);
263
264 return dispatchResponse(state->chppServiceState.appContext,
265 state->outReqStates, &state->common,
266 reinterpret_cast<struct ChppAppHeader *>(buf), len);
267 }
268
269 const struct ChppService kService = {
270 .descriptor.uuid = TEST_UUID,
271 .descriptor.name = "Test",
272 .descriptor.version.major = 1,
273 .descriptor.version.minor = 0,
274 .descriptor.version.patch = 0,
275 .resetNotifierFunctionPtr = nullptr,
276 .requestDispatchFunctionPtr = &serviceDispatchRequest,
277 .notificationDispatchFunctionPtr = nullptr,
278 .responseDispatchFunctionPtr = &serviceDispatchResponse,
279 .outReqCount = kNumCommands,
280 .minLength = sizeof(struct ChppAppHeader),
281 };
282
283 /**
284 * Test requests and responses.
285 *
286 * The test parameter is:
287 * - CHPP_MESSAGE_TYPE_CLIENT_REQUEST for client side requests
288 * - CHPP_MESSAGE_TYPE_SERVICE_REQUEST for service side requests
289 */
290 class AppReqRespParamTest : public testing::TestWithParam<ChppMessageType> {
291 protected:
SetUp()292 void SetUp() {
293 chppClearTotalAllocBytes();
294 chppNotifierInit(&mClientState.common.notifier);
295 chppNotifierInit(&mServiceState.common.notifier);
296 memset(&mClientLinkState, 0, sizeof(mClientLinkState));
297 memset(&mServiceLinkState, 0, sizeof(mServiceLinkState));
298
299 mServiceLinkState.linkThreadName = "Service Link";
300 mServiceLinkState.workThreadName = "Service worker";
301 mServiceLinkState.isLinkActive = true;
302 mServiceLinkState.remoteLinkState = &mClientLinkState;
303 mServiceLinkState.rxInRemoteEndpointWorker = false;
304
305 mClientLinkState.linkThreadName = "Client Link";
306 mClientLinkState.workThreadName = "Client worker";
307 mClientLinkState.isLinkActive = true;
308 mClientLinkState.remoteLinkState = &mServiceLinkState;
309 mClientLinkState.rxInRemoteEndpointWorker = false;
310
311 // No default clients/services.
312 struct ChppClientServiceSet set;
313 memset(&set, 0, sizeof(set));
314
315 const struct ChppLinkApi *linkApi = getLinuxLinkApi();
316
317 // Init client side.
318 chppTransportInit(&mClientTransportState, &mClientAppState,
319 &mClientLinkState, linkApi);
320 chppAppInitWithClientServiceSet(&mClientAppState, &mClientTransportState,
321 set);
322
323 // Init service side.
324 chppTransportInit(&mServiceTransportState, &mServiceAppState,
325 &mServiceLinkState, linkApi);
326 chppAppInitWithClientServiceSet(&mServiceAppState, &mServiceTransportState,
327 set);
328
329 BringUpClient();
330 std::this_thread::sleep_for(std::chrono::milliseconds(450));
331 BringUpService();
332 mClientLinkState.linkEstablished = true;
333 mServiceLinkState.linkEstablished = true;
334
335 EXPECT_TRUE(chppTransportWaitForResetComplete(&mClientTransportState,
336 kResetWaitTimeMs));
337 EXPECT_TRUE(chppTransportWaitForResetComplete(&mServiceTransportState,
338 kResetWaitTimeMs));
339
340 EXPECT_TRUE(
341 chppWaitForDiscoveryComplete(&mClientAppState, kDiscoveryWaitTimeMs));
342 EXPECT_TRUE(
343 chppWaitForDiscoveryComplete(&mServiceAppState, kDiscoveryWaitTimeMs));
344 }
345
BringUpClient()346 void BringUpClient() {
347 memset(&mClientState, 0, sizeof(mClientState));
348 chppRegisterClient(&mClientAppState, &mClientState,
349 &mClientState.chppClientState,
350 &mClientState.outReqStates[0], &kClient);
351
352 pthread_create(&mClientWorkThread, NULL, workThread,
353 &mClientTransportState);
354 }
355
BringUpService()356 void BringUpService() {
357 memset(&mServiceState, 0, sizeof(mServiceState));
358 chppRegisterService(&mServiceAppState, &mServiceState,
359 &mServiceState.chppServiceState,
360 &mServiceState.outReqStates[0], &kService);
361
362 pthread_create(&mServiceWorkThread, NULL, workThread,
363 &mServiceTransportState);
364 }
365
TearDown()366 void TearDown() {
367 chppNotifierDeinit(&mClientState.common.notifier);
368 chppNotifierDeinit(&mServiceState.common.notifier);
369 chppWorkThreadStop(&mClientTransportState);
370 chppWorkThreadStop(&mServiceTransportState);
371 pthread_join(mClientWorkThread, NULL);
372 pthread_join(mServiceWorkThread, NULL);
373
374 // Deinit client side.
375 chppAppDeinit(&mClientAppState);
376 chppTransportDeinit(&mClientTransportState);
377
378 // Deinit service side.
379 chppAppDeinit(&mServiceAppState);
380 chppTransportDeinit(&mServiceTransportState);
381
382 EXPECT_EQ(chppGetTotalAllocBytes(), 0);
383 }
384
AllocRequestCommand(Commands command)385 struct ChppAppHeader *AllocRequestCommand(Commands command) {
386 return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST
387 ? chppAllocClientRequestCommand(&mClientState.chppClientState,
388 asBaseType(command))
389 : chppAllocServiceRequestCommand(&mServiceState.chppServiceState,
390 asBaseType(command));
391 }
392
GetCommonState()393 struct CommonState *GetCommonState() {
394 return GetParam() == CHPP_MESSAGE_TYPE_CLIENT_REQUEST
395 ? &mClientState.common
396 : &mServiceState.common;
397 }
398
SendTimestampedRequestAndWait(struct ChppAppHeader * request)399 bool SendTimestampedRequestAndWait(struct ChppAppHeader *request) {
400 constexpr size_t len = sizeof(struct ChppAppHeader);
401 if (request->type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) {
402 return chppClientSendTimestampedRequestAndWait(
403 &mClientState.chppClientState,
404 &mClientState.outReqStates[request->command], request, len);
405 }
406
407 return chppServiceSendTimestampedRequestAndWait(
408 &mServiceState.chppServiceState,
409 &mServiceState.outReqStates[request->command], request, len);
410 }
411
SendTimestampedRequestOrFail(struct ChppAppHeader * request,uint64_t timeoutNs)412 bool SendTimestampedRequestOrFail(struct ChppAppHeader *request,
413 uint64_t timeoutNs) {
414 constexpr size_t len = sizeof(struct ChppAppHeader);
415 if (request->type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST) {
416 return chppClientSendTimestampedRequestOrFail(
417 &mClientState.chppClientState,
418 &mClientState.outReqStates[request->command], request, len,
419 timeoutNs);
420 }
421
422 return chppServiceSendTimestampedRequestOrFail(
423 &mServiceState.chppServiceState,
424 &mServiceState.outReqStates[request->command], request, len, timeoutNs);
425 }
426
427 // Client side.
428 ChppLinuxLinkState mClientLinkState = {};
429 ChppTransportState mClientTransportState = {};
430 ChppAppState mClientAppState = {};
431 ClientState mClientState;
432 pthread_t mClientWorkThread;
433
434 // Service side
435 ChppLinuxLinkState mServiceLinkState = {};
436 ChppTransportState mServiceTransportState = {};
437 ChppAppState mServiceAppState = {};
438 ServiceState mServiceState = {};
439 pthread_t mServiceWorkThread;
440 };
441
TEST_P(AppReqRespParamTest,sendsRequestAndReceiveResponse)442 TEST_P(AppReqRespParamTest, sendsRequestAndReceiveResponse) {
443 struct ChppAppHeader *request = AllocRequestCommand(Commands::kOk);
444 ASSERT_NE(request, nullptr);
445
446 GetCommonState()->okResponseStatus = false;
447
448 EXPECT_TRUE(SendTimestampedRequestAndWait(request));
449
450 EXPECT_TRUE(GetCommonState()->okResponseStatus);
451 }
452
TEST_P(AppReqRespParamTest,sendsRequestAndReceiveErrorResponse)453 TEST_P(AppReqRespParamTest, sendsRequestAndReceiveErrorResponse) {
454 struct ChppAppHeader *request = AllocRequestCommand(Commands::kError);
455 ASSERT_NE(request, nullptr);
456
457 GetCommonState()->errorResponseStatus = false;
458
459 EXPECT_TRUE(SendTimestampedRequestAndWait(request));
460
461 EXPECT_TRUE(GetCommonState()->errorResponseStatus);
462 }
463
TEST_P(AppReqRespParamTest,sendsRequestAndReceiveTimeoutResponse)464 TEST_P(AppReqRespParamTest, sendsRequestAndReceiveTimeoutResponse) {
465 struct ChppAppHeader *request = AllocRequestCommand(Commands::kTimeout);
466 ASSERT_NE(request, nullptr);
467
468 GetCommonState()->timeoutResponseStatus = false;
469
470 EXPECT_TRUE(
471 SendTimestampedRequestOrFail(request, 10 * kOneMicrosecondInNanoseconds));
472
473 chppNotifierWait(&GetCommonState()->notifier);
474
475 EXPECT_TRUE(GetCommonState()->timeoutResponseStatus);
476 }
477
478 INSTANTIATE_TEST_SUITE_P(
479 AppReqRespTest, AppReqRespParamTest,
480 testing::Values(CHPP_MESSAGE_TYPE_CLIENT_REQUEST,
481 CHPP_MESSAGE_TYPE_SERVICE_REQUEST),
__anonbd6951b40202(const testing::TestParamInfo<AppReqRespParamTest::ParamType> &info) 482 [](const testing::TestParamInfo<AppReqRespParamTest::ParamType> &info) {
483 return info.param == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? "ClientRequests"
484 : "ServiceRequests";
485 });
486
487 } // namespace
488 } // namespace chre