1 /*
2 * Copyright (C) 2020 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 "chpp/clients.h"
18
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23
24 #include "chpp/app.h"
25 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
26 #include "chpp/clients/discovery.h"
27 #endif
28 #ifdef CHPP_CLIENT_ENABLED_GNSS
29 #include "chpp/clients/gnss.h"
30 #endif
31 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
32 #include "chpp/clients/loopback.h"
33 #endif
34 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
35 #include "chpp/clients/timesync.h"
36 #endif
37 #ifdef CHPP_CLIENT_ENABLED_WIFI
38 #include "chpp/clients/wifi.h"
39 #endif
40 #ifdef CHPP_CLIENT_ENABLED_WWAN
41 #include "chpp/clients/wwan.h"
42 #endif
43 #include "chpp/log.h"
44 #include "chpp/macros.h"
45 #include "chpp/memory.h"
46 #include "chpp/time.h"
47 #include "chpp/transport.h"
48
49 /************************************************
50 * Prototypes
51 ***********************************************/
52
53 static bool chppIsClientApiReady(struct ChppEndpointState *clientState);
54 static ChppClientDeinitFunction *chppGetClientDeinitFunction(
55 struct ChppAppState *context, uint8_t index);
56
57 /************************************************
58 * Private Functions
59 ***********************************************/
60
61 /**
62 * Determines whether a client is ready to accept commands via its API (i.e. is
63 * initialized and opened). If the client is in the process of reopening, it
64 * will wait for the client to reopen.
65 *
66 * @param clientState State of the client sending the client request.
67 *
68 * @return Indicates whether the client is ready.
69 */
chppIsClientApiReady(struct ChppEndpointState * clientState)70 static bool chppIsClientApiReady(struct ChppEndpointState *clientState) {
71 CHPP_DEBUG_NOT_NULL(clientState);
72
73 bool result = false;
74
75 if (clientState->initialized) {
76 switch (clientState->openState) {
77 case (CHPP_OPEN_STATE_CLOSED):
78 case (CHPP_OPEN_STATE_WAITING_TO_OPEN): {
79 // result remains false
80 break;
81 }
82
83 case (CHPP_OPEN_STATE_OPENED): {
84 result = true;
85 break;
86 }
87
88 case (CHPP_OPEN_STATE_OPENING): {
89 // Allow the open request to go through
90 clientState->openState = CHPP_OPEN_STATE_WAITING_TO_OPEN;
91 result = true;
92 break;
93 }
94 }
95 }
96
97 if (!result) {
98 CHPP_LOGE("Client not ready (everInit=%d, init=%d, open=%" PRIu8 ")",
99 clientState->everInitialized, clientState->initialized,
100 clientState->openState);
101 }
102 return result;
103 }
104
105 /**
106 * Returns the deinitialization function pointer of a particular negotiated
107 * client.
108 *
109 * @param context Maintains status for each app layer instance.
110 * @param index Index of the registered client.
111 *
112 * @return Pointer to the match notification function.
113 */
chppGetClientDeinitFunction(struct ChppAppState * context,uint8_t index)114 static ChppClientDeinitFunction *chppGetClientDeinitFunction(
115 struct ChppAppState *context, uint8_t index) {
116 CHPP_DEBUG_NOT_NULL(context);
117
118 return context->registeredClients[index]->deinitFunctionPtr;
119 }
120
121 /************************************************
122 * Public Functions
123 ***********************************************/
124
chppRegisterCommonClients(struct ChppAppState * context)125 void chppRegisterCommonClients(struct ChppAppState *context) {
126 UNUSED_VAR(context);
127 CHPP_DEBUG_NOT_NULL(context);
128
129 CHPP_LOGD("Registering Clients");
130
131 #ifdef CHPP_CLIENT_ENABLED_WWAN
132 if (context->clientServiceSet.wwanClient) {
133 chppRegisterWwanClient(context);
134 }
135 #endif
136
137 #ifdef CHPP_CLIENT_ENABLED_WIFI
138 if (context->clientServiceSet.wifiClient) {
139 chppRegisterWifiClient(context);
140 }
141 #endif
142
143 #ifdef CHPP_CLIENT_ENABLED_GNSS
144 if (context->clientServiceSet.gnssClient) {
145 chppRegisterGnssClient(context);
146 }
147 #endif
148 }
149
chppDeregisterCommonClients(struct ChppAppState * context)150 void chppDeregisterCommonClients(struct ChppAppState *context) {
151 UNUSED_VAR(context);
152 CHPP_DEBUG_NOT_NULL(context);
153
154 CHPP_LOGD("Deregistering Clients");
155
156 #ifdef CHPP_CLIENT_ENABLED_WWAN
157 if (context->clientServiceSet.wwanClient) {
158 chppDeregisterWwanClient(context);
159 }
160 #endif
161
162 #ifdef CHPP_CLIENT_ENABLED_WIFI
163 if (context->clientServiceSet.wifiClient) {
164 chppDeregisterWifiClient(context);
165 }
166 #endif
167
168 #ifdef CHPP_CLIENT_ENABLED_GNSS
169 if (context->clientServiceSet.gnssClient) {
170 chppDeregisterGnssClient(context);
171 }
172 #endif
173 }
174
chppRegisterClient(struct ChppAppState * appContext,void * clientContext,struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqStates,const struct ChppClient * newClient)175 void chppRegisterClient(struct ChppAppState *appContext, void *clientContext,
176 struct ChppEndpointState *clientState,
177 struct ChppOutgoingRequestState *outReqStates,
178 const struct ChppClient *newClient) {
179 CHPP_NOT_NULL(newClient);
180 CHPP_DEBUG_NOT_NULL(appContext);
181 CHPP_DEBUG_NOT_NULL(clientContext);
182 CHPP_DEBUG_NOT_NULL(clientState);
183 CHPP_DEBUG_NOT_NULL(newClient);
184
185 if (appContext->registeredClientCount >= CHPP_MAX_REGISTERED_CLIENTS) {
186 CHPP_LOGE("Max clients registered: %" PRIu8,
187 appContext->registeredClientCount);
188 return;
189 }
190 clientState->appContext = appContext;
191 clientState->outReqStates = outReqStates;
192 clientState->index = appContext->registeredClientCount;
193 clientState->context = clientContext;
194 clientState->nextTimerTimeoutNs = CHPP_TIME_MAX;
195 appContext->registeredClientStates[appContext->registeredClientCount] =
196 clientState;
197
198 appContext->registeredClients[appContext->registeredClientCount] = newClient;
199
200 char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
201 chppUuidToStr(newClient->descriptor.uuid, uuidText);
202 CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8
203 ".%" PRIu16 ", min_len=%" PRIuSIZE,
204 appContext->registeredClientCount, uuidText,
205 newClient->descriptor.version.major,
206 newClient->descriptor.version.minor,
207 newClient->descriptor.version.patch, newClient->minLength);
208
209 appContext->registeredClientCount++;
210 }
211
chppInitBasicClients(struct ChppAppState * context)212 void chppInitBasicClients(struct ChppAppState *context) {
213 UNUSED_VAR(context);
214 CHPP_DEBUG_NOT_NULL(context);
215
216 CHPP_LOGD("Initializing basic clients");
217
218 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
219 if (context->clientServiceSet.loopbackClient) {
220 chppLoopbackClientInit(context);
221 }
222 #endif
223
224 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
225 chppTimesyncClientInit(context);
226 #endif
227
228 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
229 chppDiscoveryInit(context);
230 #endif
231 }
232
chppClientInit(struct ChppEndpointState * clientState,uint8_t handle)233 void chppClientInit(struct ChppEndpointState *clientState, uint8_t handle) {
234 CHPP_DEBUG_NOT_NULL(clientState);
235 CHPP_ASSERT_LOG(!clientState->initialized,
236 "Client H#%" PRIu8 " already initialized", handle);
237
238 if (!clientState->everInitialized) {
239 clientState->handle = handle;
240 chppMutexInit(&clientState->syncResponse.mutex);
241 chppConditionVariableInit(&clientState->syncResponse.condVar);
242 clientState->everInitialized = true;
243 }
244
245 clientState->initialized = true;
246 }
247
chppClientDeinit(struct ChppEndpointState * clientState)248 void chppClientDeinit(struct ChppEndpointState *clientState) {
249 CHPP_DEBUG_NOT_NULL(clientState);
250 CHPP_ASSERT_LOG(clientState->initialized,
251 "Client H#%" PRIu8 " already deinitialized",
252 clientState->handle);
253
254 clientState->initialized = false;
255 }
256
chppDeinitBasicClients(struct ChppAppState * context)257 void chppDeinitBasicClients(struct ChppAppState *context) {
258 UNUSED_VAR(context);
259 CHPP_DEBUG_NOT_NULL(context);
260
261 CHPP_LOGD("Deinitializing basic clients");
262
263 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
264 if (context->clientServiceSet.loopbackClient) {
265 chppLoopbackClientDeinit(context);
266 }
267 #endif
268
269 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
270 chppTimesyncClientDeinit(context);
271 #endif
272
273 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
274 chppDiscoveryDeinit(context);
275 #endif
276 }
277
chppDeinitMatchedClients(struct ChppAppState * context)278 void chppDeinitMatchedClients(struct ChppAppState *context) {
279 CHPP_DEBUG_NOT_NULL(context);
280 CHPP_LOGD("Deinitializing matched clients");
281
282 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
283 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
284 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
285 // Discovered service has a matched client
286 ChppClientDeinitFunction *clientDeinitFunction =
287 chppGetClientDeinitFunction(context, clientIndex);
288
289 CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex,
290 CHPP_SERVICE_HANDLE_OF_INDEX(i),
291 (clientDeinitFunction != NULL));
292
293 if (clientDeinitFunction != NULL) {
294 clientDeinitFunction(
295 context->registeredClientStates[clientIndex]->context);
296 }
297 }
298 }
299 }
300
chppAllocClientRequest(struct ChppEndpointState * clientState,size_t len)301 struct ChppAppHeader *chppAllocClientRequest(
302 struct ChppEndpointState *clientState, size_t len) {
303 CHPP_DEBUG_NOT_NULL(clientState);
304 return chppAllocRequest(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, clientState, len);
305 }
306
chppAllocClientRequestCommand(struct ChppEndpointState * clientState,uint16_t command)307 struct ChppAppHeader *chppAllocClientRequestCommand(
308 struct ChppEndpointState *clientState, uint16_t command) {
309 struct ChppAppHeader *request =
310 chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader));
311
312 if (request != NULL) {
313 request->command = command;
314 }
315 return request;
316 }
317
chppClientSendTimestampedRequestOrFail(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)318 bool chppClientSendTimestampedRequestOrFail(
319 struct ChppEndpointState *clientState,
320 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
321 uint64_t timeoutNs) {
322 CHPP_DEBUG_NOT_NULL(clientState);
323 CHPP_DEBUG_NOT_NULL(outReqState);
324 CHPP_DEBUG_NOT_NULL(buf);
325
326 if (!chppIsClientApiReady(clientState)) {
327 if (clientState->initialized &&
328 clientState->openState == CHPP_OPEN_STATE_CLOSED) {
329 CHPP_LOGW("Trying to send request when closed - link broken?");
330 chppTransportForceReset(clientState->appContext->transportContext);
331 }
332 CHPP_FREE_AND_NULLIFY(buf);
333 return false;
334 }
335
336 return chppSendTimestampedRequestOrFail(clientState, outReqState, buf, len,
337 timeoutNs);
338 }
339
chppClientSendTimestampedRequestAndWait(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len)340 bool chppClientSendTimestampedRequestAndWait(
341 struct ChppEndpointState *clientState,
342 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len) {
343 return chppClientSendTimestampedRequestAndWaitTimeout(
344 clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_DEFAULT);
345 }
346
chppClientSendTimestampedRequestAndWaitTimeout(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)347 bool chppClientSendTimestampedRequestAndWaitTimeout(
348 struct ChppEndpointState *clientState,
349 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
350 uint64_t timeoutNs) {
351 bool result = chppClientSendTimestampedRequestOrFail(
352 clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_INFINITE);
353
354 if (!result) {
355 return false;
356 }
357
358 return chppWaitForResponseWithTimeout(&clientState->syncResponse, outReqState,
359 timeoutNs);
360 }
361
chppClientPseudoOpen(struct ChppEndpointState * clientState)362 void chppClientPseudoOpen(struct ChppEndpointState *clientState) {
363 clientState->pseudoOpen = true;
364 }
365
chppClientSendOpenRequest(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * openReqState,uint16_t openCommand,bool blocking)366 bool chppClientSendOpenRequest(struct ChppEndpointState *clientState,
367 struct ChppOutgoingRequestState *openReqState,
368 uint16_t openCommand, bool blocking) {
369 CHPP_NOT_NULL(clientState);
370 CHPP_NOT_NULL(openReqState);
371
372 bool result = false;
373 uint8_t priorState = clientState->openState;
374
375 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
376 chppTimesyncMeasureOffset(clientState->appContext);
377 #endif
378
379 struct ChppAppHeader *request =
380 chppAllocClientRequestCommand(clientState, openCommand);
381
382 if (request == NULL) {
383 return false;
384 }
385
386 clientState->openState = CHPP_OPEN_STATE_OPENING;
387
388 if (blocking) {
389 CHPP_LOGD("Opening service - blocking");
390 result = chppClientSendTimestampedRequestAndWait(clientState, openReqState,
391 request, sizeof(*request));
392 } else {
393 CHPP_LOGD("Opening service - non-blocking");
394 result = chppClientSendTimestampedRequestOrFail(
395 clientState, openReqState, request, sizeof(*request),
396 CHPP_REQUEST_TIMEOUT_DEFAULT);
397 }
398
399 if (!result) {
400 CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d",
401 priorState, clientState->pseudoOpen, blocking);
402 clientState->openState = CHPP_OPEN_STATE_CLOSED;
403
404 } else if (blocking) {
405 result = (clientState->openState == CHPP_OPEN_STATE_OPENED);
406 }
407
408 return result;
409 }
410
chppClientProcessOpenResponse(struct ChppEndpointState * clientState,uint8_t * buf,size_t len)411 void chppClientProcessOpenResponse(struct ChppEndpointState *clientState,
412 uint8_t *buf, size_t len) {
413 CHPP_DEBUG_NOT_NULL(clientState);
414 CHPP_DEBUG_NOT_NULL(buf);
415
416 UNUSED_VAR(len); // Necessary depending on assert macro below
417 // Assert condition already guaranteed by chppAppProcessRxDatagram() but
418 // checking again since this is a public function
419 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
420
421 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
422 if (rxHeader->error != CHPP_APP_ERROR_NONE) {
423 CHPP_LOGE("Service open failed at service");
424 clientState->openState = CHPP_OPEN_STATE_CLOSED;
425 } else {
426 CHPP_LOGD("Service open succeeded at service");
427 clientState->openState = CHPP_OPEN_STATE_OPENED;
428 }
429 }
430
chppClientCloseOpenRequests(struct ChppEndpointState * clientState,const struct ChppClient * client,bool clearOnly)431 void chppClientCloseOpenRequests(struct ChppEndpointState *clientState,
432 const struct ChppClient *client,
433 bool clearOnly) {
434 UNUSED_VAR(client);
435 chppCloseOpenRequests(clientState, CHPP_ENDPOINT_CLIENT, clearOnly);
436 }
437
chppAllocClientNotification(size_t len)438 struct ChppAppHeader *chppAllocClientNotification(size_t len) {
439 return chppAllocNotification(CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION, len);
440 }