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 appContext->registeredClientStates[appContext->registeredClientCount] =
195 clientState;
196
197 appContext->registeredClients[appContext->registeredClientCount] = newClient;
198
199 char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
200 chppUuidToStr(newClient->descriptor.uuid, uuidText);
201 CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8
202 ".%" PRIu16 ", min_len=%" PRIuSIZE,
203 appContext->registeredClientCount, uuidText,
204 newClient->descriptor.version.major,
205 newClient->descriptor.version.minor,
206 newClient->descriptor.version.patch, newClient->minLength);
207
208 appContext->registeredClientCount++;
209 }
210
chppInitBasicClients(struct ChppAppState * context)211 void chppInitBasicClients(struct ChppAppState *context) {
212 UNUSED_VAR(context);
213 CHPP_DEBUG_NOT_NULL(context);
214
215 CHPP_LOGD("Initializing basic clients");
216
217 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
218 if (context->clientServiceSet.loopbackClient) {
219 chppLoopbackClientInit(context);
220 }
221 #endif
222
223 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
224 chppTimesyncClientInit(context);
225 #endif
226
227 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
228 chppDiscoveryInit(context);
229 #endif
230 }
231
chppClientInit(struct ChppEndpointState * clientState,uint8_t handle)232 void chppClientInit(struct ChppEndpointState *clientState, uint8_t handle) {
233 CHPP_DEBUG_NOT_NULL(clientState);
234 CHPP_ASSERT_LOG(!clientState->initialized,
235 "Client H#%" PRIu8 " already initialized", handle);
236
237 if (!clientState->everInitialized) {
238 clientState->handle = handle;
239 chppMutexInit(&clientState->syncResponse.mutex);
240 chppConditionVariableInit(&clientState->syncResponse.condVar);
241 clientState->everInitialized = true;
242 }
243
244 clientState->initialized = true;
245 }
246
chppClientDeinit(struct ChppEndpointState * clientState)247 void chppClientDeinit(struct ChppEndpointState *clientState) {
248 CHPP_DEBUG_NOT_NULL(clientState);
249 CHPP_ASSERT_LOG(clientState->initialized,
250 "Client H#%" PRIu8 " already deinitialized",
251 clientState->handle);
252
253 clientState->initialized = false;
254 }
255
chppDeinitBasicClients(struct ChppAppState * context)256 void chppDeinitBasicClients(struct ChppAppState *context) {
257 UNUSED_VAR(context);
258 CHPP_DEBUG_NOT_NULL(context);
259
260 CHPP_LOGD("Deinitializing basic clients");
261
262 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
263 if (context->clientServiceSet.loopbackClient) {
264 chppLoopbackClientDeinit(context);
265 }
266 #endif
267
268 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
269 chppTimesyncClientDeinit(context);
270 #endif
271
272 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
273 chppDiscoveryDeinit(context);
274 #endif
275 }
276
chppDeinitMatchedClients(struct ChppAppState * context)277 void chppDeinitMatchedClients(struct ChppAppState *context) {
278 CHPP_DEBUG_NOT_NULL(context);
279 CHPP_LOGD("Deinitializing matched clients");
280
281 for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
282 uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
283 if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
284 // Discovered service has a matched client
285 ChppClientDeinitFunction *clientDeinitFunction =
286 chppGetClientDeinitFunction(context, clientIndex);
287
288 CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex,
289 CHPP_SERVICE_HANDLE_OF_INDEX(i),
290 (clientDeinitFunction != NULL));
291
292 if (clientDeinitFunction != NULL) {
293 clientDeinitFunction(
294 context->registeredClientStates[clientIndex]->context);
295 }
296 }
297 }
298 }
299
chppAllocClientRequest(struct ChppEndpointState * clientState,size_t len)300 struct ChppAppHeader *chppAllocClientRequest(
301 struct ChppEndpointState *clientState, size_t len) {
302 CHPP_DEBUG_NOT_NULL(clientState);
303 return chppAllocRequest(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, clientState, len);
304 }
305
chppAllocClientRequestCommand(struct ChppEndpointState * clientState,uint16_t command)306 struct ChppAppHeader *chppAllocClientRequestCommand(
307 struct ChppEndpointState *clientState, uint16_t command) {
308 struct ChppAppHeader *request =
309 chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader));
310
311 if (request != NULL) {
312 request->command = command;
313 }
314 return request;
315 }
316
chppClientSendTimestampedRequestOrFail(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)317 bool chppClientSendTimestampedRequestOrFail(
318 struct ChppEndpointState *clientState,
319 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
320 uint64_t timeoutNs) {
321 CHPP_DEBUG_NOT_NULL(clientState);
322 CHPP_DEBUG_NOT_NULL(outReqState);
323 CHPP_DEBUG_NOT_NULL(buf);
324
325 if (!chppIsClientApiReady(clientState)) {
326 CHPP_FREE_AND_NULLIFY(buf);
327 return false;
328 }
329
330 return chppSendTimestampedRequestOrFail(clientState, outReqState, buf, len,
331 timeoutNs);
332 }
333
chppClientSendTimestampedRequestAndWait(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len)334 bool chppClientSendTimestampedRequestAndWait(
335 struct ChppEndpointState *clientState,
336 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len) {
337 return chppClientSendTimestampedRequestAndWaitTimeout(
338 clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_DEFAULT);
339 }
340
chppClientSendTimestampedRequestAndWaitTimeout(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)341 bool chppClientSendTimestampedRequestAndWaitTimeout(
342 struct ChppEndpointState *clientState,
343 struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
344 uint64_t timeoutNs) {
345 bool result = chppClientSendTimestampedRequestOrFail(
346 clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_INFINITE);
347
348 if (!result) {
349 return false;
350 }
351
352 return chppWaitForResponseWithTimeout(&clientState->syncResponse, outReqState,
353 timeoutNs);
354 }
355
chppClientPseudoOpen(struct ChppEndpointState * clientState)356 void chppClientPseudoOpen(struct ChppEndpointState *clientState) {
357 clientState->pseudoOpen = true;
358 }
359
chppClientSendOpenRequest(struct ChppEndpointState * clientState,struct ChppOutgoingRequestState * openReqState,uint16_t openCommand,bool blocking)360 bool chppClientSendOpenRequest(struct ChppEndpointState *clientState,
361 struct ChppOutgoingRequestState *openReqState,
362 uint16_t openCommand, bool blocking) {
363 CHPP_NOT_NULL(clientState);
364 CHPP_NOT_NULL(openReqState);
365
366 bool result = false;
367 uint8_t priorState = clientState->openState;
368
369 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
370 chppTimesyncMeasureOffset(clientState->appContext);
371 #endif
372
373 struct ChppAppHeader *request =
374 chppAllocClientRequestCommand(clientState, openCommand);
375
376 if (request == NULL) {
377 return false;
378 }
379
380 clientState->openState = CHPP_OPEN_STATE_OPENING;
381
382 if (blocking) {
383 CHPP_LOGD("Opening service - blocking");
384 result = chppClientSendTimestampedRequestAndWait(clientState, openReqState,
385 request, sizeof(*request));
386 } else {
387 CHPP_LOGD("Opening service - non-blocking");
388 result = chppClientSendTimestampedRequestOrFail(
389 clientState, openReqState, request, sizeof(*request),
390 CHPP_REQUEST_TIMEOUT_INFINITE);
391 }
392
393 if (!result) {
394 CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d",
395 priorState, clientState->pseudoOpen, blocking);
396 clientState->openState = CHPP_OPEN_STATE_CLOSED;
397
398 } else if (blocking) {
399 result = (clientState->openState == CHPP_OPEN_STATE_OPENED);
400 }
401
402 return result;
403 }
404
chppClientProcessOpenResponse(struct ChppEndpointState * clientState,uint8_t * buf,size_t len)405 void chppClientProcessOpenResponse(struct ChppEndpointState *clientState,
406 uint8_t *buf, size_t len) {
407 CHPP_DEBUG_NOT_NULL(clientState);
408 CHPP_DEBUG_NOT_NULL(buf);
409
410 UNUSED_VAR(len); // Necessary depending on assert macro below
411 // Assert condition already guaranteed by chppAppProcessRxDatagram() but
412 // checking again since this is a public function
413 CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
414
415 struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
416 if (rxHeader->error != CHPP_APP_ERROR_NONE) {
417 CHPP_LOGE("Service open failed at service");
418 clientState->openState = CHPP_OPEN_STATE_CLOSED;
419 } else {
420 CHPP_LOGD("Service open succeeded at service");
421 clientState->openState = CHPP_OPEN_STATE_OPENED;
422 }
423 }
424
chppClientCloseOpenRequests(struct ChppEndpointState * clientState,const struct ChppClient * client,bool clearOnly)425 void chppClientCloseOpenRequests(struct ChppEndpointState *clientState,
426 const struct ChppClient *client,
427 bool clearOnly) {
428 UNUSED_VAR(client);
429 chppCloseOpenRequests(clientState, CHPP_ENDPOINT_CLIENT, clearOnly);
430 }
431
chppAllocClientNotification(size_t len)432 struct ChppAppHeader *chppAllocClientNotification(size_t len) {
433 return chppAllocNotification(CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION, len);
434 }