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 */ 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 */ 112 ChppClientDeinitFunction *chppGetClientDeinitFunction( 113 struct ChppAppState *context, uint8_t index) { 114 return context->registeredClients[index]->deinitFunctionPtr; 115 } 116 117 /************************************************ 118 * Public Functions 119 ***********************************************/ 120 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 475 void chppClientPseudoOpen(struct ChppClientState *clientState) { 476 clientState->pseudoOpen = true; 477 } 478 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 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 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 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