/* * 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/timesync.h" #include #include #include #include #include "chpp/app.h" #include "chpp/clients.h" #include "chpp/common/timesync.h" #include "chpp/log.h" #include "chpp/memory.h" #include "chpp/time.h" #include "chpp/transport.h" #include "chpp/clients/discovery.h" /************************************************ * Private Definitions ***********************************************/ /** * Structure to maintain state for the Timesync client and its Request/Response * (RR) functionality. */ struct ChppTimesyncClientState { struct ChppClientState client; // Timesync client state struct ChppRequestResponseState measureOffset; // Request response state struct ChppTimesyncResult timesyncResult; // Result of measureOffset }; /************************************************ * Public Functions ***********************************************/ void chppTimesyncClientInit(struct ChppAppState *context) { CHPP_LOGD("Timesync client init"); context->timesyncClientContext = chppMalloc(sizeof(struct ChppTimesyncClientState)); CHPP_NOT_NULL(context->timesyncClientContext); memset(context->timesyncClientContext, 0, sizeof(struct ChppTimesyncClientState)); context->timesyncClientContext->client.appContext = context; context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; chppClientInit(&context->timesyncClientContext->client, CHPP_HANDLE_TIMESYNC); context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_UNSPECIFIED; context->timesyncClientContext->client.openState = CHPP_OPEN_STATE_OPENED; } void chppTimesyncClientDeinit(struct ChppAppState *context) { CHPP_LOGD("Timesync client deinit"); CHPP_NOT_NULL(context->timesyncClientContext); chppClientDeinit(&context->timesyncClientContext->client); CHPP_FREE_AND_NULLIFY(context->timesyncClientContext); } void chppTimesyncClientReset(struct ChppAppState *context) { CHPP_LOGD("Timesync client reset"); CHPP_NOT_NULL(context->timesyncClientContext); context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; context->timesyncClientContext->timesyncResult.offsetNs = 0; context->timesyncClientContext->timesyncResult.rttNs = 0; context->timesyncClientContext->timesyncResult.measurementTimeNs = 0; } bool chppDispatchTimesyncServiceResponse(struct ChppAppState *context, const uint8_t *buf, size_t len) { CHPP_LOGD("Timesync client dispatch service response"); CHPP_NOT_NULL(context->timesyncClientContext); CHPP_NOT_NULL(buf); if (len < sizeof(struct ChppTimesyncResponse)) { CHPP_LOGE("Timesync resp short len=%" PRIuSIZE, len); context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_INVALID_LENGTH; return false; } const struct ChppTimesyncResponse *response = (const struct ChppTimesyncResponse *)buf; if (chppClientTimestampResponse( &context->timesyncClientContext->client, &context->timesyncClientContext->measureOffset, &response->header)) { context->timesyncClientContext->timesyncResult.rttNs = context->timesyncClientContext->measureOffset.responseTimeNs - context->timesyncClientContext->measureOffset.requestTimeNs; int64_t offsetNs = (int64_t)(response->timeNs - context->timesyncClientContext->measureOffset.responseTimeNs); int64_t offsetChangeNs = offsetNs - context->timesyncClientContext->timesyncResult.offsetNs; int64_t clippedOffsetChangeNs = offsetChangeNs; if (context->timesyncClientContext->timesyncResult.offsetNs != 0) { clippedOffsetChangeNs = MIN(clippedOffsetChangeNs, (int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS); clippedOffsetChangeNs = MAX(clippedOffsetChangeNs, -(int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS); } context->timesyncClientContext->timesyncResult.offsetNs += clippedOffsetChangeNs; if (offsetChangeNs != clippedOffsetChangeNs) { CHPP_LOGW("Drift=%" PRId64 " clipped to %" PRId64 " at t=%" PRIu64, offsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, context->timesyncClientContext->measureOffset.responseTimeNs / CHPP_NSEC_PER_MSEC); } else { context->timesyncClientContext->timesyncResult.measurementTimeNs = context->timesyncClientContext->measureOffset.responseTimeNs; } context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; CHPP_LOGI("Timesync RTT=%" PRIu64 " correction=%" PRId64 " offset=%" PRId64 " t=%" PRIu64, context->timesyncClientContext->timesyncResult.rttNs / CHPP_NSEC_PER_MSEC, clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, offsetNs / (int64_t)CHPP_NSEC_PER_MSEC, context->timesyncClientContext->timesyncResult.measurementTimeNs / CHPP_NSEC_PER_MSEC); } return true; } bool chppTimesyncMeasureOffset(struct ChppAppState *context) { bool result = false; CHPP_LOGI("Measuring timesync t=%" PRIu64, chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC); CHPP_NOT_NULL(context->timesyncClientContext); context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_BUSY; // A measurement is in progress struct ChppAppHeader *request = chppAllocClientRequestCommand( &context->timesyncClientContext->client, CHPP_TIMESYNC_COMMAND_GETTIME); size_t requestLen = sizeof(*request); if (request == NULL) { context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_OOM; CHPP_LOG_OOM(); } else if (!chppSendTimestampedRequestOrFail( &context->timesyncClientContext->client, &context->timesyncClientContext->measureOffset, request, requestLen, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE)) { context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_UNSPECIFIED; } else { result = true; } return result; } int64_t chppTimesyncGetOffset(struct ChppAppState *context, uint64_t maxTimesyncAgeNs) { bool timesyncNeverDone = (context->timesyncClientContext->timesyncResult.offsetNs == 0); bool timesyncIsStale = (chppGetCurrentTimeNs() - context->timesyncClientContext->timesyncResult.measurementTimeNs > maxTimesyncAgeNs); if (timesyncNeverDone || timesyncIsStale) { chppTimesyncMeasureOffset(context); } else { CHPP_LOGD("No need to timesync at t~=%" PRIu64 "offset=%" PRId64, chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC, context->timesyncClientContext->timesyncResult.offsetNs / (int64_t)CHPP_NSEC_PER_MSEC); } return context->timesyncClientContext->timesyncResult.offsetNs; } const struct ChppTimesyncResult *chppTimesyncGetResult( struct ChppAppState *context) { return &context->timesyncClientContext->timesyncResult; }