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