/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chpp/clients.h" #include #include #include #include #include "chpp/app.h" #ifdef CHPP_CLIENT_ENABLED_DISCOVERY #include "chpp/clients/discovery.h" #endif #ifdef CHPP_CLIENT_ENABLED_GNSS #include "chpp/clients/gnss.h" #endif #ifdef CHPP_CLIENT_ENABLED_LOOPBACK #include "chpp/clients/loopback.h" #endif #ifdef CHPP_CLIENT_ENABLED_TIMESYNC #include "chpp/clients/timesync.h" #endif #ifdef CHPP_CLIENT_ENABLED_WIFI #include "chpp/clients/wifi.h" #endif #ifdef CHPP_CLIENT_ENABLED_WWAN #include "chpp/clients/wwan.h" #endif #include "chpp/log.h" #include "chpp/macros.h" #include "chpp/memory.h" #include "chpp/time.h" #include "chpp/transport.h" /************************************************ * Prototypes ***********************************************/ static bool chppIsClientApiReady(struct ChppEndpointState *clientState); static ChppClientDeinitFunction *chppGetClientDeinitFunction( struct ChppAppState *context, uint8_t index); /************************************************ * Private Functions ***********************************************/ /** * Determines whether a client is ready to accept commands via its API (i.e. is * initialized and opened). If the client is in the process of reopening, it * will wait for the client to reopen. * * @param clientState State of the client sending the client request. * * @return Indicates whether the client is ready. */ static bool chppIsClientApiReady(struct ChppEndpointState *clientState) { CHPP_DEBUG_NOT_NULL(clientState); bool result = false; if (clientState->initialized) { switch (clientState->openState) { case (CHPP_OPEN_STATE_CLOSED): case (CHPP_OPEN_STATE_WAITING_TO_OPEN): { // result remains false break; } case (CHPP_OPEN_STATE_OPENED): { result = true; break; } case (CHPP_OPEN_STATE_OPENING): { // Allow the open request to go through clientState->openState = CHPP_OPEN_STATE_WAITING_TO_OPEN; result = true; break; } } } if (!result) { CHPP_LOGE("Client not ready (everInit=%d, init=%d, open=%" PRIu8 ")", clientState->everInitialized, clientState->initialized, clientState->openState); } return result; } /** * Returns the deinitialization function pointer of a particular negotiated * client. * * @param context Maintains status for each app layer instance. * @param index Index of the registered client. * * @return Pointer to the match notification function. */ static ChppClientDeinitFunction *chppGetClientDeinitFunction( struct ChppAppState *context, uint8_t index) { CHPP_DEBUG_NOT_NULL(context); return context->registeredClients[index]->deinitFunctionPtr; } /************************************************ * Public Functions ***********************************************/ void chppRegisterCommonClients(struct ChppAppState *context) { UNUSED_VAR(context); CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Registering Clients"); #ifdef CHPP_CLIENT_ENABLED_WWAN if (context->clientServiceSet.wwanClient) { chppRegisterWwanClient(context); } #endif #ifdef CHPP_CLIENT_ENABLED_WIFI if (context->clientServiceSet.wifiClient) { chppRegisterWifiClient(context); } #endif #ifdef CHPP_CLIENT_ENABLED_GNSS if (context->clientServiceSet.gnssClient) { chppRegisterGnssClient(context); } #endif } void chppDeregisterCommonClients(struct ChppAppState *context) { UNUSED_VAR(context); CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Deregistering Clients"); #ifdef CHPP_CLIENT_ENABLED_WWAN if (context->clientServiceSet.wwanClient) { chppDeregisterWwanClient(context); } #endif #ifdef CHPP_CLIENT_ENABLED_WIFI if (context->clientServiceSet.wifiClient) { chppDeregisterWifiClient(context); } #endif #ifdef CHPP_CLIENT_ENABLED_GNSS if (context->clientServiceSet.gnssClient) { chppDeregisterGnssClient(context); } #endif } void chppRegisterClient(struct ChppAppState *appContext, void *clientContext, struct ChppEndpointState *clientState, struct ChppOutgoingRequestState *outReqStates, const struct ChppClient *newClient) { CHPP_NOT_NULL(newClient); CHPP_DEBUG_NOT_NULL(appContext); CHPP_DEBUG_NOT_NULL(clientContext); CHPP_DEBUG_NOT_NULL(clientState); CHPP_DEBUG_NOT_NULL(newClient); if (appContext->registeredClientCount >= CHPP_MAX_REGISTERED_CLIENTS) { CHPP_LOGE("Max clients registered: %" PRIu8, appContext->registeredClientCount); return; } clientState->appContext = appContext; clientState->outReqStates = outReqStates; clientState->index = appContext->registeredClientCount; clientState->context = clientContext; appContext->registeredClientStates[appContext->registeredClientCount] = clientState; appContext->registeredClients[appContext->registeredClientCount] = newClient; char uuidText[CHPP_SERVICE_UUID_STRING_LEN]; chppUuidToStr(newClient->descriptor.uuid, uuidText); CHPP_LOGD("Client # %" PRIu8 " UUID=%s, version=%" PRIu8 ".%" PRIu8 ".%" PRIu16 ", min_len=%" PRIuSIZE, appContext->registeredClientCount, uuidText, newClient->descriptor.version.major, newClient->descriptor.version.minor, newClient->descriptor.version.patch, newClient->minLength); appContext->registeredClientCount++; } void chppInitBasicClients(struct ChppAppState *context) { UNUSED_VAR(context); CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Initializing basic clients"); #ifdef CHPP_CLIENT_ENABLED_LOOPBACK if (context->clientServiceSet.loopbackClient) { chppLoopbackClientInit(context); } #endif #ifdef CHPP_CLIENT_ENABLED_TIMESYNC chppTimesyncClientInit(context); #endif #ifdef CHPP_CLIENT_ENABLED_DISCOVERY chppDiscoveryInit(context); #endif } void chppClientInit(struct ChppEndpointState *clientState, uint8_t handle) { CHPP_DEBUG_NOT_NULL(clientState); CHPP_ASSERT_LOG(!clientState->initialized, "Client H#%" PRIu8 " already initialized", handle); if (!clientState->everInitialized) { clientState->handle = handle; chppMutexInit(&clientState->syncResponse.mutex); chppConditionVariableInit(&clientState->syncResponse.condVar); clientState->everInitialized = true; } clientState->initialized = true; } void chppClientDeinit(struct ChppEndpointState *clientState) { CHPP_DEBUG_NOT_NULL(clientState); CHPP_ASSERT_LOG(clientState->initialized, "Client H#%" PRIu8 " already deinitialized", clientState->handle); clientState->initialized = false; } void chppDeinitBasicClients(struct ChppAppState *context) { UNUSED_VAR(context); CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Deinitializing basic clients"); #ifdef CHPP_CLIENT_ENABLED_LOOPBACK if (context->clientServiceSet.loopbackClient) { chppLoopbackClientDeinit(context); } #endif #ifdef CHPP_CLIENT_ENABLED_TIMESYNC chppTimesyncClientDeinit(context); #endif #ifdef CHPP_CLIENT_ENABLED_DISCOVERY chppDiscoveryDeinit(context); #endif } void chppDeinitMatchedClients(struct ChppAppState *context) { CHPP_DEBUG_NOT_NULL(context); CHPP_LOGD("Deinitializing matched clients"); for (uint8_t i = 0; i < context->discoveredServiceCount; i++) { uint8_t clientIndex = context->clientIndexOfServiceIndex[i]; if (clientIndex != CHPP_CLIENT_INDEX_NONE) { // Discovered service has a matched client ChppClientDeinitFunction *clientDeinitFunction = chppGetClientDeinitFunction(context, clientIndex); CHPP_LOGD("Client #%" PRIu8 " (H#%d) deinit fp found=%d", clientIndex, CHPP_SERVICE_HANDLE_OF_INDEX(i), (clientDeinitFunction != NULL)); if (clientDeinitFunction != NULL) { clientDeinitFunction( context->registeredClientStates[clientIndex]->context); } } } } struct ChppAppHeader *chppAllocClientRequest( struct ChppEndpointState *clientState, size_t len) { CHPP_DEBUG_NOT_NULL(clientState); return chppAllocRequest(CHPP_MESSAGE_TYPE_CLIENT_REQUEST, clientState, len); } struct ChppAppHeader *chppAllocClientRequestCommand( struct ChppEndpointState *clientState, uint16_t command) { struct ChppAppHeader *request = chppAllocClientRequest(clientState, sizeof(struct ChppAppHeader)); if (request != NULL) { request->command = command; } return request; } bool chppClientSendTimestampedRequestOrFail( struct ChppEndpointState *clientState, struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, uint64_t timeoutNs) { CHPP_DEBUG_NOT_NULL(clientState); CHPP_DEBUG_NOT_NULL(outReqState); CHPP_DEBUG_NOT_NULL(buf); if (!chppIsClientApiReady(clientState)) { CHPP_FREE_AND_NULLIFY(buf); return false; } return chppSendTimestampedRequestOrFail(clientState, outReqState, buf, len, timeoutNs); } bool chppClientSendTimestampedRequestAndWait( struct ChppEndpointState *clientState, struct ChppOutgoingRequestState *outReqState, void *buf, size_t len) { return chppClientSendTimestampedRequestAndWaitTimeout( clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_DEFAULT); } bool chppClientSendTimestampedRequestAndWaitTimeout( struct ChppEndpointState *clientState, struct ChppOutgoingRequestState *outReqState, void *buf, size_t len, uint64_t timeoutNs) { bool result = chppClientSendTimestampedRequestOrFail( clientState, outReqState, buf, len, CHPP_REQUEST_TIMEOUT_INFINITE); if (!result) { return false; } return chppWaitForResponseWithTimeout(&clientState->syncResponse, outReqState, timeoutNs); } void chppClientPseudoOpen(struct ChppEndpointState *clientState) { clientState->pseudoOpen = true; } bool chppClientSendOpenRequest(struct ChppEndpointState *clientState, struct ChppOutgoingRequestState *openReqState, uint16_t openCommand, bool blocking) { CHPP_NOT_NULL(clientState); CHPP_NOT_NULL(openReqState); bool result = false; uint8_t priorState = clientState->openState; #ifdef CHPP_CLIENT_ENABLED_TIMESYNC chppTimesyncMeasureOffset(clientState->appContext); #endif struct ChppAppHeader *request = chppAllocClientRequestCommand(clientState, openCommand); if (request == NULL) { return false; } clientState->openState = CHPP_OPEN_STATE_OPENING; if (blocking) { CHPP_LOGD("Opening service - blocking"); result = chppClientSendTimestampedRequestAndWait(clientState, openReqState, request, sizeof(*request)); } else { CHPP_LOGD("Opening service - non-blocking"); result = chppClientSendTimestampedRequestOrFail( clientState, openReqState, request, sizeof(*request), CHPP_REQUEST_TIMEOUT_INFINITE); } if (!result) { CHPP_LOGE("Service open fail from state=%" PRIu8 " psudo=%d blocking=%d", priorState, clientState->pseudoOpen, blocking); clientState->openState = CHPP_OPEN_STATE_CLOSED; } else if (blocking) { result = (clientState->openState == CHPP_OPEN_STATE_OPENED); } return result; } void chppClientProcessOpenResponse(struct ChppEndpointState *clientState, uint8_t *buf, size_t len) { CHPP_DEBUG_NOT_NULL(clientState); CHPP_DEBUG_NOT_NULL(buf); UNUSED_VAR(len); // Necessary depending on assert macro below // Assert condition already guaranteed by chppAppProcessRxDatagram() but // checking again since this is a public function CHPP_ASSERT(len >= sizeof(struct ChppAppHeader)); struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf; if (rxHeader->error != CHPP_APP_ERROR_NONE) { CHPP_LOGE("Service open failed at service"); clientState->openState = CHPP_OPEN_STATE_CLOSED; } else { CHPP_LOGD("Service open succeeded at service"); clientState->openState = CHPP_OPEN_STATE_OPENED; } } void chppClientCloseOpenRequests(struct ChppEndpointState *clientState, const struct ChppClient *client, bool clearOnly) { UNUSED_VAR(client); chppCloseOpenRequests(clientState, CHPP_ENDPOINT_CLIENT, clearOnly); } struct ChppAppHeader *chppAllocClientNotification(size_t len) { return chppAllocNotification(CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION, len); }