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/timesync.h" 18 19 #include <stdbool.h> 20 #include <stddef.h> 21 #include <stdint.h> 22 #include <string.h> 23 24 #include "chpp/app.h" 25 #include "chpp/clients.h" 26 #include "chpp/common/timesync.h" 27 #include "chpp/log.h" 28 #include "chpp/memory.h" 29 #include "chpp/time.h" 30 #include "chpp/transport.h" 31 32 #include "chpp/clients/discovery.h" 33 34 /************************************************ 35 * Private Definitions 36 ***********************************************/ 37 38 /** 39 * Structure to maintain state for the Timesync client and its Request/Response 40 * (RR) functionality. 41 */ 42 struct ChppTimesyncClientState { 43 struct ChppClientState client; // Timesync client state 44 struct ChppRequestResponseState measureOffset; // Request response state 45 46 struct ChppTimesyncResult timesyncResult; // Result of measureOffset 47 }; 48 49 /************************************************ 50 * Public Functions 51 ***********************************************/ 52 53 void chppTimesyncClientInit(struct ChppAppState *context) { 54 CHPP_LOGD("Timesync client init"); 55 56 context->timesyncClientContext = 57 chppMalloc(sizeof(struct ChppTimesyncClientState)); 58 CHPP_NOT_NULL(context->timesyncClientContext); 59 memset(context->timesyncClientContext, 0, 60 sizeof(struct ChppTimesyncClientState)); 61 62 context->timesyncClientContext->client.appContext = context; 63 context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; 64 65 chppClientInit(&context->timesyncClientContext->client, CHPP_HANDLE_TIMESYNC); 66 context->timesyncClientContext->timesyncResult.error = 67 CHPP_APP_ERROR_UNSPECIFIED; 68 context->timesyncClientContext->client.openState = CHPP_OPEN_STATE_OPENED; 69 } 70 71 void chppTimesyncClientDeinit(struct ChppAppState *context) { 72 CHPP_LOGD("Timesync client deinit"); 73 74 CHPP_NOT_NULL(context->timesyncClientContext); 75 chppClientDeinit(&context->timesyncClientContext->client); 76 CHPP_FREE_AND_NULLIFY(context->timesyncClientContext); 77 } 78 79 void chppTimesyncClientReset(struct ChppAppState *context) { 80 CHPP_LOGD("Timesync client reset"); 81 82 CHPP_NOT_NULL(context->timesyncClientContext); 83 84 context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; 85 context->timesyncClientContext->timesyncResult.offsetNs = 0; 86 context->timesyncClientContext->timesyncResult.rttNs = 0; 87 context->timesyncClientContext->timesyncResult.measurementTimeNs = 0; 88 } 89 90 bool chppDispatchTimesyncServiceResponse(struct ChppAppState *context, 91 const uint8_t *buf, size_t len) { 92 CHPP_LOGD("Timesync client dispatch service response"); 93 94 CHPP_NOT_NULL(context->timesyncClientContext); 95 CHPP_NOT_NULL(buf); 96 97 if (len < sizeof(struct ChppTimesyncResponse)) { 98 CHPP_LOGE("Timesync resp short len=%" PRIuSIZE, len); 99 context->timesyncClientContext->timesyncResult.error = 100 CHPP_APP_ERROR_INVALID_LENGTH; 101 return false; 102 } 103 104 const struct ChppTimesyncResponse *response = 105 (const struct ChppTimesyncResponse *)buf; 106 if (chppClientTimestampResponse( 107 &context->timesyncClientContext->client, 108 &context->timesyncClientContext->measureOffset, &response->header)) { 109 context->timesyncClientContext->timesyncResult.rttNs = 110 context->timesyncClientContext->measureOffset.responseTimeNs - 111 context->timesyncClientContext->measureOffset.requestTimeNs; 112 int64_t offsetNs = 113 (int64_t)(response->timeNs - 114 context->timesyncClientContext->measureOffset.responseTimeNs); 115 int64_t offsetChangeNs = 116 offsetNs - context->timesyncClientContext->timesyncResult.offsetNs; 117 118 int64_t clippedOffsetChangeNs = offsetChangeNs; 119 if (context->timesyncClientContext->timesyncResult.offsetNs != 0) { 120 clippedOffsetChangeNs = MIN(clippedOffsetChangeNs, 121 (int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS); 122 clippedOffsetChangeNs = MAX(clippedOffsetChangeNs, 123 -(int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS); 124 } 125 126 context->timesyncClientContext->timesyncResult.offsetNs += 127 clippedOffsetChangeNs; 128 129 if (offsetChangeNs != clippedOffsetChangeNs) { 130 CHPP_LOGW("Drift=%" PRId64 " clipped to %" PRId64 " at t=%" PRIu64, 131 offsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, 132 clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, 133 context->timesyncClientContext->measureOffset.responseTimeNs / 134 CHPP_NSEC_PER_MSEC); 135 } else { 136 context->timesyncClientContext->timesyncResult.measurementTimeNs = 137 context->timesyncClientContext->measureOffset.responseTimeNs; 138 } 139 140 context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_NONE; 141 142 CHPP_LOGI("Timesync RTT=%" PRIu64 " correction=%" PRId64 " offset=%" PRId64 143 " t=%" PRIu64, 144 context->timesyncClientContext->timesyncResult.rttNs / 145 CHPP_NSEC_PER_MSEC, 146 clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC, 147 offsetNs / (int64_t)CHPP_NSEC_PER_MSEC, 148 context->timesyncClientContext->timesyncResult.measurementTimeNs / 149 CHPP_NSEC_PER_MSEC); 150 } 151 152 return true; 153 } 154 155 bool chppTimesyncMeasureOffset(struct ChppAppState *context) { 156 bool result = false; 157 CHPP_LOGI("Measuring timesync t=%" PRIu64, 158 chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC); 159 160 CHPP_NOT_NULL(context->timesyncClientContext); 161 162 context->timesyncClientContext->timesyncResult.error = 163 CHPP_APP_ERROR_BUSY; // A measurement is in progress 164 165 struct ChppAppHeader *request = chppAllocClientRequestCommand( 166 &context->timesyncClientContext->client, CHPP_TIMESYNC_COMMAND_GETTIME); 167 size_t requestLen = sizeof(*request); 168 169 if (request == NULL) { 170 context->timesyncClientContext->timesyncResult.error = CHPP_APP_ERROR_OOM; 171 CHPP_LOG_OOM(); 172 173 } else if (!chppSendTimestampedRequestOrFail( 174 &context->timesyncClientContext->client, 175 &context->timesyncClientContext->measureOffset, request, 176 requestLen, CHPP_CLIENT_REQUEST_TIMEOUT_INFINITE)) { 177 context->timesyncClientContext->timesyncResult.error = 178 CHPP_APP_ERROR_UNSPECIFIED; 179 180 } else { 181 result = true; 182 } 183 184 return result; 185 } 186 187 int64_t chppTimesyncGetOffset(struct ChppAppState *context, 188 uint64_t maxTimesyncAgeNs) { 189 bool timesyncNeverDone = 190 (context->timesyncClientContext->timesyncResult.offsetNs == 0); 191 bool timesyncIsStale = 192 (chppGetCurrentTimeNs() - 193 context->timesyncClientContext->timesyncResult.measurementTimeNs > 194 maxTimesyncAgeNs); 195 196 if (timesyncNeverDone || timesyncIsStale) { 197 chppTimesyncMeasureOffset(context); 198 } else { 199 CHPP_LOGD("No need to timesync at t~=%" PRIu64 "offset=%" PRId64, 200 chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC, 201 context->timesyncClientContext->timesyncResult.offsetNs / 202 (int64_t)CHPP_NSEC_PER_MSEC); 203 } 204 205 return context->timesyncClientContext->timesyncResult.offsetNs; 206 } 207 208 const struct ChppTimesyncResult *chppTimesyncGetResult( 209 struct ChppAppState *context) { 210 return &context->timesyncClientContext->timesyncResult; 211 } 212