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
chppTimesyncClientInit(struct ChppAppState * context)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
chppTimesyncClientDeinit(struct ChppAppState * context)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
chppTimesyncClientReset(struct ChppAppState * context)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
chppDispatchTimesyncServiceResponse(struct ChppAppState * context,const uint8_t * buf,size_t len)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
chppTimesyncMeasureOffset(struct ChppAppState * context)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
chppTimesyncGetOffset(struct ChppAppState * context,uint64_t maxTimesyncAgeNs)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
chppTimesyncGetResult(struct ChppAppState * context)208 const struct ChppTimesyncResult *chppTimesyncGetResult(
209 struct ChppAppState *context) {
210 return &context->timesyncClientContext->timesyncResult;
211 }
212