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 }