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