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 ChppClientState *clientState);
54 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 whetherthe client is ready.
69  */
chppIsClientApiReady(struct ChppClientState * clientState)70 static bool chppIsClientApiReady(struct ChppClientState *clientState) {
71   bool result = false;
72 
73   if (clientState->initialized) {
74     switch (clientState->openState) {
75       case (CHPP_OPEN_STATE_CLOSED):
76       case (CHPP_OPEN_STATE_WAITING_TO_OPEN): {
77         // result remains false
78         break;
79       }
80 
81       case (CHPP_OPEN_STATE_OPENED): {
82         result = true;
83         break;
84       }
85 
86       case (CHPP_OPEN_STATE_OPENING): {
87         // Allow the open request to go through
88         clientState->openState = CHPP_OPEN_STATE_WAITING_TO_OPEN;
89         result = true;
90         break;
91       }
92     }
93   }
94 
95   if (!result) {
96     CHPP_LOGE("Client not ready (everInit=%d, init=%d, open=%" PRIu8 ")",
97               clientState->everInitialized, clientState->initialized,
98               clientState->openState);
99   }
100   return result;
101 }
102 
103 /**
104  * Returns the deinitialization function pointer of a particular negotiated
105  * client.
106  *
107  * @param context Maintains status for each app layer instance.
108  * @param index Index of the registered client.
109  *
110  * @return Pointer to the match notification function.
111  */
chppGetClientDeinitFunction(struct ChppAppState * context,uint8_t index)112 ChppClientDeinitFunction *chppGetClientDeinitFunction(
113     struct ChppAppState *context, uint8_t index) {
114   return context->registeredClients[index]->deinitFunctionPtr;
115 }
116 
117 /************************************************
118  *  Public Functions
119  ***********************************************/
120 
chppRegisterCommonClients(struct ChppAppState * context)121 void chppRegisterCommonClients(struct ChppAppState *context) {
122   UNUSED_VAR(context);
123   CHPP_LOGD("Registering Clients");
124 
125 #ifdef CHPP_CLIENT_ENABLED_WWAN
126   if (context->clientServiceSet.wwanClient) {
127     chppRegisterWwanClient(context);
128   }
129 #endif
130 
131 #ifdef CHPP_CLIENT_ENABLED_WIFI
132   if (context->clientServiceSet.wifiClient) {
133     chppRegisterWifiClient(context);
134   }
135 #endif
136 
137 #ifdef CHPP_CLIENT_ENABLED_GNSS
138   if (context->clientServiceSet.gnssClient) {
139     chppRegisterGnssClient(context);
140   }
141 #endif
142 }
143 
chppDeregisterCommonClients(struct ChppAppState * context)144 void chppDeregisterCommonClients(struct ChppAppState *context) {
145   UNUSED_VAR(context);
146   CHPP_LOGD("Deregistering Clients");
147 
148 #ifdef CHPP_CLIENT_ENABLED_WWAN
149   if (context->clientServiceSet.wwanClient) {
150     chppDeregisterWwanClient(context);
151   }
152 #endif
153 
154 #ifdef CHPP_CLIENT_ENABLED_WIFI
155   if (context->clientServiceSet.wifiClient) {
156     chppDeregisterWifiClient(context);
157   }
158 #endif
159 
160 #ifdef CHPP_CLIENT_ENABLED_GNSS
161   if (context->clientServiceSet.gnssClient) {
162     chppDeregisterGnssClient(context);
163   }
164 #endif
165 }
166 
chppRegisterClient(struct ChppAppState * appContext,void * clientContext,struct ChppClientState * clientState,struct ChppRequestResponseState * rRStates,const struct ChppClient * newClient)167 void chppRegisterClient(struct ChppAppState *appContext, void *clientContext,
168                         struct ChppClientState *clientState,
169                         struct ChppRequestResponseState *rRStates,
170                         const struct ChppClient *newClient) {
171   CHPP_NOT_NULL(newClient);
172 
173   if (appContext->registeredClientCount >= CHPP_MAX_REGISTERED_CLIENTS) {
174     CHPP_LOGE("Max clients registered: %" PRIu8,
175               appContext->registeredClientCount);
176 
177   } else {
178     clientState->appContext = appContext;
179     clientState->rRStates = rRStates;
180     clientState->index = appContext->registeredClientCount;
181 
182     appContext->registeredClientContexts[appContext->registeredClientCount] =
183         clientContext;
184     appContext->registeredClientStates[appContext->registeredClientCount] =
185         clientState;
186     appContext->registeredClients[appContext->registeredClientCount] =
187         newClient;
188 
189     char uuidText[CHPP_SERVICE_UUID_STRING_LEN];
190     chppUuidToStr(newClient->descriptor.uuid, uuidText);
191     CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8
192               ".%" PRIu16 ", min_len=%" PRIuSIZE,
193               appContext->registeredClientCount, uuidText,
194               newClient->descriptor.version.major,
195               newClient->descriptor.version.minor,
196               newClient->descriptor.version.patch, newClient->minLength);
197 
198     appContext->registeredClientCount++;
199   }
200 }
201 
chppInitBasicClients(struct ChppAppState * context)202 void chppInitBasicClients(struct ChppAppState *context) {
203   UNUSED_VAR(context);
204   CHPP_LOGD("Initializing basic clients");
205 
206 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
207   if (context->clientServiceSet.loopbackClient) {
208     chppLoopbackClientInit(context);
209   }
210 #endif
211 
212 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
213   chppTimesyncClientInit(context);
214 #endif
215 
216 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
217   chppDiscoveryInit(context);
218 #endif
219 }
220 
chppClientInit(struct ChppClientState * clientState,uint8_t handle)221 void chppClientInit(struct ChppClientState *clientState, uint8_t handle) {
222   CHPP_ASSERT_LOG(!clientState->initialized,
223                   "Client H#%" PRIu8 " already initialized", handle);
224 
225   if (!clientState->everInitialized) {
226     clientState->handle = handle;
227     chppMutexInit(&clientState->responseMutex);
228     chppConditionVariableInit(&clientState->responseCondVar);
229     clientState->everInitialized = true;
230   }
231 
232   clientState->initialized = true;
233 }
234 
chppClientDeinit(struct ChppClientState * clientState)235 void chppClientDeinit(struct ChppClientState *clientState) {
236   CHPP_ASSERT_LOG(clientState->initialized,
237                   "Client H#%" PRIu8 " already deinitialized",
238                   clientState->handle);
239 
240   clientState->initialized = false;
241 }
242 
chppDeinitBasicClients(struct ChppAppState * context)243 void chppDeinitBasicClients(struct ChppAppState *context) {
244   UNUSED_VAR(context);
245   CHPP_LOGD("Deinitializing basic clients");
246 
247 #ifdef CHPP_CLIENT_ENABLED_LOOPBACK
248   if (context->clientServiceSet.loopbackClient) {
249     chppLoopbackClientDeinit(context);
250   }
251 #endif
252 
253 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
254   chppTimesyncClientDeinit(context);
255 #endif
256 
257 #ifdef CHPP_CLIENT_ENABLED_DISCOVERY
258   chppDiscoveryDeinit(context);
259 #endif
260 }
261 
chppDeinitMatchedClients(struct ChppAppState * context)262 void chppDeinitMatchedClients(struct ChppAppState *context) {
263   CHPP_LOGD("Deinitializing matched clients");
264 
265   for (uint8_t i = 0; i < context->discoveredServiceCount; i++) {
266     uint8_t clientIndex = context->clientIndexOfServiceIndex[i];
267     if (clientIndex != CHPP_CLIENT_INDEX_NONE) {
268       // Discovered service has a matched client
269       ChppClientDeinitFunction *clientDeinitFunction =
270           chppGetClientDeinitFunction(context, clientIndex);
271 
272       CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex,
273                 CHPP_SERVICE_HANDLE_OF_INDEX(i),
274                 (clientDeinitFunction != NULL));
275 
276       if (clientDeinitFunction != NULL) {
277         clientDeinitFunction(context->registeredClientContexts[clientIndex]);
278       }
279     }
280   }
281 }
282 
chppAllocClientRequest(struct ChppClientState * clientState,size_t len)283 struct ChppAppHeader *chppAllocClientRequest(
284     struct ChppClientState *clientState, size_t len) {
285   CHPP_ASSERT(len >= CHPP_APP_MIN_LEN_HEADER_WITH_TRANSACTION);
286 
287   struct ChppAppHeader *result = chppMalloc(len);
288   if (result != NULL) {
289     result->handle = clientState->handle;
290     result->type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
291     result->transaction = clientState->transaction;
292     result->error = CHPP_APP_ERROR_NONE;
293     result->command = CHPP_APP_COMMAND_NONE;
294 
295     clientState->transaction++;
296   }
297   return result;
298 }
299 
chppAllocClientRequestCommand(struct ChppClientState * clientState,uint16_t command)300 struct ChppAppHeader *chppAllocClientRequestCommand(
301     struct ChppClientState *clientState, uint16_t command) {
302   struct ChppAppHeader *result =
303       chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader));
304 
305   if (result != NULL) {
306     result->command = command;
307   }
308   return result;
309 }
310 
chppClientTimestampRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,struct ChppAppHeader * requestHeader,uint64_t timeoutNs)311 void chppClientTimestampRequest(struct ChppClientState *clientState,
312                                 struct ChppRequestResponseState *rRState,
313                                 struct ChppAppHeader *requestHeader,
314                                 uint64_t timeoutNs) {
315   if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
316     CHPP_LOGE("Dupe req ID=%" PRIu8 " existing ID=%" PRIu8 " from t=%" PRIu64,
317               requestHeader->transaction, rRState->transaction,
318               rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
319 
320     // Clear a possible pending timeout from the previous request
321     rRState->responseTimeNs = CHPP_TIME_MAX;
322     chppClientRecalculateNextTimeout(clientState->appContext);
323   }
324 
325   rRState->requestTimeNs = chppGetCurrentTimeNs();
326   rRState->requestState = CHPP_REQUEST_STATE_REQUEST_SENT;
327   rRState->transaction = requestHeader->transaction;
328 
329   if (timeoutNs == CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE) {
330     rRState->responseTimeNs = CHPP_TIME_MAX;
331 
332   } else {
333     rRState->responseTimeNs = timeoutNs + rRState->requestTimeNs;
334 
335     clientState->appContext->nextRequestTimeoutNs = MIN(
336         clientState->appContext->nextRequestTimeoutNs, rRState->responseTimeNs);
337   }
338 
339   CHPP_LOGD("Timestamp req ID=%" PRIu8 " at t=%" PRIu64 " timeout=%" PRIu64
340             " (requested=%" PRIu64 "), next timeout=%" PRIu64,
341             rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
342             rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
343             timeoutNs / CHPP_NSEC_PER_MSEC,
344             clientState->appContext->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
345 }
346 
chppClientTimestampResponse(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,const struct ChppAppHeader * responseHeader)347 bool chppClientTimestampResponse(struct ChppClientState *clientState,
348                                  struct ChppRequestResponseState *rRState,
349                                  const struct ChppAppHeader *responseHeader) {
350   bool success = false;
351   uint64_t responseTime = chppGetCurrentTimeNs();
352 
353   switch (rRState->requestState) {
354     case CHPP_REQUEST_STATE_NONE: {
355       CHPP_LOGE("Resp with no req t=%" PRIu64,
356                 responseTime / CHPP_NSEC_PER_MSEC);
357       break;
358     }
359 
360     case CHPP_REQUEST_STATE_RESPONSE_RCV: {
361       CHPP_LOGE("Extra resp at t=%" PRIu64 " for req t=%" PRIu64,
362                 responseTime / CHPP_NSEC_PER_MSEC,
363                 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
364       break;
365     }
366 
367     case CHPP_REQUEST_STATE_RESPONSE_TIMEOUT: {
368       CHPP_LOGE("Late resp at t=%" PRIu64 " for req t=%" PRIu64,
369                 responseTime / CHPP_NSEC_PER_MSEC,
370                 rRState->requestTimeNs / CHPP_NSEC_PER_MSEC);
371       break;
372     }
373 
374     case CHPP_REQUEST_STATE_REQUEST_SENT: {
375       if (responseHeader->transaction != rRState->transaction) {
376         CHPP_LOGE("Invalid resp ID=%" PRIu8 " at t=%" PRIu64
377                   " expected=%" PRIu8,
378                   responseHeader->transaction,
379                   responseTime / CHPP_NSEC_PER_MSEC, rRState->transaction);
380       } else {
381         rRState->requestState = (responseTime > rRState->responseTimeNs)
382                                     ? CHPP_REQUEST_STATE_RESPONSE_TIMEOUT
383                                     : CHPP_REQUEST_STATE_RESPONSE_RCV;
384         success = true;
385 
386         CHPP_LOGD(
387             "Timestamp resp ID=%" PRIu8 " req t=%" PRIu64 " resp t=%" PRIu64
388             " timeout t=%" PRIu64 " (RTT=%" PRIu64 ", timeout = %s)",
389             rRState->transaction, rRState->requestTimeNs / CHPP_NSEC_PER_MSEC,
390             responseTime / CHPP_NSEC_PER_MSEC,
391             rRState->responseTimeNs / CHPP_NSEC_PER_MSEC,
392             (responseTime - rRState->requestTimeNs) / CHPP_NSEC_PER_MSEC,
393             (responseTime > rRState->responseTimeNs) ? "yes" : "no");
394       }
395       break;
396     }
397 
398     default: {
399       CHPP_DEBUG_ASSERT(false);
400     }
401   }
402 
403   if (success) {
404     if (rRState->responseTimeNs ==
405         clientState->appContext->nextRequestTimeoutNs) {
406       // This was the next upcoming timeout
407       chppClientRecalculateNextTimeout(clientState->appContext);
408     }
409     rRState->responseTimeNs = responseTime;
410   }
411   return success;
412 }
413 
chppSendTimestampedRequestOrFail(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)414 bool chppSendTimestampedRequestOrFail(struct ChppClientState *clientState,
415                                       struct ChppRequestResponseState *rRState,
416                                       void *buf, size_t len,
417                                       uint64_t timeoutNs) {
418   CHPP_ASSERT(len >= CHPP_APP_MIN_LEN_HEADER_WITH_TRANSACTION);
419   if (!chppIsClientApiReady(clientState)) {
420     CHPP_FREE_AND_NULLIFY(buf);
421     return false;
422   }
423 
424   chppClientTimestampRequest(clientState, rRState, buf, timeoutNs);
425   clientState->responseReady = false;
426 
427   bool success = chppEnqueueTxDatagramOrFail(
428       clientState->appContext->transportContext, buf, len);
429 
430   // Failure to enqueue a TX datagram means that a request was known to be not
431   // transmitted. We explicitly set requestState to be in the NONE state, so
432   // that unintended app layer timeouts do not occur.
433   if (!success) {
434     rRState->requestState = CHPP_REQUEST_STATE_NONE;
435   }
436 
437   return success;
438 }
439 
chppSendTimestampedRequestAndWait(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len)440 bool chppSendTimestampedRequestAndWait(struct ChppClientState *clientState,
441                                        struct ChppRequestResponseState *rRState,
442                                        void *buf, size_t len) {
443   return chppSendTimestampedRequestAndWaitTimeout(
444       clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT);
445 }
446 
chppSendTimestampedRequestAndWaitTimeout(struct ChppClientState * clientState,struct ChppRequestResponseState * rRState,void * buf,size_t len,uint64_t timeoutNs)447 bool chppSendTimestampedRequestAndWaitTimeout(
448     struct ChppClientState *clientState,
449     struct ChppRequestResponseState *rRState, void *buf, size_t len,
450     uint64_t timeoutNs) {
451   bool result = chppSendTimestampedRequestOrFail(
452       clientState, rRState, buf, len, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
453 
454   if (result) {
455     chppMutexLock(&clientState->responseMutex);
456 
457     while (result && !clientState->responseReady) {
458       result = chppConditionVariableTimedWait(&clientState->responseCondVar,
459                                               &clientState->responseMutex,
460                                               timeoutNs);
461     }
462     if (!clientState->responseReady) {
463       rRState->requestState = CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
464       CHPP_LOGE("Response timeout after %" PRIu64 " ms",
465                 timeoutNs / CHPP_NSEC_PER_MSEC);
466       result = false;
467     }
468 
469     chppMutexUnlock(&clientState->responseMutex);
470   }
471 
472   return result;
473 }
474 
chppClientPseudoOpen(struct ChppClientState * clientState)475 void chppClientPseudoOpen(struct ChppClientState *clientState) {
476   clientState->pseudoOpen = true;
477 }
478 
chppClientSendOpenRequest(struct ChppClientState * clientState,struct ChppRequestResponseState * openRRState,uint16_t openCommand,bool blocking)479 bool chppClientSendOpenRequest(struct ChppClientState *clientState,
480                                struct ChppRequestResponseState *openRRState,
481                                uint16_t openCommand, bool blocking) {
482   bool result = false;
483   uint8_t priorState = clientState->openState;
484 
485 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
486   chppTimesyncMeasureOffset(clientState->appContext);
487 #endif
488 
489   struct ChppAppHeader *request =
490       chppAllocClientRequestCommand(clientState, openCommand);
491 
492   if (request == NULL) {
493     CHPP_LOG_OOM();
494 
495   } else {
496     clientState->openState = CHPP_OPEN_STATE_OPENING;
497 
498     if (blocking) {
499       CHPP_LOGD("Opening service - blocking");
500       result = chppSendTimestampedRequestAndWait(clientState, openRRState,
501                                                  request, sizeof(*request));
502     } else {
503       CHPP_LOGD("Opening service - non-blocking");
504       result = chppSendTimestampedRequestOrFail(
505           clientState, openRRState, request, sizeof(*request),
506           CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE);
507     }
508 
509     if (!result) {
510       CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d",
511                 priorState, clientState->pseudoOpen, blocking);
512       clientState->openState = CHPP_OPEN_STATE_CLOSED;
513 
514     } else if (blocking) {
515       result = (clientState->openState == CHPP_OPEN_STATE_OPENED);
516     }
517   }
518 
519   return result;
520 }
521 
chppClientProcessOpenResponse(struct ChppClientState * clientState,uint8_t * buf,size_t len)522 void chppClientProcessOpenResponse(struct ChppClientState *clientState,
523                                    uint8_t *buf, size_t len) {
524   UNUSED_VAR(len);  // Necessary depending on assert macro below
525   // Assert condition already guaranteed by chppAppProcessRxDatagram() but
526   // checking again since this is a public function
527   CHPP_ASSERT(len >= sizeof(struct ChppAppHeader));
528 
529   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
530   if (rxHeader->error != CHPP_APP_ERROR_NONE) {
531     CHPP_LOGE("Service open failed at service");
532     clientState->openState = CHPP_OPEN_STATE_CLOSED;
533   } else {
534     CHPP_LOGI("Service open succeeded at service");
535     clientState->openState = CHPP_OPEN_STATE_OPENED;
536   }
537 }
538 
chppClientRecalculateNextTimeout(struct ChppAppState * context)539 void chppClientRecalculateNextTimeout(struct ChppAppState *context) {
540   context->nextRequestTimeoutNs = CHPP_TIME_MAX;
541 
542   for (uint8_t clientIdx = 0; clientIdx < context->registeredClientCount;
543        clientIdx++) {
544     for (uint16_t cmdIdx = 0;
545          cmdIdx < context->registeredClients[clientIdx]->rRStateCount;
546          cmdIdx++) {
547       struct ChppRequestResponseState *rRState =
548           &context->registeredClientStates[clientIdx]->rRStates[cmdIdx];
549 
550       if (rRState->requestState == CHPP_REQUEST_STATE_REQUEST_SENT) {
551         context->nextRequestTimeoutNs =
552             MIN(context->nextRequestTimeoutNs, rRState->responseTimeNs);
553       }
554     }
555   }
556 
557   CHPP_LOGD("nextReqTimeout=%" PRIu64,
558             context->nextRequestTimeoutNs / CHPP_NSEC_PER_MSEC);
559 }
560 
chppClientCloseOpenRequests(struct ChppClientState * clientState,const struct ChppClient * client,bool clearOnly)561 void chppClientCloseOpenRequests(struct ChppClientState *clientState,
562                                  const struct ChppClient *client,
563                                  bool clearOnly) {
564   bool recalcNeeded = false;
565 
566   for (uint16_t cmdIdx = 0; cmdIdx < client->rRStateCount; cmdIdx++) {
567     if (clientState->rRStates[cmdIdx].requestState ==
568         CHPP_REQUEST_STATE_REQUEST_SENT) {
569       recalcNeeded = true;
570 
571       CHPP_LOGE("Closing open req #%" PRIu16 " clear %d", cmdIdx, clearOnly);
572 
573       if (clearOnly) {
574         clientState->rRStates[cmdIdx].requestState =
575             CHPP_REQUEST_STATE_RESPONSE_TIMEOUT;
576       } else {
577         struct ChppAppHeader *response =
578             chppMalloc(sizeof(struct ChppAppHeader));
579         if (response == NULL) {
580           CHPP_LOG_OOM();
581         } else {
582           response->handle = clientState->handle;
583           response->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
584           response->transaction = clientState->rRStates[cmdIdx].transaction;
585           response->error = CHPP_APP_ERROR_TIMEOUT;
586           response->command = cmdIdx;
587 
588           chppAppProcessRxDatagram(clientState->appContext, (uint8_t *)response,
589                                    sizeof(struct ChppAppHeader));
590         }
591       }
592     }
593   }
594 
595   if (recalcNeeded) {
596     chppClientRecalculateNextTimeout(clientState->appContext);
597   }
598 }
599