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