1*84e33947SAndroid Build Coastguard Worker /*
2*84e33947SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*84e33947SAndroid Build Coastguard Worker *
4*84e33947SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*84e33947SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*84e33947SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*84e33947SAndroid Build Coastguard Worker *
8*84e33947SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*84e33947SAndroid Build Coastguard Worker *
10*84e33947SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*84e33947SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*84e33947SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*84e33947SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*84e33947SAndroid Build Coastguard Worker * limitations under the License.
15*84e33947SAndroid Build Coastguard Worker */
16*84e33947SAndroid Build Coastguard Worker
17*84e33947SAndroid Build Coastguard Worker #include "chpp/clients/timesync.h"
18*84e33947SAndroid Build Coastguard Worker
19*84e33947SAndroid Build Coastguard Worker #include <stdbool.h>
20*84e33947SAndroid Build Coastguard Worker #include <stddef.h>
21*84e33947SAndroid Build Coastguard Worker #include <stdint.h>
22*84e33947SAndroid Build Coastguard Worker #include <string.h>
23*84e33947SAndroid Build Coastguard Worker
24*84e33947SAndroid Build Coastguard Worker #include "chpp/app.h"
25*84e33947SAndroid Build Coastguard Worker #include "chpp/clients.h"
26*84e33947SAndroid Build Coastguard Worker #include "chpp/common/timesync.h"
27*84e33947SAndroid Build Coastguard Worker #include "chpp/log.h"
28*84e33947SAndroid Build Coastguard Worker #include "chpp/memory.h"
29*84e33947SAndroid Build Coastguard Worker #include "chpp/time.h"
30*84e33947SAndroid Build Coastguard Worker #include "chpp/transport.h"
31*84e33947SAndroid Build Coastguard Worker
32*84e33947SAndroid Build Coastguard Worker #include "chpp/clients/discovery.h"
33*84e33947SAndroid Build Coastguard Worker
34*84e33947SAndroid Build Coastguard Worker /************************************************
35*84e33947SAndroid Build Coastguard Worker * Private Definitions
36*84e33947SAndroid Build Coastguard Worker ***********************************************/
37*84e33947SAndroid Build Coastguard Worker
38*84e33947SAndroid Build Coastguard Worker /**
39*84e33947SAndroid Build Coastguard Worker * Structure to maintain state for the Timesync client and its Request/Response
40*84e33947SAndroid Build Coastguard Worker * (RR) functionality.
41*84e33947SAndroid Build Coastguard Worker */
42*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState {
43*84e33947SAndroid Build Coastguard Worker struct ChppEndpointState client; // CHPP client state
44*84e33947SAndroid Build Coastguard Worker struct ChppOutgoingRequestState measureOffset; // Request response state
45*84e33947SAndroid Build Coastguard Worker
46*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncResult timesyncResult; // Result of measureOffset
47*84e33947SAndroid Build Coastguard Worker };
48*84e33947SAndroid Build Coastguard Worker
49*84e33947SAndroid Build Coastguard Worker /************************************************
50*84e33947SAndroid Build Coastguard Worker * Public Functions
51*84e33947SAndroid Build Coastguard Worker ***********************************************/
52*84e33947SAndroid Build Coastguard Worker
chppTimesyncClientInit(struct ChppAppState * appState)53*84e33947SAndroid Build Coastguard Worker void chppTimesyncClientInit(struct ChppAppState *appState) {
54*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Timesync client init");
55*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
56*84e33947SAndroid Build Coastguard Worker
57*84e33947SAndroid Build Coastguard Worker appState->timesyncClientContext =
58*84e33947SAndroid Build Coastguard Worker chppMalloc(sizeof(struct ChppTimesyncClientState));
59*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(appState->timesyncClientContext);
60*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState *state = appState->timesyncClientContext;
61*84e33947SAndroid Build Coastguard Worker
62*84e33947SAndroid Build Coastguard Worker memset(state, 0, sizeof(struct ChppTimesyncClientState));
63*84e33947SAndroid Build Coastguard Worker state->client.appContext = appState;
64*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_NONE;
65*84e33947SAndroid Build Coastguard Worker
66*84e33947SAndroid Build Coastguard Worker chppClientInit(&state->client, CHPP_HANDLE_TIMESYNC);
67*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_UNSPECIFIED;
68*84e33947SAndroid Build Coastguard Worker state->client.openState = CHPP_OPEN_STATE_OPENED;
69*84e33947SAndroid Build Coastguard Worker }
70*84e33947SAndroid Build Coastguard Worker
chppTimesyncClientDeinit(struct ChppAppState * appState)71*84e33947SAndroid Build Coastguard Worker void chppTimesyncClientDeinit(struct ChppAppState *appState) {
72*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Timesync client deinit");
73*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
74*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(appState->timesyncClientContext);
75*84e33947SAndroid Build Coastguard Worker chppClientDeinit(&appState->timesyncClientContext->client);
76*84e33947SAndroid Build Coastguard Worker CHPP_FREE_AND_NULLIFY(appState->timesyncClientContext);
77*84e33947SAndroid Build Coastguard Worker }
78*84e33947SAndroid Build Coastguard Worker
chppTimesyncClientReset(struct ChppAppState * appState)79*84e33947SAndroid Build Coastguard Worker void chppTimesyncClientReset(struct ChppAppState *appState) {
80*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Timesync client reset");
81*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
82*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState *state = appState->timesyncClientContext;
83*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(state);
84*84e33947SAndroid Build Coastguard Worker
85*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_NONE;
86*84e33947SAndroid Build Coastguard Worker state->timesyncResult.offsetNs = 0;
87*84e33947SAndroid Build Coastguard Worker state->timesyncResult.rttNs = 0;
88*84e33947SAndroid Build Coastguard Worker state->timesyncResult.measurementTimeNs = 0;
89*84e33947SAndroid Build Coastguard Worker }
90*84e33947SAndroid Build Coastguard Worker
chppDispatchTimesyncServiceResponse(struct ChppAppState * appState,const uint8_t * buf,size_t len)91*84e33947SAndroid Build Coastguard Worker bool chppDispatchTimesyncServiceResponse(struct ChppAppState *appState,
92*84e33947SAndroid Build Coastguard Worker const uint8_t *buf, size_t len) {
93*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Timesync client dispatch service response");
94*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
95*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState *state = appState->timesyncClientContext;
96*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(state);
97*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(buf);
98*84e33947SAndroid Build Coastguard Worker
99*84e33947SAndroid Build Coastguard Worker if (len < sizeof(struct ChppTimesyncResponse)) {
100*84e33947SAndroid Build Coastguard Worker CHPP_LOGE("Timesync resp short len=%" PRIuSIZE, len);
101*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_INVALID_LENGTH;
102*84e33947SAndroid Build Coastguard Worker return false;
103*84e33947SAndroid Build Coastguard Worker }
104*84e33947SAndroid Build Coastguard Worker
105*84e33947SAndroid Build Coastguard Worker const struct ChppTimesyncResponse *response =
106*84e33947SAndroid Build Coastguard Worker (const struct ChppTimesyncResponse *)buf;
107*84e33947SAndroid Build Coastguard Worker if (chppTimestampIncomingResponse(state->client.appContext,
108*84e33947SAndroid Build Coastguard Worker &state->measureOffset, &response->header)) {
109*84e33947SAndroid Build Coastguard Worker state->timesyncResult.rttNs = state->measureOffset.responseTimeNs -
110*84e33947SAndroid Build Coastguard Worker state->measureOffset.requestTimeNs;
111*84e33947SAndroid Build Coastguard Worker int64_t offsetNs =
112*84e33947SAndroid Build Coastguard Worker (int64_t)(response->timeNs - state->measureOffset.responseTimeNs);
113*84e33947SAndroid Build Coastguard Worker int64_t offsetChangeNs = offsetNs - state->timesyncResult.offsetNs;
114*84e33947SAndroid Build Coastguard Worker
115*84e33947SAndroid Build Coastguard Worker int64_t clippedOffsetChangeNs = offsetChangeNs;
116*84e33947SAndroid Build Coastguard Worker if (state->timesyncResult.offsetNs != 0) {
117*84e33947SAndroid Build Coastguard Worker clippedOffsetChangeNs = MIN(clippedOffsetChangeNs,
118*84e33947SAndroid Build Coastguard Worker (int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS);
119*84e33947SAndroid Build Coastguard Worker clippedOffsetChangeNs = MAX(clippedOffsetChangeNs,
120*84e33947SAndroid Build Coastguard Worker -(int64_t)CHPP_CLIENT_TIMESYNC_MAX_CHANGE_NS);
121*84e33947SAndroid Build Coastguard Worker }
122*84e33947SAndroid Build Coastguard Worker
123*84e33947SAndroid Build Coastguard Worker state->timesyncResult.offsetNs += clippedOffsetChangeNs;
124*84e33947SAndroid Build Coastguard Worker
125*84e33947SAndroid Build Coastguard Worker if (offsetChangeNs != clippedOffsetChangeNs) {
126*84e33947SAndroid Build Coastguard Worker CHPP_LOGW("Drift=%" PRId64 " clipped to %" PRId64 " at t=%" PRIu64,
127*84e33947SAndroid Build Coastguard Worker offsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC,
128*84e33947SAndroid Build Coastguard Worker clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC,
129*84e33947SAndroid Build Coastguard Worker state->measureOffset.responseTimeNs / CHPP_NSEC_PER_MSEC);
130*84e33947SAndroid Build Coastguard Worker } else {
131*84e33947SAndroid Build Coastguard Worker state->timesyncResult.measurementTimeNs =
132*84e33947SAndroid Build Coastguard Worker state->measureOffset.responseTimeNs;
133*84e33947SAndroid Build Coastguard Worker }
134*84e33947SAndroid Build Coastguard Worker
135*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_NONE;
136*84e33947SAndroid Build Coastguard Worker
137*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Timesync RTT=%" PRIu64 " correction=%" PRId64 " offset=%" PRId64
138*84e33947SAndroid Build Coastguard Worker " t=%" PRIu64,
139*84e33947SAndroid Build Coastguard Worker state->timesyncResult.rttNs / CHPP_NSEC_PER_MSEC,
140*84e33947SAndroid Build Coastguard Worker clippedOffsetChangeNs / (int64_t)CHPP_NSEC_PER_MSEC,
141*84e33947SAndroid Build Coastguard Worker offsetNs / (int64_t)CHPP_NSEC_PER_MSEC,
142*84e33947SAndroid Build Coastguard Worker state->timesyncResult.measurementTimeNs / CHPP_NSEC_PER_MSEC);
143*84e33947SAndroid Build Coastguard Worker }
144*84e33947SAndroid Build Coastguard Worker
145*84e33947SAndroid Build Coastguard Worker return true;
146*84e33947SAndroid Build Coastguard Worker }
147*84e33947SAndroid Build Coastguard Worker
chppTimesyncMeasureOffset(struct ChppAppState * appState)148*84e33947SAndroid Build Coastguard Worker bool chppTimesyncMeasureOffset(struct ChppAppState *appState) {
149*84e33947SAndroid Build Coastguard Worker bool result = false;
150*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("Measuring timesync t=%" PRIu64,
151*84e33947SAndroid Build Coastguard Worker chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC);
152*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
153*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState *state = appState->timesyncClientContext;
154*84e33947SAndroid Build Coastguard Worker CHPP_NOT_NULL(state);
155*84e33947SAndroid Build Coastguard Worker
156*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error =
157*84e33947SAndroid Build Coastguard Worker CHPP_APP_ERROR_BUSY; // A measurement is in progress
158*84e33947SAndroid Build Coastguard Worker
159*84e33947SAndroid Build Coastguard Worker struct ChppAppHeader *request = chppAllocClientRequestCommand(
160*84e33947SAndroid Build Coastguard Worker &state->client, CHPP_TIMESYNC_COMMAND_GETTIME);
161*84e33947SAndroid Build Coastguard Worker size_t requestLen = sizeof(*request);
162*84e33947SAndroid Build Coastguard Worker
163*84e33947SAndroid Build Coastguard Worker if (request == NULL) {
164*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_OOM;
165*84e33947SAndroid Build Coastguard Worker CHPP_LOG_OOM();
166*84e33947SAndroid Build Coastguard Worker
167*84e33947SAndroid Build Coastguard Worker } else if (!chppClientSendTimestampedRequestOrFail(
168*84e33947SAndroid Build Coastguard Worker &state->client, &state->measureOffset, request, requestLen,
169*84e33947SAndroid Build Coastguard Worker CHPP_REQUEST_TIMEOUT_INFINITE)) {
170*84e33947SAndroid Build Coastguard Worker state->timesyncResult.error = CHPP_APP_ERROR_UNSPECIFIED;
171*84e33947SAndroid Build Coastguard Worker
172*84e33947SAndroid Build Coastguard Worker } else {
173*84e33947SAndroid Build Coastguard Worker result = true;
174*84e33947SAndroid Build Coastguard Worker }
175*84e33947SAndroid Build Coastguard Worker
176*84e33947SAndroid Build Coastguard Worker return result;
177*84e33947SAndroid Build Coastguard Worker }
178*84e33947SAndroid Build Coastguard Worker
chppTimesyncGetOffset(struct ChppAppState * appState,uint64_t maxTimesyncAgeNs)179*84e33947SAndroid Build Coastguard Worker int64_t chppTimesyncGetOffset(struct ChppAppState *appState,
180*84e33947SAndroid Build Coastguard Worker uint64_t maxTimesyncAgeNs) {
181*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
182*84e33947SAndroid Build Coastguard Worker struct ChppTimesyncClientState *state = appState->timesyncClientContext;
183*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(state);
184*84e33947SAndroid Build Coastguard Worker
185*84e33947SAndroid Build Coastguard Worker bool timesyncNeverDone = state->timesyncResult.offsetNs == 0;
186*84e33947SAndroid Build Coastguard Worker bool timesyncIsStale =
187*84e33947SAndroid Build Coastguard Worker chppGetCurrentTimeNs() - state->timesyncResult.measurementTimeNs >
188*84e33947SAndroid Build Coastguard Worker maxTimesyncAgeNs;
189*84e33947SAndroid Build Coastguard Worker
190*84e33947SAndroid Build Coastguard Worker if (timesyncNeverDone || timesyncIsStale) {
191*84e33947SAndroid Build Coastguard Worker chppTimesyncMeasureOffset(appState);
192*84e33947SAndroid Build Coastguard Worker } else {
193*84e33947SAndroid Build Coastguard Worker CHPP_LOGD("No need to timesync at t~=%" PRIu64 "offset=%" PRId64,
194*84e33947SAndroid Build Coastguard Worker chppGetCurrentTimeNs() / CHPP_NSEC_PER_MSEC,
195*84e33947SAndroid Build Coastguard Worker state->timesyncResult.offsetNs / (int64_t)CHPP_NSEC_PER_MSEC);
196*84e33947SAndroid Build Coastguard Worker }
197*84e33947SAndroid Build Coastguard Worker
198*84e33947SAndroid Build Coastguard Worker return state->timesyncResult.offsetNs;
199*84e33947SAndroid Build Coastguard Worker }
200*84e33947SAndroid Build Coastguard Worker
chppTimesyncGetResult(struct ChppAppState * appState)201*84e33947SAndroid Build Coastguard Worker const struct ChppTimesyncResult *chppTimesyncGetResult(
202*84e33947SAndroid Build Coastguard Worker struct ChppAppState *appState) {
203*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState);
204*84e33947SAndroid Build Coastguard Worker CHPP_DEBUG_NOT_NULL(appState->timesyncClientContext);
205*84e33947SAndroid Build Coastguard Worker return &appState->timesyncClientContext->timesyncResult;
206*84e33947SAndroid Build Coastguard Worker }
207