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/wifi.h"
18 
19 #include <inttypes.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include "chpp/app.h"
26 #include "chpp/clients.h"
27 #include "chpp/clients/discovery.h"
28 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
29 #include "chpp/clients/timesync.h"
30 #endif
31 #include "chpp/common/standard_uuids.h"
32 #include "chpp/common/wifi.h"
33 #include "chpp/common/wifi_types.h"
34 #include "chpp/common/wifi_utils.h"
35 #include "chpp/log.h"
36 #include "chpp/macros.h"
37 #include "chpp/memory.h"
38 #include "chre/pal/wifi.h"
39 #include "chre_api/chre/wifi.h"
40 
41 #ifndef CHPP_WIFI_DISCOVERY_TIMEOUT_MS
42 #define CHPP_WIFI_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS
43 #endif
44 
45 #ifndef CHPP_WIFI_MAX_TIMESYNC_AGE_NS
46 #define CHPP_WIFI_MAX_TIMESYNC_AGE_NS CHPP_TIMESYNC_DEFAULT_MAX_AGE_NS
47 #endif
48 
49 /************************************************
50  *  Prototypes
51  ***********************************************/
52 
53 static enum ChppAppErrorCode chppDispatchWifiResponse(void *clientContext,
54                                                       uint8_t *buf, size_t len);
55 static enum ChppAppErrorCode chppDispatchWifiNotification(void *clientContext,
56                                                           uint8_t *buf,
57                                                           size_t len);
58 static bool chppWifiClientInit(void *clientContext, uint8_t handle,
59                                struct ChppVersion serviceVersion);
60 static void chppWifiClientDeinit(void *clientContext);
61 static void chppWifiClientNotifyReset(void *clientContext);
62 static void chppWifiClientNotifyMatch(void *clientContext);
63 
64 /************************************************
65  *  Private Definitions
66  ***********************************************/
67 
68 /**
69  * Structure to maintain state for the WiFi client and its Request/Response
70  * (RR) functionality.
71  */
72 struct ChppWifiClientState {
73   struct ChppClientState client;     // WiFi client state
74   const struct chrePalWifiApi *api;  // WiFi PAL API
75 
76   struct ChppRequestResponseState rRState[CHPP_WIFI_CLIENT_REQUEST_MAX + 1];
77 
78   uint32_t capabilities;            // Cached GetCapabilities result
79   bool scanMonitorEnabled;          // Scan monitoring is enabled
80   bool scanMonitorSilenceCallback;  // Silence callback during recovery from a
81                                     // service reset
82 };
83 
84 // Note: This global definition of gWifiClientContext supports only one
85 // instance of the CHPP WiFi client at a time.
86 struct ChppWifiClientState gWifiClientContext;
87 static const struct chrePalSystemApi *gSystemApi;
88 static const struct chrePalWifiCallbacks *gCallbacks;
89 
90 /**
91  * Configuration parameters for this client
92  */
93 static const struct ChppClient kWifiClientConfig = {
94     .descriptor.uuid = CHPP_UUID_WIFI_STANDARD,
95 
96     // Version
97     .descriptor.version.major = 1,
98     .descriptor.version.minor = 0,
99     .descriptor.version.patch = 0,
100 
101     // Notifies client if CHPP is reset
102     .resetNotifierFunctionPtr = &chppWifiClientNotifyReset,
103 
104     // Notifies client if they are matched to a service
105     .matchNotifierFunctionPtr = &chppWifiClientNotifyMatch,
106 
107     // Service response dispatch function pointer
108     .responseDispatchFunctionPtr = &chppDispatchWifiResponse,
109 
110     // Service notification dispatch function pointer
111     .notificationDispatchFunctionPtr = &chppDispatchWifiNotification,
112 
113     // Service response dispatch function pointer
114     .initFunctionPtr = &chppWifiClientInit,
115 
116     // Service notification dispatch function pointer
117     .deinitFunctionPtr = &chppWifiClientDeinit,
118 
119     // Number of request-response states in the rRStates array.
120     .rRStateCount = ARRAY_SIZE(gWifiClientContext.rRState),
121 
122     // Min length is the entire header
123     .minLength = sizeof(struct ChppAppHeader),
124 };
125 
126 /************************************************
127  *  Prototypes
128  ***********************************************/
129 
130 static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi,
131                                const struct chrePalWifiCallbacks *callbacks);
132 static void chppWifiClientClose(void);
133 static uint32_t chppWifiClientGetCapabilities(void);
134 static bool chppWifiClientConfigureScanMonitor(bool enable);
135 static bool chppWifiClientRequestScan(const struct chreWifiScanParams *params);
136 static void chppWifiClientReleaseScanEvent(struct chreWifiScanEvent *event);
137 static bool chppWifiClientRequestRanging(
138     const struct chreWifiRangingParams *params);
139 static void chppWifiClientReleaseRangingEvent(
140     struct chreWifiRangingEvent *event);
141 
142 static void chppWiFiRecoverScanMonitor(
143     struct ChppWifiClientState *clientContext);
144 static void chppWifiCloseResult(struct ChppWifiClientState *clientContext,
145                                 uint8_t *buf, size_t len);
146 static void chppWifiGetCapabilitiesResult(
147     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len);
148 static void chppWifiConfigureScanMonitorResult(
149     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len);
150 static void chppWifiRequestScanResult(struct ChppWifiClientState *clientContext,
151                                       uint8_t *buf, size_t len);
152 static void chppWifiRequestRangingResult(
153     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len);
154 
155 static void chppWifiScanEventNotification(
156     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len);
157 static void chppWifiRangingEventNotification(
158     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len);
159 
160 /************************************************
161  *  Private Functions
162  ***********************************************/
163 
164 /**
165  * Dispatches a service response from the transport layer that is determined to
166  * be for the WiFi client.
167  *
168  * This function is called from the app layer using its function pointer given
169  * during client registration.
170  *
171  * @param clientContext Maintains status for each client instance.
172  * @param buf Input data. Cannot be null.
173  * @param len Length of input data in bytes.
174  *
175  * @return Indicates the result of this function call.
176  */
177 static enum ChppAppErrorCode chppDispatchWifiResponse(void *clientContext,
178                                                       uint8_t *buf,
179                                                       size_t len) {
180   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
181   struct ChppWifiClientState *wifiClientContext =
182       (struct ChppWifiClientState *)clientContext;
183   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
184 
185   if (rxHeader->command > CHPP_WIFI_CLIENT_REQUEST_MAX) {
186     error = CHPP_APP_ERROR_INVALID_COMMAND;
187 
188   } else if (!chppClientTimestampResponse(
189                  &wifiClientContext->client,
190                  &wifiClientContext->rRState[rxHeader->command], rxHeader)) {
191     error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE;
192 
193   } else {
194     switch (rxHeader->command) {
195       case CHPP_WIFI_OPEN: {
196         chppClientProcessOpenResponse(&wifiClientContext->client, buf, len);
197         chppWiFiRecoverScanMonitor(wifiClientContext);
198         break;
199       }
200 
201       case CHPP_WIFI_CLOSE: {
202         chppWifiCloseResult(wifiClientContext, buf, len);
203         break;
204       }
205 
206       case CHPP_WIFI_GET_CAPABILITIES: {
207         chppWifiGetCapabilitiesResult(wifiClientContext, buf, len);
208         break;
209       }
210 
211       case CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC: {
212         chppWifiConfigureScanMonitorResult(wifiClientContext, buf, len);
213         break;
214       }
215 
216       case CHPP_WIFI_REQUEST_SCAN_ASYNC: {
217         chppWifiRequestScanResult(wifiClientContext, buf, len);
218         break;
219       }
220 
221       case CHPP_WIFI_REQUEST_RANGING_ASYNC: {
222         chppWifiRequestRangingResult(wifiClientContext, buf, len);
223         break;
224       }
225 
226       default: {
227         error = CHPP_APP_ERROR_INVALID_COMMAND;
228         break;
229       }
230     }
231   }
232 
233   return error;
234 }
235 
236 /**
237  * Dispatches a service notification from the transport layer that is determined
238  * to be for the WiFi client.
239  *
240  * This function is called from the app layer using its function pointer given
241  * during client registration.
242  *
243  * @param clientContext Maintains status for each client instance.
244  * @param buf Input data. Cannot be null.
245  * @param len Length of input data in bytes.
246  *
247  * @return Indicates the result of this function call.
248  */
249 static enum ChppAppErrorCode chppDispatchWifiNotification(void *clientContext,
250                                                           uint8_t *buf,
251                                                           size_t len) {
252   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
253   struct ChppWifiClientState *wifiClientContext =
254       (struct ChppWifiClientState *)clientContext;
255   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
256 
257   switch (rxHeader->command) {
258     case CHPP_WIFI_REQUEST_SCAN_ASYNC: {
259       chppWifiScanEventNotification(wifiClientContext, buf, len);
260       break;
261     }
262 
263     case CHPP_WIFI_REQUEST_RANGING_ASYNC: {
264       chppWifiRangingEventNotification(wifiClientContext, buf, len);
265       break;
266     }
267 
268     default: {
269       error = CHPP_APP_ERROR_INVALID_COMMAND;
270       break;
271     }
272   }
273 
274   return error;
275 }
276 
277 /**
278  * Initializes the client and provides its handle number and the version of the
279  * matched service when/if it the client is matched with a service during
280  * discovery.
281  *
282  * @param clientContext Maintains status for each client instance.
283  * @param handle Handle number for this client.
284  * @param serviceVersion Version of the matched service.
285  *
286  * @return True if client is compatible and successfully initialized.
287  */
288 static bool chppWifiClientInit(void *clientContext, uint8_t handle,
289                                struct ChppVersion serviceVersion) {
290   UNUSED_VAR(serviceVersion);
291 
292   struct ChppWifiClientState *wifiClientContext =
293       (struct ChppWifiClientState *)clientContext;
294   chppClientInit(&wifiClientContext->client, handle);
295 
296   return true;
297 }
298 
299 /**
300  * Deinitializes the client.
301  *
302  * @param clientContext Maintains status for each client instance.
303  */
304 static void chppWifiClientDeinit(void *clientContext) {
305   struct ChppWifiClientState *wifiClientContext =
306       (struct ChppWifiClientState *)clientContext;
307   chppClientDeinit(&wifiClientContext->client);
308 }
309 
310 /**
311  * Notifies the client of an incoming reset.
312  *
313  * @param clientContext Maintains status for each client instance.
314  */
315 static void chppWifiClientNotifyReset(void *clientContext) {
316   struct ChppWifiClientState *wifiClientContext =
317       (struct ChppWifiClientState *)clientContext;
318 
319   chppClientCloseOpenRequests(&wifiClientContext->client, &kWifiClientConfig,
320                               false /* clearOnly */);
321   chppCheckWifiScanEventNotificationReset();
322 
323   if (wifiClientContext->client.openState != CHPP_OPEN_STATE_OPENED &&
324       !wifiClientContext->client.pseudoOpen) {
325     CHPP_LOGW("WiFi client reset but wasn't open");
326   } else {
327     CHPP_LOGI("WiFi client reopening from state=%" PRIu8,
328               wifiClientContext->client.openState);
329     chppClientSendOpenRequest(&wifiClientContext->client,
330                               &wifiClientContext->rRState[CHPP_WIFI_OPEN],
331                               CHPP_WIFI_OPEN,
332                               /*blocking=*/false);
333   }
334 }
335 
336 /**
337  * Notifies the client of being matched to a service.
338  *
339  * @param clientContext Maintains status for each client instance.
340  */
341 static void chppWifiClientNotifyMatch(void *clientContext) {
342   struct ChppWifiClientState *wifiClientContext =
343       (struct ChppWifiClientState *)clientContext;
344 
345   if (wifiClientContext->client.pseudoOpen) {
346     CHPP_LOGD("Pseudo-open WiFi client opening");
347     chppClientSendOpenRequest(&wifiClientContext->client,
348                               &wifiClientContext->rRState[CHPP_WIFI_OPEN],
349                               CHPP_WIFI_OPEN,
350                               /*blocking=*/false);
351   }
352 }
353 
354 /**
355  * Restores the state of scan monitoring after an incoming reset.
356  *
357  * @param clientContext Maintains status for each client instance.
358  */
359 static void chppWiFiRecoverScanMonitor(
360     struct ChppWifiClientState *clientContext) {
361   if (clientContext->scanMonitorEnabled) {
362     CHPP_LOGI("Re-enabling WiFi scan monitoring after reset");
363     clientContext->scanMonitorEnabled = false;
364     clientContext->scanMonitorSilenceCallback = true;
365 
366     if (!chppWifiClientConfigureScanMonitor(true)) {
367       clientContext->scanMonitorSilenceCallback = false;
368       CHPP_DEBUG_ASSERT_LOG(
369           false, "Unable to re-enable WiFi scan monitoring after reset");
370     }
371   }
372 }
373 
374 /**
375  * Handles the service response for the close client request.
376  *
377  * This function is called from chppDispatchWifiResponse().
378  *
379  * @param clientContext Maintains status for each client instance.
380  * @param buf Input data. Cannot be null.
381  * @param len Length of input data in bytes.
382  */
383 static void chppWifiCloseResult(struct ChppWifiClientState *clientContext,
384                                 uint8_t *buf, size_t len) {
385   // TODO
386   UNUSED_VAR(clientContext);
387   UNUSED_VAR(buf);
388   UNUSED_VAR(len);
389 }
390 
391 /**
392  * Handles the service response for the get capabilities client request.
393  *
394  * This function is called from chppDispatchWifiResponse().
395  *
396  * @param clientContext Maintains status for each client instance.
397  * @param buf Input data. Cannot be null.
398  * @param len Length of input data in bytes.
399  */
400 static void chppWifiGetCapabilitiesResult(
401     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) {
402   if (len < sizeof(struct ChppWifiGetCapabilitiesResponse)) {
403     struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
404     CHPP_LOGE("GetCapabilities resp. too short. err=%" PRIu8, rxHeader->error);
405 
406   } else {
407     struct ChppWifiGetCapabilitiesParameters *result =
408         &((struct ChppWifiGetCapabilitiesResponse *)buf)->params;
409 
410     CHPP_LOGD("chppWifiGetCapabilitiesResult received capabilities=0x%" PRIx32,
411               result->capabilities);
412 
413 #ifdef CHPP_WIFI_DEFAULT_CAPABILITIES
414     CHPP_ASSERT_LOG((result->capabilities == CHPP_WIFI_DEFAULT_CAPABILITIES),
415                     "Unexpected capability 0x%" PRIx32 " != 0x%" PRIx32,
416                     result->capabilities, CHPP_WIFI_DEFAULT_CAPABILITIES);
417 #endif
418 
419     clientContext->capabilities = result->capabilities;
420   }
421 }
422 
423 /**
424  * Handles the service response for the Configure Scan Monitor client request.
425  *
426  * This function is called from chppDispatchWifiResponse().
427  *
428  * @param clientContext Maintains status for each client instance.
429  * @param buf Input data. Cannot be null.
430  * @param len Length of input data in bytes.
431  */
432 static void chppWifiConfigureScanMonitorResult(
433     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) {
434   UNUSED_VAR(clientContext);
435 
436   if (len < sizeof(struct ChppWifiConfigureScanMonitorAsyncResponse)) {
437     // Short response length indicates an error
438 
439     struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
440     CHPP_LOGE("ScanMonitor resp. too short. err=%" PRIu8, rxHeader->error);
441 
442     if (rxHeader->error == CHPP_APP_ERROR_NONE) {
443       rxHeader->error = CHPP_APP_ERROR_INVALID_LENGTH;
444     }
445     gCallbacks->scanMonitorStatusChangeCallback(
446         false, chppAppErrorToChreError(rxHeader->error));
447 
448   } else {
449     struct ChppWifiConfigureScanMonitorAsyncResponseParameters *result =
450         &((struct ChppWifiConfigureScanMonitorAsyncResponse *)buf)->params;
451 
452     gWifiClientContext.scanMonitorEnabled = result->enabled;
453     CHPP_LOGD(
454         "chppWifiConfigureScanMonitorResult received enable=%d, "
455         "errorCode=%" PRIu8,
456         result->enabled, result->errorCode);
457 
458     if (!gWifiClientContext.scanMonitorSilenceCallback) {
459       // Per the scanMonitorStatusChangeCallback API contract, unsolicited
460       // calls to scanMonitorStatusChangeCallback must not be made, and it
461       // should only be invoked as the direct result of an earlier call to
462       // configureScanMonitor.
463       gCallbacks->scanMonitorStatusChangeCallback(result->enabled,
464                                                   result->errorCode);
465     }  // Else, the WiFi subsystem has been reset and we are required to
466        // silently reenable the scan monitor.
467 
468     gWifiClientContext.scanMonitorSilenceCallback = false;
469   }
470 }
471 
472 /**
473  * Handles the service response for the Request Scan Result client request.
474  *
475  * This function is called from chppDispatchWifiResponse().
476  *
477  * @param clientContext Maintains status for each client instance.
478  * @param buf Input data. Cannot be null.
479  * @param len Length of input data in bytes.
480  */
481 static void chppWifiRequestScanResult(struct ChppWifiClientState *clientContext,
482                                       uint8_t *buf, size_t len) {
483   UNUSED_VAR(clientContext);
484 
485   if (len < sizeof(struct ChppWifiRequestScanResponse)) {
486     // Short response length indicates an error
487 
488     struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
489     CHPP_LOGE("ScanRequest resp. too short. err=%" PRIu8, rxHeader->error);
490 
491     if (rxHeader->error == CHPP_APP_ERROR_NONE) {
492       rxHeader->error = CHPP_APP_ERROR_INVALID_LENGTH;
493     }
494     gCallbacks->scanResponseCallback(false,
495                                      chppAppErrorToChreError(rxHeader->error));
496 
497   } else {
498     struct ChppWifiRequestScanResponseParameters *result =
499         &((struct ChppWifiRequestScanResponse *)buf)->params;
500 
501     // TODO(b/193540354): Remove when resolved
502     {
503       static uint32_t sNumConsecutiveError = 0;
504       if (result->errorCode != CHRE_ERROR_NONE) {
505         sNumConsecutiveError++;
506       } else {
507         sNumConsecutiveError = 0;
508       }
509       if (sNumConsecutiveError > 20) {
510         CHPP_ASSERT("Too many consecutive WiFi scan errors");
511       }
512     }
513 
514     CHPP_LOGI("Scan request success=%d (at service)", result->pending);
515     gCallbacks->scanResponseCallback(result->pending, result->errorCode);
516   }
517 }
518 
519 /**
520  * Handles the service response for the Request Ranging Result client request.
521  *
522  * This function is called from chppDispatchWifiResponse().
523  *
524  * @param clientContext Maintains status for each client instance.
525  * @param buf Input data. Cannot be null.
526  * @param len Length of input data in bytes.
527  */
528 static void chppWifiRequestRangingResult(
529     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) {
530   UNUSED_VAR(clientContext);
531   UNUSED_VAR(len);
532 
533   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
534 
535   if (rxHeader->error != CHPP_APP_ERROR_NONE) {
536     CHPP_LOGE("RangingRequest failed at service err=%" PRIu8, rxHeader->error);
537     gCallbacks->rangingEventCallback(chppAppErrorToChreError(rxHeader->error),
538                                      NULL);
539 
540   } else {
541     CHPP_LOGD("Ranging request accepted at service");
542   }
543 }
544 
545 /**
546  * Handles the WiFi scan event service notification.
547  *
548  * This function is called from chppDispatchWifiNotification().
549  *
550  * @param clientContext Maintains status for each client instance.
551  * @param buf Input data. Cannot be null.
552  * @param len Length of input data in bytes.
553  */
554 static void chppWifiScanEventNotification(
555     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) {
556   UNUSED_VAR(clientContext);
557   CHPP_LOGD("chppWifiScanEventNotification received data len=%" PRIuSIZE, len);
558 
559   buf += sizeof(struct ChppAppHeader);
560   len -= sizeof(struct ChppAppHeader);
561 
562   struct chreWifiScanEvent *chre =
563       chppWifiScanEventToChre((struct ChppWifiScanEvent *)buf, len);
564 
565   if (chre == NULL) {
566     CHPP_LOGE("Scan event conversion failed: len=%" PRIuSIZE, len);
567   } else {
568 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
569     uint64_t correctedTime =
570         chre->referenceTime -
571         (uint64_t)chppTimesyncGetOffset(gWifiClientContext.client.appContext,
572                                         CHPP_WIFI_MAX_TIMESYNC_AGE_NS);
573     CHPP_LOGD("WiFi scan time corrected from %" PRIu64 "to %" PRIu64,
574               chre->referenceTime / CHPP_NSEC_PER_MSEC,
575               correctedTime / CHPP_NSEC_PER_MSEC);
576     chre->referenceTime = correctedTime;
577 #endif
578 
579     CHPP_DEBUG_ASSERT(chppCheckWifiScanEventNotification(chre));
580 
581     gCallbacks->scanEventCallback(chre);
582   }
583 }
584 
585 /**
586  * Handles the WiFi ranging event service notification.
587  *
588  * This function is called from chppDispatchWifiNotification().
589  *
590  * @param clientContext Maintains status for each client instance.
591  * @param buf Input data. Cannot be null.
592  * @param len Length of input data in bytes.
593  */
594 static void chppWifiRangingEventNotification(
595     struct ChppWifiClientState *clientContext, uint8_t *buf, size_t len) {
596   UNUSED_VAR(clientContext);
597 
598   CHPP_LOGD("chppWifiRangingEventNotification received data len=%" PRIuSIZE,
599             len);
600 
601   buf += sizeof(struct ChppAppHeader);
602   len -= sizeof(struct ChppAppHeader);
603 
604   // Timestamp correction prior to conversion to avoid const casting issues.
605 #ifdef CHPP_CLIENT_ENABLED_TIMESYNC
606   struct ChppWifiRangingEvent *event = (struct ChppWifiRangingEvent *)buf;
607 
608   for (size_t i = 0; i < event->resultCount; i++) {
609     struct ChppWifiRangingResult *results =
610         (struct ChppWifiRangingResult *)&buf[event->results.offset];
611 
612     uint64_t correctedTime =
613         results[i].timestamp -
614         (uint64_t)chppTimesyncGetOffset(gWifiClientContext.client.appContext,
615                                         CHPP_WIFI_MAX_TIMESYNC_AGE_NS);
616     CHPP_LOGD("WiFi ranging result time corrected from %" PRIu64 "to %" PRIu64,
617               results[i].timestamp / CHPP_NSEC_PER_MSEC,
618               correctedTime / CHPP_NSEC_PER_MSEC);
619     results[i].timestamp = correctedTime;
620   }
621 #endif
622 
623   struct chreWifiRangingEvent *chre =
624       chppWifiRangingEventToChre((struct ChppWifiRangingEvent *)buf, len);
625 
626   uint8_t error = CHRE_ERROR_NONE;
627   if (chre == NULL) {
628     error = CHRE_ERROR;
629     CHPP_LOGE("Ranging event conversion failed len=%" PRIuSIZE, len);
630   }
631 
632   gCallbacks->rangingEventCallback(error, chre);
633 }
634 
635 /**
636  * Initializes the WiFi client upon an open request from CHRE and responds
637  * with the result.
638  *
639  * @param systemApi CHRE system function pointers.
640  * @param callbacks CHRE entry points.
641  *
642  * @return True if successful. False otherwise.
643  */
644 static bool chppWifiClientOpen(const struct chrePalSystemApi *systemApi,
645                                const struct chrePalWifiCallbacks *callbacks) {
646   CHPP_DEBUG_ASSERT(systemApi != NULL);
647   CHPP_DEBUG_ASSERT(callbacks != NULL);
648 
649   bool result = false;
650   gSystemApi = systemApi;
651   gCallbacks = callbacks;
652 
653   CHPP_LOGD("WiFi client opening");
654 
655   if (chppWaitForDiscoveryComplete(gWifiClientContext.client.appContext,
656                                    CHPP_WIFI_DISCOVERY_TIMEOUT_MS)) {
657     result = chppClientSendOpenRequest(
658         &gWifiClientContext.client, &gWifiClientContext.rRState[CHPP_WIFI_OPEN],
659         CHPP_WIFI_OPEN,
660         /*blocking=*/true);
661   }
662 
663 #ifdef CHPP_WIFI_CLIENT_OPEN_ALWAYS_SUCCESS
664   chppClientPseudoOpen(&gWifiClientContext.client);
665   result = true;
666 #endif
667 
668   return result;
669 }
670 
671 /**
672  * Deinitializes the WiFi client.
673  */
674 static void chppWifiClientClose(void) {
675   // Remote
676   struct ChppAppHeader *request = chppAllocClientRequestCommand(
677       &gWifiClientContext.client, CHPP_WIFI_CLOSE);
678 
679   if (request == NULL) {
680     CHPP_LOG_OOM();
681   } else if (chppSendTimestampedRequestAndWait(
682                  &gWifiClientContext.client,
683                  &gWifiClientContext.rRState[CHPP_WIFI_CLOSE], request,
684                  sizeof(*request))) {
685     gWifiClientContext.client.openState = CHPP_OPEN_STATE_CLOSED;
686     gWifiClientContext.capabilities = CHRE_WIFI_CAPABILITIES_NONE;
687     chppClientCloseOpenRequests(&gWifiClientContext.client, &kWifiClientConfig,
688                                 true /* clearOnly */);
689   }
690 }
691 
692 /**
693  * Retrieves a set of flags indicating the WiFi features supported by the
694  * current implementation.
695  *
696  * @return Capabilities flags.
697  */
698 static uint32_t chppWifiClientGetCapabilities(void) {
699 #ifdef CHPP_WIFI_DEFAULT_CAPABILITIES
700   uint32_t capabilities = CHPP_WIFI_DEFAULT_CAPABILITIES;
701 #else
702   uint32_t capabilities = CHRE_WIFI_CAPABILITIES_NONE;
703 #endif
704 
705   if (gWifiClientContext.capabilities != CHRE_WIFI_CAPABILITIES_NONE) {
706     // Result already cached
707     capabilities = gWifiClientContext.capabilities;
708 
709   } else {
710     struct ChppAppHeader *request = chppAllocClientRequestCommand(
711         &gWifiClientContext.client, CHPP_WIFI_GET_CAPABILITIES);
712 
713     if (request == NULL) {
714       CHPP_LOG_OOM();
715     } else {
716       if (chppSendTimestampedRequestAndWait(
717               &gWifiClientContext.client,
718               &gWifiClientContext.rRState[CHPP_WIFI_GET_CAPABILITIES], request,
719               sizeof(*request))) {
720         // Success. gWifiClientContext.capabilities is now populated
721         capabilities = gWifiClientContext.capabilities;
722       }
723     }
724   }
725 
726   return capabilities;
727 }
728 
729 /**
730  * Enables/disables receiving unsolicited scan results (scan monitoring).
731  *
732  * @param enable True to enable.
733  *
734  * @return True indicates the request was sent off to the service.
735  */
736 static bool chppWifiClientConfigureScanMonitor(bool enable) {
737   bool result = false;
738 
739   struct ChppWifiConfigureScanMonitorAsyncRequest *request =
740       chppAllocClientRequestFixed(
741           &gWifiClientContext.client,
742           struct ChppWifiConfigureScanMonitorAsyncRequest);
743 
744   if (request == NULL) {
745     CHPP_LOG_OOM();
746   } else {
747     request->header.command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC;
748     request->params.enable = enable;
749     request->params.cookie =
750         &gWifiClientContext.rRState[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC];
751 
752     result = chppSendTimestampedRequestOrFail(
753         &gWifiClientContext.client,
754         &gWifiClientContext.rRState[CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC],
755         request, sizeof(*request), CHPP_CLIENT_REQUEST_TIMEOUT_DEFAULT);
756   }
757 
758   return result;
759 }
760 
761 /**
762  * Request that the WiFi chipset perform a scan or deliver results from its
763  * cache.
764  *
765  * @param params See chreWifiRequestScanAsync().
766  *
767  * @return True indicates the request was sent off to the service.
768  */
769 static bool chppWifiClientRequestScan(const struct chreWifiScanParams *params) {
770   struct ChppWifiScanParamsWithHeader *request;
771   size_t requestLen;
772 
773   bool result = chppWifiScanParamsFromChre(params, &request, &requestLen);
774 
775   if (!result) {
776     CHPP_LOG_OOM();
777   } else {
778     request->header.handle = gWifiClientContext.client.handle;
779     request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
780     request->header.transaction = gWifiClientContext.client.transaction++;
781     request->header.error = CHPP_APP_ERROR_NONE;
782     request->header.command = CHPP_WIFI_REQUEST_SCAN_ASYNC;
783 
784     result = chppSendTimestampedRequestOrFail(
785         &gWifiClientContext.client,
786         &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_SCAN_ASYNC], request,
787         requestLen, CHRE_WIFI_SCAN_RESULT_TIMEOUT_NS);
788   }
789 
790   return result;
791 }
792 
793 /**
794  * Releases the memory held for the scan event callback.
795  *
796  * @param event Location event to be released.
797  */
798 static void chppWifiClientReleaseScanEvent(struct chreWifiScanEvent *event) {
799   if (event->scannedFreqListLen > 0) {
800     void *scannedFreqList = CHPP_CONST_CAST_POINTER(event->scannedFreqList);
801     CHPP_FREE_AND_NULLIFY(scannedFreqList);
802   }
803 
804   if (event->resultCount > 0) {
805     void *results = CHPP_CONST_CAST_POINTER(event->results);
806     CHPP_FREE_AND_NULLIFY(results);
807   }
808 
809   CHPP_FREE_AND_NULLIFY(event);
810 }
811 
812 /**
813  * Request that the WiFi chipset perform RTT ranging.
814  *
815  * @param params See chreWifiRequestRangingAsync().
816  *
817  * @return True indicates the request was sent off to the service.
818  */
819 static bool chppWifiClientRequestRanging(
820     const struct chreWifiRangingParams *params) {
821   struct ChppWifiRangingParamsWithHeader *request;
822   size_t requestLen;
823 
824   bool result = chppWifiRangingParamsFromChre(params, &request, &requestLen);
825 
826   if (!result) {
827     CHPP_LOG_OOM();
828   } else {
829     request->header.handle = gWifiClientContext.client.handle;
830     request->header.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
831     request->header.transaction = gWifiClientContext.client.transaction++;
832     request->header.error = CHPP_APP_ERROR_NONE;
833     request->header.command = CHPP_WIFI_REQUEST_RANGING_ASYNC;
834 
835     result = chppSendTimestampedRequestOrFail(
836         &gWifiClientContext.client,
837         &gWifiClientContext.rRState[CHPP_WIFI_REQUEST_RANGING_ASYNC], request,
838         requestLen, CHRE_WIFI_RANGING_RESULT_TIMEOUT_NS);
839   }
840 
841   return result;
842 }
843 
844 /**
845  * Releases the memory held for the RTT ranging event callback.
846  *
847  * @param event Location event to be released.
848  */
849 static void chppWifiClientReleaseRangingEvent(
850     struct chreWifiRangingEvent *event) {
851   if (event->resultCount > 0) {
852     void *results = CHPP_CONST_CAST_POINTER(event->results);
853     CHPP_FREE_AND_NULLIFY(results);
854   }
855 
856   CHPP_FREE_AND_NULLIFY(event);
857 }
858 
859 /************************************************
860  *  Public Functions
861  ***********************************************/
862 
863 void chppRegisterWifiClient(struct ChppAppState *appContext) {
864   chppRegisterClient(appContext, (void *)&gWifiClientContext,
865                      &gWifiClientContext.client, gWifiClientContext.rRState,
866                      &kWifiClientConfig);
867 }
868 
869 void chppDeregisterWifiClient(struct ChppAppState *appContext) {
870   // TODO
871 
872   UNUSED_VAR(appContext);
873 }
874 
875 struct ChppClientState *getChppWifiClientState(void) {
876   return &gWifiClientContext.client;
877 }
878 
879 #ifdef CHPP_CLIENT_ENABLED_WIFI
880 
881 #ifdef CHPP_CLIENT_ENABLED_CHRE_WIFI
882 const struct chrePalWifiApi *chrePalWifiGetApi(uint32_t requestedApiVersion) {
883 #else
884 const struct chrePalWifiApi *chppPalWifiGetApi(uint32_t requestedApiVersion) {
885 #endif
886 
887   static const struct chrePalWifiApi api = {
888       .moduleVersion = CHPP_PAL_WIFI_API_VERSION,
889       .open = chppWifiClientOpen,
890       .close = chppWifiClientClose,
891       .getCapabilities = chppWifiClientGetCapabilities,
892       .configureScanMonitor = chppWifiClientConfigureScanMonitor,
893       .requestScan = chppWifiClientRequestScan,
894       .releaseScanEvent = chppWifiClientReleaseScanEvent,
895       .requestRanging = chppWifiClientRequestRanging,
896       .releaseRangingEvent = chppWifiClientReleaseRangingEvent,
897   };
898 
899   CHPP_STATIC_ASSERT(
900       CHRE_PAL_WIFI_API_CURRENT_VERSION == CHPP_PAL_WIFI_API_VERSION,
901       "A newer CHRE PAL API version is available. Please update.");
902 
903   if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion,
904                                         requestedApiVersion)) {
905     return NULL;
906   } else {
907     return &api;
908   }
909 }
910 
911 #endif
912