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/app.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "chpp/clients.h"
27 #include "chpp/clients/discovery.h"
28 #include "chpp/services.h"
29 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
30 #include "chpp/clients/loopback.h"
31 #endif
32 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
33 #include "chpp/clients/timesync.h"
34 #endif
35 #include "chpp/log.h"
36 #include "chpp/macros.h"
37 #include "chpp/notifier.h"
38 #include "chpp/pal_api.h"
39 #include "chpp/services.h"
40 #include "chpp/services/discovery.h"
41 #include "chpp/services/loopback.h"
42 #include "chpp/services/nonhandle.h"
43 #include "chpp/services/timesync.h"
44 #include "chpp/time.h"
45 
46 /************************************************
47  *  Prototypes
48  ***********************************************/
49 
50 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
51                                                uint8_t *buf, size_t len);
52 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
53                                                  uint8_t *buf, size_t len);
54 
55 static bool chppDatagramLenIsOk(struct ChppAppState *context,
56                                 const struct ChppAppHeader *rxHeader,
57                                 size_t len);
58 static ChppDispatchFunction *chppGetDispatchFunction(
59     struct ChppAppState *context, uint8_t handle, enum ChppMessageType type);
60 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
61 static ChppNotifierFunction *chppGetClientResetNotifierFunction(
62     struct ChppAppState *context, uint8_t index);
63 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
64 static ChppNotifierFunction *chppGetServiceResetNotifierFunction(
65     struct ChppAppState *context, uint8_t index);
66 static inline const struct ChppService *chppServiceOfHandle(
67     struct ChppAppState *appContext, uint8_t handle);
68 static inline const struct ChppClient *chppClientOfHandle(
69     struct ChppAppState *appContext, uint8_t handle);
70 static inline struct ChppEndpointState *chppServiceStateOfHandle(
71     struct ChppAppState *appContext, uint8_t handle);
72 static inline struct ChppEndpointState *chppClientStateOfHandle(
73     struct ChppAppState *appContext, uint8_t handle);
74 static struct ChppEndpointState *chppClientOrServiceStateOfHandle(
75     struct ChppAppState *appContext, uint8_t handle, enum ChppMessageType type);
76 
77 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
78                                                 uint8_t *buf, size_t len);
79 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *context,
80                                                 uint8_t *buf, size_t len);
81 
82 /************************************************
83  *  Private Functions
84  ***********************************************/
85 
86 /**
87  * Processes a client request that is determined to be for a predefined CHPP
88  * service.
89  *
90  * @param context State of the app layer.
91  * @param buf Input data. Cannot be null.
92  * @param len Length of input data in bytes.
93  *
94  * @return False if handle is invalid. True otherwise.
95  */
chppProcessPredefinedClientRequest(struct ChppAppState * context,uint8_t * buf,size_t len)96 static bool chppProcessPredefinedClientRequest(struct ChppAppState *context,
97                                                uint8_t *buf, size_t len) {
98   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
99   bool handleValid = true;
100   bool dispatchResult = true;
101 
102   switch (rxHeader->handle) {
103     case CHPP_HANDLE_LOOPBACK: {
104       dispatchResult = chppDispatchLoopbackClientRequest(context, buf, len);
105       break;
106     }
107 
108     case CHPP_HANDLE_TIMESYNC: {
109       dispatchResult = chppDispatchTimesyncClientRequest(context, buf, len);
110       break;
111     }
112 
113     case CHPP_HANDLE_DISCOVERY: {
114       dispatchResult = chppDispatchDiscoveryClientRequest(context, buf, len);
115       break;
116     }
117 
118     default: {
119       handleValid = false;
120     }
121   }
122 
123   if (dispatchResult == false) {
124     CHPP_LOGE("H#%" PRIu8 " unknown request. cmd=%#x, ID=%" PRIu8,
125               rxHeader->handle, rxHeader->command, rxHeader->transaction);
126   }
127 
128   return handleValid;
129 }
130 
131 /**
132  * Processes a service response that is determined to be for a predefined CHPP
133  * client.
134  *
135  * @param context State of the app layer.
136  * @param buf Input data. Cannot be null.
137  * @param len Length of input data in bytes.
138  *
139  * @return False if handle is invalid. True otherwise.
140  */
chppProcessPredefinedServiceResponse(struct ChppAppState * context,uint8_t * buf,size_t len)141 static bool chppProcessPredefinedServiceResponse(struct ChppAppState *context,
142                                                  uint8_t *buf, size_t len) {
143   CHPP_DEBUG_NOT_NULL(buf);
144   // Possibly unused if compiling without the clients below enabled
145   UNUSED_VAR(context);
146   UNUSED_VAR(len);
147 
148   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
149   bool handleValid = true;
150   bool dispatchResult = true;
151 
152   switch (rxHeader->handle) {
153 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
154     case CHPP_HANDLE_LOOPBACK: {
155       dispatchResult = chppDispatchLoopbackServiceResponse(context, buf, len);
156       break;
157     }
158 #endif  // CHPP_CLIENT_ENABLED_LOOPBACK
159 
160 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
161     case CHPP_HANDLE_TIMESYNC: {
162       dispatchResult = chppDispatchTimesyncServiceResponse(context, buf, len);
163       break;
164     }
165 #endif  // CHPP_CLIENT_ENABLED_TIMESYNC
166 
167 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
168     case CHPP_HANDLE_DISCOVERY: {
169       dispatchResult = chppDispatchDiscoveryServiceResponse(context, buf, len);
170       break;
171     }
172 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
173 
174     default: {
175       handleValid = false;
176     }
177   }
178 
179   if (dispatchResult == false) {
180     CHPP_LOGE("H#%" PRIu8 " unknown response. cmd=%#x, ID=%" PRIu8
181               ", len=%" PRIuSIZE,
182               rxHeader->handle, rxHeader->command, rxHeader->transaction, len);
183   }
184 
185   return handleValid;
186 }
187 
188 /**
189  * Verifies if the length of a Rx Datagram from the transport layer is
190  * sufficient for the associated service/client.
191  *
192  * @param context State of the app layer.
193  * @param rxHeader The pointer to the datagram RX header.
194  * @param len Length of the datagram in bytes.
195  *
196  * @return true if length is ok.
197  */
chppDatagramLenIsOk(struct ChppAppState * context,const struct ChppAppHeader * rxHeader,size_t len)198 static bool chppDatagramLenIsOk(struct ChppAppState *context,
199                                 const struct ChppAppHeader *rxHeader,
200                                 size_t len) {
201   CHPP_DEBUG_NOT_NULL(context);
202   CHPP_DEBUG_NOT_NULL(rxHeader);
203 
204   size_t minLen = SIZE_MAX;
205   uint8_t handle = rxHeader->handle;
206 
207   if (handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {  // Predefined
208     switch (handle) {
209       case CHPP_HANDLE_NONE:
210         minLen = sizeof_member(struct ChppAppHeader, handle);
211         break;
212 
213       case CHPP_HANDLE_LOOPBACK:
214         minLen = sizeof_member(struct ChppAppHeader, handle) +
215                  sizeof_member(struct ChppAppHeader, type);
216         break;
217 
218       case CHPP_HANDLE_TIMESYNC:
219       case CHPP_HANDLE_DISCOVERY:
220         minLen = sizeof(struct ChppAppHeader);
221         break;
222 
223       default:
224         // len remains SIZE_MAX
225         CHPP_LOGE("Invalid H#%" PRIu8, handle);
226         return false;
227     }
228 
229   } else {  // Negotiated
230     enum ChppMessageType messageType =
231         CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
232 
233     switch (messageType) {
234       case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
235       case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
236       case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION: {
237         const struct ChppService *service =
238             chppServiceOfHandle(context, handle);
239         if (service != NULL) {
240           minLen = service->minLength;
241         }
242         break;
243       }
244       case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
245       case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
246       case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
247         const struct ChppClient *client = chppClientOfHandle(context, handle);
248         if (client != NULL) {
249           minLen = client->minLength;
250         }
251         break;
252       }
253       default:
254         CHPP_LOGE("Invalid type=%d or H#%" PRIu8, messageType, handle);
255         return false;
256     }
257   }
258 
259   if (len < minLen) {
260     CHPP_LOGE("Datagram len=%" PRIuSIZE " < %" PRIuSIZE " for H#%" PRIu8, len,
261               minLen, handle);
262     return false;
263   }
264 
265   return true;
266 }
267 
268 /**
269  * Returns the dispatch function of a particular negotiated client/service
270  * handle and message type.
271  *
272  * Returns null if it is unsupported by the service.
273  *
274  * @param context State of the app layer.
275  * @param handle Handle number for the client/service.
276  * @param type Message type.
277  *
278  * @return Pointer to a function that dispatches incoming datagrams for any
279  * particular client/service.
280  */
chppGetDispatchFunction(struct ChppAppState * context,uint8_t handle,enum ChppMessageType type)281 static ChppDispatchFunction *chppGetDispatchFunction(
282     struct ChppAppState *context, uint8_t handle, enum ChppMessageType type) {
283   CHPP_DEBUG_NOT_NULL(context);
284   // chppDatagramLenIsOk() has already confirmed that the handle # is valid.
285   // Therefore, no additional checks are necessary for chppClientOfHandle(),
286   // chppServiceOfHandle(), or chppClientOrServiceStateOfHandle().
287 
288   // Make sure the client is open before it can receive any message:
289   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
290     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
291     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
292     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION: {
293       struct ChppEndpointState *clientState =
294           chppClientStateOfHandle(context, handle);
295       if (clientState->openState == CHPP_OPEN_STATE_CLOSED) {
296         CHPP_LOGE("RX service response but client closed");
297         return NULL;
298       }
299       break;
300     }
301     default:
302       // no check needed on the service side
303       break;
304   }
305 
306   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
307     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
308       return chppServiceOfHandle(context, handle)->requestDispatchFunctionPtr;
309     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
310       return chppClientOfHandle(context, handle)->responseDispatchFunctionPtr;
311     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
312       return chppClientOfHandle(context, handle)->requestDispatchFunctionPtr;
313     case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
314       return chppServiceOfHandle(context, handle)->responseDispatchFunctionPtr;
315     case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
316       return chppServiceOfHandle(context, handle)
317           ->notificationDispatchFunctionPtr;
318     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
319       return chppClientOfHandle(context, handle)
320           ->notificationDispatchFunctionPtr;
321   }
322 
323   return NULL;
324 }
325 
326 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
327 /**
328  * Returns the reset notification function pointer of a particular negotiated
329  * client.
330  *
331  * Returns null for clients that do not need or support a reset notification.
332  *
333  * @param context State of the app layer.
334  * @param index Index of the registered client.
335  *
336  * @return Pointer to the reset notification function.
337  */
chppGetClientResetNotifierFunction(struct ChppAppState * context,uint8_t index)338 static ChppNotifierFunction *chppGetClientResetNotifierFunction(
339     struct ChppAppState *context, uint8_t index) {
340   CHPP_DEBUG_NOT_NULL(context);
341   return context->registeredClients[index]->resetNotifierFunctionPtr;
342 }
343 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
344 
345 /**
346  * Returns the reset function pointer of a particular registered service.
347  *
348  * Returns null for services that do not need or support a reset notification.
349  *
350  * @param context State of the app layer.
351  * @param index Index of the registered service.
352  *
353  * @return Pointer to the reset function.
354  */
chppGetServiceResetNotifierFunction(struct ChppAppState * context,uint8_t index)355 ChppNotifierFunction *chppGetServiceResetNotifierFunction(
356     struct ChppAppState *context, uint8_t index) {
357   CHPP_DEBUG_NOT_NULL(context);
358   return context->registeredServices[index]->resetNotifierFunctionPtr;
359 }
360 
361 /**
362  * Returns a pointer to the ChppService struct of the service matched to a
363  * negotiated handle.
364  *
365  * Returns null if a service doesn't exist for the handle.
366  *
367  * @param context State of the app layer.
368  * @param handle Handle number.
369  *
370  * @return Pointer to the ChppService struct of a particular service handle.
371  */
chppServiceOfHandle(struct ChppAppState * context,uint8_t handle)372 static inline const struct ChppService *chppServiceOfHandle(
373     struct ChppAppState *context, uint8_t handle) {
374   CHPP_DEBUG_NOT_NULL(context);
375   uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
376   if (serviceIndex < context->registeredServiceCount) {
377     return context->registeredServices[serviceIndex];
378   }
379 
380   return NULL;
381 }
382 
383 /**
384  * Returns a pointer to the ChppClient struct of the client matched to a
385  * negotiated handle.
386  *
387  * Returns null if a client doesn't exist for the handle.
388  *
389  * @param context State of the app layer.
390  * @param handle Handle number.
391  *
392  * @return Pointer to the ChppClient struct matched to a particular handle.
393  */
chppClientOfHandle(struct ChppAppState * context,uint8_t handle)394 static inline const struct ChppClient *chppClientOfHandle(
395     struct ChppAppState *context, uint8_t handle) {
396   CHPP_DEBUG_NOT_NULL(context);
397   uint8_t serviceIndex = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
398   if (serviceIndex < context->discoveredServiceCount) {
399     uint8_t clientIndex = context->clientIndexOfServiceIndex[serviceIndex];
400     if (clientIndex < context->registeredClientCount) {
401       return context->registeredClients[clientIndex];
402     }
403   }
404 
405   return NULL;
406 }
407 
408 /**
409  * Returns the service state for a given handle.
410  *
411  * The caller must pass a valid handle.
412  *
413  * @param context State of the app layer.
414  * @param handle Handle number for the service.
415  *
416  * @return Pointer to a ChppEndpointState.
417  */
chppServiceStateOfHandle(struct ChppAppState * context,uint8_t handle)418 static inline struct ChppEndpointState *chppServiceStateOfHandle(
419     struct ChppAppState *context, uint8_t handle) {
420   CHPP_DEBUG_NOT_NULL(context);
421   CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
422                     context->registeredServiceCount);
423 
424   const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
425   return context->registeredServiceStates[serviceIdx];
426 }
427 
428 /**
429  * Returns a pointer to the client state for a given handle.
430  *
431  * The caller must pass a valid handle.
432  *
433  * @param context State of the app layer.
434  * @param handle Handle number for the service.
435  *
436  * @return Pointer to the endpoint state.
437  */
chppClientStateOfHandle(struct ChppAppState * context,uint8_t handle)438 static inline struct ChppEndpointState *chppClientStateOfHandle(
439     struct ChppAppState *context, uint8_t handle) {
440   CHPP_DEBUG_NOT_NULL(context);
441   CHPP_DEBUG_ASSERT(CHPP_SERVICE_INDEX_OF_HANDLE(handle) <
442                     context->registeredClientCount);
443   const uint8_t serviceIdx = CHPP_SERVICE_INDEX_OF_HANDLE(handle);
444   const uint8_t clientIdx = context->clientIndexOfServiceIndex[serviceIdx];
445   return context->registeredClientStates[clientIdx]->context;
446 }
447 
448 /**
449  * Returns a pointer to the client or service state for a given handle.
450  *
451  * The caller must pass a valid handle.
452  *
453  * @param appContext State of the app layer.
454  * @param handle Handle number for the service.
455  * @param type Message type (indicates if this is for a client or service).
456  *
457  * @return Pointer to the endpoint state (NULL if wrong type).
458  */
chppClientOrServiceStateOfHandle(struct ChppAppState * appContext,uint8_t handle,enum ChppMessageType type)459 static struct ChppEndpointState *chppClientOrServiceStateOfHandle(
460     struct ChppAppState *appContext, uint8_t handle,
461     enum ChppMessageType type) {
462   switch (CHPP_APP_GET_MESSAGE_TYPE(type)) {
463     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST:
464     case CHPP_MESSAGE_TYPE_CLIENT_RESPONSE:
465     case CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION:
466       return chppServiceStateOfHandle(appContext, handle);
467     case CHPP_MESSAGE_TYPE_SERVICE_REQUEST:
468     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE:
469     case CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION:
470       return chppClientStateOfHandle(appContext, handle);
471     default:
472       CHPP_LOGE("Unknown type=0x%" PRIx8 " (H#%" PRIu8 ")", type, handle);
473       return NULL;
474   }
475 }
476 
477 /**
478  * Processes a received datagram that is determined to be for a predefined CHPP
479  * service. Responds with an error if unsuccessful.
480  *
481  * Predefined requests are only sent by the client side.
482  * Predefined responses are only sent by the service side.
483  *
484  * @param context State of the app layer.
485  * @param buf Input data. Cannot be null.
486  * @param len Length of input data in bytes.
487  */
chppProcessPredefinedHandleDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)488 static void chppProcessPredefinedHandleDatagram(struct ChppAppState *context,
489                                                 uint8_t *buf, size_t len) {
490   CHPP_DEBUG_NOT_NULL(context);
491   CHPP_DEBUG_NOT_NULL(buf);
492 
493   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
494   bool success = false;
495 
496   switch (CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type)) {
497     case CHPP_MESSAGE_TYPE_CLIENT_REQUEST: {
498       success = chppProcessPredefinedClientRequest(context, buf, len);
499       break;
500     }
501     case CHPP_MESSAGE_TYPE_SERVICE_RESPONSE: {
502       success = chppProcessPredefinedServiceResponse(context, buf, len);
503       break;
504     }
505     default:
506       // Predefined client/services do not use
507       // - notifications,
508       // - service requests / client responses
509       break;
510   }
511 
512   if (!success) {
513     CHPP_LOGE("H#%" PRIu8 " undefined msg type=0x%" PRIx8 " (len=%" PRIuSIZE
514               ", ID=%" PRIu8 ")",
515               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
516     chppEnqueueTxErrorDatagram(context->transportContext,
517                                CHPP_TRANSPORT_ERROR_APPLAYER);
518   }
519 }
520 
521 /**
522  * Processes a received datagram that is determined to be for a negotiated CHPP
523  * client or service.
524  *
525  * The datagram is processed by the dispatch function matching the datagram
526  * type. @see ChppService and ChppClient.
527  *
528  * If a request dispatch function returns an error (anything different from
529  * CHPP_APP_ERROR_NONE) then an error response is automatically sent back to the
530  * remote endpoint.
531  *
532  * @param appContext State of the app layer.
533  * @param buf Input data. Cannot be null.
534  * @param len Length of input data in bytes.
535  */
chppProcessNegotiatedHandleDatagram(struct ChppAppState * appContext,uint8_t * buf,size_t len)536 static void chppProcessNegotiatedHandleDatagram(struct ChppAppState *appContext,
537                                                 uint8_t *buf, size_t len) {
538   CHPP_DEBUG_NOT_NULL(appContext);
539   CHPP_DEBUG_NOT_NULL(buf);
540 
541   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
542   enum ChppMessageType messageType = CHPP_APP_GET_MESSAGE_TYPE(rxHeader->type);
543 
544   // Could be either the client or the service state depending on the message
545   // type.
546   struct ChppEndpointState *endpointState = chppClientOrServiceStateOfHandle(
547       appContext, rxHeader->handle, messageType);
548   if (endpointState == NULL) {
549     CHPP_LOGE("H#%" PRIu8 " missing ctx (msg=0x%" PRIx8 " len=%" PRIuSIZE
550               ", ID=%" PRIu8 ")",
551               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
552     chppEnqueueTxErrorDatagram(appContext->transportContext,
553                                CHPP_TRANSPORT_ERROR_APPLAYER);
554     CHPP_DEBUG_ASSERT(false);
555     return;
556   }
557 
558   ChppDispatchFunction *dispatchFunc =
559       chppGetDispatchFunction(appContext, rxHeader->handle, messageType);
560   if (dispatchFunc == NULL) {
561     CHPP_LOGE("H#%" PRIu8 " unsupported msg=0x%" PRIx8 " (len=%" PRIuSIZE
562               ", ID=%" PRIu8 ")",
563               rxHeader->handle, rxHeader->type, len, rxHeader->transaction);
564     chppEnqueueTxErrorDatagram(appContext->transportContext,
565                                CHPP_TRANSPORT_ERROR_APPLAYER);
566     return;
567   }
568 
569   // All good. Dispatch datagram and possibly notify a waiting client
570   enum ChppAppErrorCode error = dispatchFunc(endpointState->context, buf, len);
571 
572   if (error != CHPP_APP_ERROR_NONE) {
573     CHPP_LOGE("RX dispatch err=0x%" PRIx16 " H#%" PRIu8 " type=0x%" PRIx8
574               " ID=%" PRIu8 " cmd=0x%" PRIx16 " len=%" PRIuSIZE,
575               error, rxHeader->handle, rxHeader->type, rxHeader->transaction,
576               rxHeader->command, len);
577 
578     // Requests require a dispatch failure response.
579     if (messageType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
580         messageType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST) {
581       struct ChppAppHeader *response =
582           chppAllocResponseFixed(rxHeader, struct ChppAppHeader);
583       if (response != NULL) {
584         response->error = (uint8_t)error;
585         chppEnqueueTxDatagramOrFail(appContext->transportContext, response,
586                                     sizeof(*response));
587       }
588     }
589     return;
590   }
591 
592   // Datagram is a response.
593   // Check for synchronous operation and notify waiting endpoint if needed.
594   if (messageType == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ||
595       messageType == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE) {
596     struct ChppSyncResponse *syncResponse = &endpointState->syncResponse;
597     chppMutexLock(&syncResponse->mutex);
598     syncResponse->ready = true;
599     CHPP_LOGD("Finished dispatching a response -> synchronous notification");
600     chppConditionVariableSignal(&syncResponse->condVar);
601     chppMutexUnlock(&syncResponse->mutex);
602   }
603 }
604 
605 /************************************************
606  *  Public Functions
607  ***********************************************/
608 
chppAppInit(struct ChppAppState * appContext,struct ChppTransportState * transportContext)609 void chppAppInit(struct ChppAppState *appContext,
610                  struct ChppTransportState *transportContext) {
611   // Default initialize all service/clients
612   struct ChppClientServiceSet set;
613   memset(&set, 0xff, sizeof(set));  // set all bits to 1
614 
615   chppAppInitWithClientServiceSet(appContext, transportContext, set);
616 }
617 
chppAppInitWithClientServiceSet(struct ChppAppState * appContext,struct ChppTransportState * transportContext,struct ChppClientServiceSet clientServiceSet)618 void chppAppInitWithClientServiceSet(
619     struct ChppAppState *appContext,
620     struct ChppTransportState *transportContext,
621     struct ChppClientServiceSet clientServiceSet) {
622   CHPP_NOT_NULL(appContext);
623   CHPP_DEBUG_NOT_NULL(transportContext);
624 
625   CHPP_LOGD("App Init");
626 
627   memset(appContext, 0, sizeof(*appContext));
628 
629   appContext->clientServiceSet = clientServiceSet;
630   appContext->transportContext = transportContext;
631   appContext->nextClientRequestTimeoutNs = CHPP_TIME_MAX;
632   appContext->nextServiceRequestTimeoutNs = CHPP_TIME_MAX;
633 
634   chppPalSystemApiInit(appContext);
635 
636 #ifdef CHPP_SERVICE_ENABLED
637   chppRegisterCommonServices(appContext);
638 #endif
639 
640 #ifdef CHPP_CLIENT_ENABLED
641   chppRegisterCommonClients(appContext);
642   chppInitBasicClients(appContext);
643 #endif
644 }
645 
chppAppDeinit(struct ChppAppState * appContext)646 void chppAppDeinit(struct ChppAppState *appContext) {
647   CHPP_LOGD("App deinit");
648 
649 #ifdef CHPP_CLIENT_ENABLED
650   chppDeinitMatchedClients(appContext);
651   chppDeinitBasicClients(appContext);
652   chppDeregisterCommonClients(appContext);
653 #endif
654 
655 #ifdef CHPP_SERVICE_ENABLED
656   chppDeregisterCommonServices(appContext);
657 #endif
658 
659   chppPalSystemApiDeinit(appContext);
660 }
661 
chppAppProcessRxDatagram(struct ChppAppState * context,uint8_t * buf,size_t len)662 void chppAppProcessRxDatagram(struct ChppAppState *context, uint8_t *buf,
663                               size_t len) {
664   CHPP_DEBUG_NOT_NULL(context);
665   CHPP_DEBUG_NOT_NULL(buf);
666 
667   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
668 
669   if (len == 0) {
670     CHPP_DEBUG_ASSERT_LOG(false, "App rx w/ len 0");
671 
672   } else if (len < sizeof(struct ChppAppHeader)) {
673     uint8_t *handle = (uint8_t *)buf;
674     CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8, len, *handle);
675 
676   } else if (rxHeader->error != CHPP_APP_ERROR_NONE) {
677     CHPP_LOGE("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
678               " ID=%" PRIu8 " ERR=%" PRIu8 " cmd=0x%" PRIx16,
679               len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
680               rxHeader->error, rxHeader->command);
681   } else {
682     CHPP_LOGD("RX datagram len=%" PRIuSIZE " H#%" PRIu8 " type=0x%" PRIx8
683               " ID=%" PRIu8 " err=%" PRIu8 " cmd=0x%" PRIx16,
684               len, rxHeader->handle, rxHeader->type, rxHeader->transaction,
685               rxHeader->error, rxHeader->command);
686   }
687 
688   if (!chppDatagramLenIsOk(context, rxHeader, len)) {
689     chppEnqueueTxErrorDatagram(context->transportContext,
690                                CHPP_TRANSPORT_ERROR_APPLAYER);
691 
692   } else {
693     if (rxHeader->handle == CHPP_HANDLE_NONE) {
694       chppDispatchNonHandle(context, buf, len);
695 
696     } else if (rxHeader->handle < CHPP_HANDLE_NEGOTIATED_RANGE_START) {
697       chppProcessPredefinedHandleDatagram(context, buf, len);
698 
699     } else {
700       chppProcessNegotiatedHandleDatagram(context, buf, len);
701     }
702   }
703 
704   chppDatagramProcessDoneCb(context->transportContext, buf);
705 }
706 
chppAppProcessReset(struct ChppAppState * context)707 void chppAppProcessReset(struct ChppAppState *context) {
708   CHPP_DEBUG_NOT_NULL(context);
709 
710 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
711   if (!context->isDiscoveryComplete) {
712     chppInitiateDiscovery(context);
713 
714   } else {
715     // Notify matched clients that a reset happened
716     for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
717       uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
718       if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
719         // Discovered service has a matched client
720         ChppNotifierFunction *ResetNotifierFunction =
721             chppGetClientResetNotifierFunction(context, clientIndex);
722 
723         CHPP_LOGD("Client #%" PRIu8 " (H#%d) reset notifier found=%d",
724                   clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i),
725                   (ResetNotifierFunction != NULL));
726 
727         if (ResetNotifierFunction != NULL) {
728           ResetNotifierFunction(
729               context->registeredClientStates[clientIndex]->context);
730         }
731       }
732     }
733   }
734 #endif  // CHPP_CLIENT_ENABLED_DISCOVERY
735 
736   // Notify registered services that a reset happened
737   for (uint8_t i = 0; i < context->registeredServiceCount; i++) {
738     ChppNotifierFunction *ResetNotifierFunction =
739         chppGetServiceResetNotifierFunction(context, i);
740 
741     CHPP_LOGD("Service #%" PRIu8 " (H#%d) reset notifier found=%d", i,
742               CHPP_SERVICE_HANDLE_OF_INDEX(i), (ResetNotifierFunction != NULL));
743 
744     if (ResetNotifierFunction != NULL) {
745       ResetNotifierFunction(context->registeredServiceStates[i]->context);
746     }
747   }
748 
749 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
750   // Reinitialize time offset
751   chppTimesyncClientReset(context);
752 #endif
753 }
754 
chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],char strOut[CHPP_SERVICE_UUID_STRING_LEN])755 void chppUuidToStr(const uint8_t uuid[CHPP_SERVICE_UUID_LEN],
756                    char strOut[CHPP_SERVICE_UUID_STRING_LEN]) {
757   snprintf(
758       strOut, CHPP_SERVICE_UUID_STRING_LEN,
759       "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
760       uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
761       uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],
762       uuid[15]);
763 }
764 
chppAppErrorToChreError(uint8_t chppError)765 uint8_t chppAppErrorToChreError(uint8_t chppError) {
766   switch (chppError) {
767     case CHPP_APP_ERROR_NONE:
768     case CHPP_APP_ERROR_INVALID_ARG:
769     case CHPP_APP_ERROR_BUSY:
770     case CHPP_APP_ERROR_OOM:
771     case CHPP_APP_ERROR_UNSUPPORTED:
772     case CHPP_APP_ERROR_TIMEOUT:
773     case CHPP_APP_ERROR_DISABLED:
774     case CHPP_APP_ERROR_RATELIMITED: {
775       // CHRE and CHPP error values are identical in these cases
776       return chppError;
777     }
778     default: {
779       return CHRE_ERROR;
780     }
781   }
782 }
783 
chppAppShortResponseErrorHandler(uint8_t * buf,size_t len,const char * responseName)784 uint8_t chppAppShortResponseErrorHandler(uint8_t *buf, size_t len,
785                                          const char *responseName) {
786   CHPP_DEBUG_NOT_NULL(buf);
787   CHPP_DEBUG_NOT_NULL(responseName);
788 
789   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
790   const struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
791 
792   if (rxHeader->error == CHPP_APP_ERROR_NONE) {
793     CHPP_LOGE("%s resp short len=%" PRIuSIZE, responseName, len);
794     return CHRE_ERROR;
795   }
796 
797   CHPP_LOGD("%s resp short len=%" PRIuSIZE, responseName, len);
798   return chppAppErrorToChreError(rxHeader->error);
799 }
800 
chppAllocNotification(uint8_t type,size_t len)801 struct ChppAppHeader *chppAllocNotification(uint8_t type, size_t len) {
802   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
803   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION ||
804               type == CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION);
805 
806   struct ChppAppHeader *notification = chppMalloc(len);
807   if (notification != NULL) {
808     notification->type = type;
809     notification->handle = CHPP_HANDLE_NONE;
810     notification->transaction = 0;
811     notification->error = CHPP_APP_ERROR_NONE;
812     notification->command = CHPP_APP_COMMAND_NONE;
813   } else {
814     CHPP_LOG_OOM();
815   }
816   return notification;
817 }
818 
chppAllocRequest(uint8_t type,struct ChppEndpointState * endpointState,size_t len)819 struct ChppAppHeader *chppAllocRequest(uint8_t type,
820                                        struct ChppEndpointState *endpointState,
821                                        size_t len) {
822   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
823   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
824               type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
825   CHPP_DEBUG_NOT_NULL(endpointState);
826 
827   struct ChppAppHeader *request = chppMalloc(len);
828   if (request != NULL) {
829     request->handle = endpointState->handle;
830     request->type = type;
831     request->transaction = endpointState->transaction;
832     request->error = CHPP_APP_ERROR_NONE;
833     request->command = CHPP_APP_COMMAND_NONE;
834 
835     endpointState->transaction++;
836   } else {
837     CHPP_LOG_OOM();
838   }
839   return request;
840 }
841 
chppAllocResponse(const struct ChppAppHeader * requestHeader,size_t len)842 struct ChppAppHeader *chppAllocResponse(
843     const struct ChppAppHeader *requestHeader, size_t len) {
844   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
845   CHPP_DEBUG_NOT_NULL(requestHeader);
846   uint8_t type = requestHeader->type;
847   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
848               type == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
849 
850   struct ChppAppHeader *response = chppMalloc(len);
851   if (response != NULL) {
852     *response = *requestHeader;
853     response->type = type == CHPP_MESSAGE_TYPE_CLIENT_REQUEST
854                          ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
855                          : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
856     response->error = CHPP_APP_ERROR_NONE;
857   } else {
858     CHPP_LOG_OOM();
859   }
860   return response;
861 }
862 
chppTimestampIncomingRequest(struct ChppIncomingRequestState * inReqState,const struct ChppAppHeader * requestHeader)863 void chppTimestampIncomingRequest(struct ChppIncomingRequestState *inReqState,
864                                   const struct ChppAppHeader *requestHeader) {
865   CHPP_DEBUG_NOT_NULL(inReqState);
866   CHPP_DEBUG_NOT_NULL(requestHeader);
867   if (inReqState->responseTimeNs == CHPP_TIME_NONE &&
868       inReqState->requestTimeNs != CHPP_TIME_NONE) {
869     CHPP_LOGE("RX dupe req t=%" PRIu64,
870               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
871   }
872   inReqState->requestTimeNs = chppGetCurrentTimeNs();
873   inReqState->responseTimeNs = CHPP_TIME_NONE;
874   inReqState->transaction = requestHeader->transaction;
875 }
876 
chppTimestampOutgoingRequest(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * requestHeader,uint64_t timeoutNs)877 void chppTimestampOutgoingRequest(struct ChppAppState *appState,
878                                   struct ChppOutgoingRequestState *outReqState,
879                                   const struct ChppAppHeader *requestHeader,
880                                   uint64_t timeoutNs) {
881   CHPP_DEBUG_NOT_NULL(appState);
882   CHPP_DEBUG_NOT_NULL(outReqState);
883   CHPP_DEBUG_NOT_NULL(requestHeader);
884   enum ChppMessageType msgType = requestHeader->type;
885   enum ChppEndpointType endpointType =
886       msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ? CHPP_ENDPOINT_CLIENT
887                                                   : CHPP_ENDPOINT_SERVICE;
888 
889   CHPP_ASSERT(msgType == CHPP_MESSAGE_TYPE_CLIENT_REQUEST ||
890               msgType == CHPP_MESSAGE_TYPE_SERVICE_REQUEST);
891 
892   // Hold the mutex to avoid concurrent read of a partially modified outReqState
893   // structure by the RX thread
894   chppMutexLock(&appState->transportContext->mutex);
895 
896   if (outReqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
897     CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
898               requestHeader->transaction, outReqState->transaction,
899               outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
900 
901     // Clear a possible pending timeout from the previous request
902     outReqState->responseTimeNs = CHPP_TIME_MAX;
903     chppRecalculateNextTimeout(appState, endpointType);
904   }
905 
906   outReqState->requestTimeNs = chppGetCurrentTimeNs();
907   outReqState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
908   outReqState->transaction = requestHeader->transaction;
909 
910   uint64_t *nextRequestTimeoutNs =
911       getNextRequestTimeoutNs(appState, endpointType);
912 
913   if (timeoutNs == CHPP_REQUEST_TIMEOUT_INFINITE) {
914     outReqState->responseTimeNs = CHPP_TIME_MAX;
915 
916   } else {
917     outReqState->responseTimeNs = timeoutNs + outReqState->requestTimeNs;
918 
919     *nextRequestTimeoutNs =
920         MIN(*nextRequestTimeoutNs, outReqState->responseTimeNs);
921   }
922 
923   chppMutexUnlock(&appState->transportContext->mutex);
924 
925   CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
926             " (requested=%" PRIu64 "), next timeout=%" PRIu64,
927             outReqState->transaction,
928             outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
929             outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
930             timeoutNs / CHPP_NSEC_PER_MSEC,
931             *nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
932 }
933 
chppTimestampIncomingResponse(struct ChppAppState * appState,struct ChppOutgoingRequestState * outReqState,const struct ChppAppHeader * responseHeader)934 bool chppTimestampIncomingResponse(struct ChppAppState *appState,
935                                    struct ChppOutgoingRequestState *outReqState,
936                                    const struct ChppAppHeader *responseHeader) {
937   CHPP_DEBUG_NOT_NULL(appState);
938   CHPP_DEBUG_NOT_NULL(outReqState);
939   CHPP_DEBUG_NOT_NULL(responseHeader);
940 
941   uint8_t type = responseHeader->type;
942 
943   CHPP_ASSERT(type == CHPP_MESSAGE_TYPE_CLIENT_RESPONSE ||
944               type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
945 
946   bool success = false;
947   uint64_t responseTime = chppGetCurrentTimeNs();
948 
949   switch (outReqState->requestState) {
950     case CHPP_REQUEST_STATE_NONE: {
951       CHPP_LOGE("Resp with no req t=%" PRIu64,
952                 responseTime / CHPP_NSEC_PER_MSEC);
953       break;
954     }
955 
956     case CHPP_REQUEST_STATE_RESPONSE_RCV: {
957       CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
958                 responseTime / CHPP_NSEC_PER_MSEC,
959                 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
960       break;
961     }
962 
963     case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
964       CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
965                 responseTime / CHPP_NSEC_PER_MSEC,
966                 outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
967       break;
968     }
969 
970     case CHPP_REQUEST_STATE_REQUEST_SENT: {
971       if (responseHeader->transaction != outReqState->transaction) {
972         CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
973                   " expected=%" PRIu8,
974                   responseHeader->transaction,
975                   responseTime / CHPP_NSEC_PER_MSEC, outReqState->transaction);
976       } else {
977         outReqState->requestState = (responseTime > outReqState->responseTimeNs)
978                                         ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
979                                         : CHPP_REQUEST_STATE_RESPONSE_RCV;
980         success = true;
981 
982         CHPP_LOGD(
983             "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
984             " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
985             outReqState->transaction,
986             outReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
987             responseTime / CHPP_NSEC_PER_MSEC,
988             outReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
989             (responseTime - outReqState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
990             (responseTime > outReqState->responseTimeNs) ? "yes" : "no");
991       }
992       break;
993     }
994 
995     default: {
996       CHPP_DEBUG_ASSERT_LOG(false, "Invalid req state");
997     }
998   }
999 
1000   if (success) {
1001     // When the received request is the next one that was expected
1002     // to timeout we need to recompute the timeout considering the
1003     // other pending requests.
1004     enum ChppEndpointType endpointType =
1005         type == CHPP_MESSAGE_TYPE_SERVICE_RESPONSE ? CHPP_ENDPOINT_CLIENT
1006                                                    : CHPP_ENDPOINT_SERVICE;
1007     if (outReqState->responseTimeNs ==
1008         *getNextRequestTimeoutNs(appState, endpointType)) {
1009       chppRecalculateNextTimeout(appState, endpointType);
1010     }
1011     outReqState->responseTimeNs = responseTime;
1012   }
1013   return success;
1014 }
1015 
chppTimestampOutgoingResponse(struct ChppIncomingRequestState * inReqState)1016 uint64_t chppTimestampOutgoingResponse(
1017     struct ChppIncomingRequestState *inReqState) {
1018   CHPP_DEBUG_NOT_NULL(inReqState);
1019 
1020   uint64_t previousResponseTime = inReqState->responseTimeNs;
1021   inReqState->responseTimeNs = chppGetCurrentTimeNs();
1022   return previousResponseTime;
1023 }
1024 
chppSendTimestampedResponseOrFail(struct ChppAppState * appState,struct ChppIncomingRequestState * inReqState,void * buf,size_t len)1025 bool chppSendTimestampedResponseOrFail(
1026     struct ChppAppState *appState, struct ChppIncomingRequestState *inReqState,
1027     void *buf, size_t len) {
1028   CHPP_DEBUG_NOT_NULL(appState);
1029   CHPP_DEBUG_NOT_NULL(inReqState);
1030   CHPP_DEBUG_NOT_NULL(buf);
1031   uint64_t previousResponseTime = chppTimestampOutgoingResponse(inReqState);
1032 
1033   if (inReqState->requestTimeNs == CHPP_TIME_NONE) {
1034     CHPP_LOGE("TX response w/ no req t=%" PRIu64,
1035               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC);
1036 
1037   } else if (previousResponseTime != CHPP_TIME_NONE) {
1038     CHPP_LOGW("TX additional response t=%" PRIu64 " for req t=%" PRIu64,
1039               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1040               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC);
1041 
1042   } else {
1043     CHPP_LOGD("Sending initial response at t=%" PRIu64
1044               " for request at t=%" PRIu64 " (RTT=%" PRIu64 ")",
1045               inReqState->responseTimeNs / CHPP_NSEC_PER_MSEC,
1046               inReqState->requestTimeNs / CHPP_NSEC_PER_MSEC,
1047               (inReqState->responseTimeNs - inReqState->requestTimeNs) /
1048                   CHPP_NSEC_PER_MSEC);
1049   }
1050 
1051   return chppEnqueueTxDatagramOrFail(appState->transportContext, buf, len);
1052 }
1053 
chppSendTimestampedRequestOrFail(struct ChppEndpointState * endpointState,struct ChppOutgoingRequestState * outReqState,void * buf,size_t len,uint64_t timeoutNs)1054 bool chppSendTimestampedRequestOrFail(
1055     struct ChppEndpointState *endpointState,
1056     struct ChppOutgoingRequestState *outReqState, void *buf, size_t len,
1057     uint64_t timeoutNs) {
1058   CHPP_DEBUG_NOT_NULL(outReqState);
1059   CHPP_DEBUG_NOT_NULL(buf);
1060   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
1061 
1062   if (timeoutNs < CHPP_TRANSPORT_TX_TIMEOUT_NS) {
1063     // The app layer sits above the transport layer.
1064     // Request timeout (app layer) should be longer than the transport timeout.
1065     CHPP_LOGW("Request timeout (%" PRIu64
1066               "ns) should be longer than the transport timeout (%" PRIu64 "ns)",
1067               timeoutNs, (uint64_t)CHPP_TRANSPORT_TX_TIMEOUT_NS);
1068   }
1069 
1070   chppTimestampOutgoingRequest(endpointState->appContext, outReqState, buf,
1071                                timeoutNs);
1072   endpointState->syncResponse.ready = false;
1073 
1074   bool success = chppEnqueueTxDatagramOrFail(
1075       endpointState->appContext->transportContext, buf, len);
1076 
1077   // Failure to enqueue a TX datagram means that a request was known to be not
1078   // transmitted. We explicitly set requestState to be in the NONE state, so
1079   // that unintended app layer timeouts do not occur.
1080   if (!success) {
1081     outReqState->requestState = CHPP_REQUEST_STATE_NONE;
1082   }
1083 
1084   return success;
1085 }
1086 
chppWaitForResponseWithTimeout(struct ChppSyncResponse * syncResponse,struct ChppOutgoingRequestState * outReqState,uint64_t timeoutNs)1087 bool chppWaitForResponseWithTimeout(
1088     struct ChppSyncResponse *syncResponse,
1089     struct ChppOutgoingRequestState *outReqState, uint64_t timeoutNs) {
1090   CHPP_DEBUG_NOT_NULL(syncResponse);
1091   CHPP_DEBUG_NOT_NULL(outReqState);
1092 
1093   bool result = true;
1094 
1095   chppMutexLock(&syncResponse->mutex);
1096 
1097   while (result && !syncResponse->ready) {
1098     result = chppConditionVariableTimedWait(&syncResponse->condVar,
1099                                             &syncResponse->mutex, timeoutNs);
1100   }
1101   if (!syncResponse->ready) {
1102     outReqState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1103     CHPP_LOGE("Response timeout after %" PRIu64 " ms",
1104               timeoutNs / CHPP_NSEC_PER_MSEC);
1105     result = false;
1106   }
1107 
1108   chppMutexUnlock(&syncResponse->mutex);
1109 
1110   return result;
1111 }
1112 
getRegisteredEndpointState(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1113 struct ChppEndpointState *getRegisteredEndpointState(
1114     struct ChppAppState *appState, uint8_t index, enum ChppEndpointType type) {
1115   CHPP_DEBUG_NOT_NULL(appState);
1116   CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1117 
1118   return type == CHPP_ENDPOINT_CLIENT
1119              ? appState->registeredClientStates[index]
1120              : appState->registeredServiceStates[index];
1121 }
1122 
getRegisteredEndpointOutReqCount(struct ChppAppState * appState,uint8_t index,enum ChppEndpointType type)1123 uint16_t getRegisteredEndpointOutReqCount(struct ChppAppState *appState,
1124                                           uint8_t index,
1125                                           enum ChppEndpointType type) {
1126   CHPP_DEBUG_NOT_NULL(appState);
1127   CHPP_DEBUG_ASSERT(index < getRegisteredEndpointCount(appState, type));
1128 
1129   return type == CHPP_ENDPOINT_CLIENT
1130              ? appState->registeredClients[index]->outReqCount
1131              : appState->registeredServices[index]->outReqCount;
1132 }
1133 
getRegisteredEndpointCount(struct ChppAppState * appState,enum ChppEndpointType type)1134 uint8_t getRegisteredEndpointCount(struct ChppAppState *appState,
1135                                    enum ChppEndpointType type) {
1136   return type == CHPP_ENDPOINT_CLIENT ? appState->registeredClientCount
1137                                       : appState->registeredServiceCount;
1138 }
1139 
chppRecalculateNextTimeout(struct ChppAppState * appState,enum ChppEndpointType type)1140 void chppRecalculateNextTimeout(struct ChppAppState *appState,
1141                                 enum ChppEndpointType type) {
1142   CHPP_DEBUG_NOT_NULL(appState);
1143 
1144   uint64_t timeoutNs = CHPP_TIME_MAX;
1145 
1146   const uint8_t endpointCount = getRegisteredEndpointCount(appState, type);
1147 
1148   for (uint8_t endpointIdx = 0; endpointIdx < endpointCount; endpointIdx++) {
1149     uint16_t reqCount =
1150         getRegisteredEndpointOutReqCount(appState, endpointIdx, type);
1151     struct ChppEndpointState *endpointState =
1152         getRegisteredEndpointState(appState, endpointIdx, type);
1153     struct ChppOutgoingRequestState *reqStates = endpointState->outReqStates;
1154     for (uint16_t cmdIdx = 0; cmdIdx < reqCount; cmdIdx++) {
1155       struct ChppOutgoingRequestState *reqState = &reqStates[cmdIdx];
1156 
1157       if (reqState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
1158         timeoutNs = MIN(timeoutNs, reqState->responseTimeNs);
1159       }
1160     }
1161   }
1162 
1163   CHPP_LOGD("nextReqTimeout=%" PRIu64, timeoutNs / CHPP_NSEC_PER_MSEC);
1164 
1165   if (type == CHPP_ENDPOINT_CLIENT) {
1166     appState->nextClientRequestTimeoutNs = timeoutNs;
1167   } else {
1168     appState->nextServiceRequestTimeoutNs = timeoutNs;
1169   }
1170 }
1171 
getNextRequestTimeoutNs(struct ChppAppState * appState,enum ChppEndpointType type)1172 uint64_t *getNextRequestTimeoutNs(struct ChppAppState *appState,
1173                                   enum ChppEndpointType type) {
1174   return type == CHPP_ENDPOINT_CLIENT ? &appState->nextClientRequestTimeoutNs
1175                                       : &appState->nextServiceRequestTimeoutNs;
1176 }
1177 
chppCloseOpenRequests(struct ChppEndpointState * endpointState,enum ChppEndpointType type,bool clearOnly)1178 void chppCloseOpenRequests(struct ChppEndpointState *endpointState,
1179                            enum ChppEndpointType type, bool clearOnly) {
1180   CHPP_DEBUG_NOT_NULL(endpointState);
1181 
1182   bool recalcNeeded = false;
1183 
1184   struct ChppAppState *appState = endpointState->appContext;
1185   const uint8_t enpointIdx = endpointState->index;
1186   const uint16_t cmdCount =
1187       getRegisteredEndpointOutReqCount(appState, enpointIdx, type);
1188 
1189   for (uint16_t cmdIdx = 0; cmdIdx < cmdCount; cmdIdx++) {
1190     if (endpointState->outReqStates[cmdIdx].requestState ==
1191         CHPP_REQUEST_STATE_REQUEST_SENT) {
1192       recalcNeeded = true;
1193 
1194       CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
1195 
1196       if (clearOnly) {
1197         endpointState->outReqStates[cmdIdx].requestState =
1198             CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
1199       } else {
1200         struct ChppAppHeader *response =
1201             chppMalloc(sizeof(struct ChppAppHeader));
1202         if (response == NULL) {
1203           CHPP_LOG_OOM();
1204         } else {
1205           // Simulate receiving a timeout response.
1206           response->handle = endpointState->handle;
1207           response->type = type == CHPP_ENDPOINT_CLIENT
1208                                ? CHPP_MESSAGE_TYPE_SERVICE_RESPONSE
1209                                : CHPP_MESSAGE_TYPE_CLIENT_RESPONSE;
1210           response->transaction =
1211               endpointState->outReqStates[cmdIdx].transaction;
1212           response->error = CHPP_APP_ERROR_TIMEOUT;
1213           response->command = cmdIdx;
1214 
1215           chppAppProcessRxDatagram(appState, (uint8_t *)response,
1216                                    sizeof(struct ChppAppHeader));
1217         }
1218       }
1219     }
1220   }
1221   if (recalcNeeded) {
1222     chppRecalculateNextTimeout(appState, type);
1223   }
1224 }