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/gnss.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 #include "chpp/common/gnss.h"
29 #include "chpp/common/gnss_types.h"
30 #include "chpp/common/standard_uuids.h"
31 #include "chpp/log.h"
32 #include "chpp/macros.h"
33 #include "chpp/memory.h"
34 #include "chre/pal/gnss.h"
35 #include "chre_api/chre/gnss.h"
36 
37 #ifndef CHPP_GNSS_DISCOVERY_TIMEOUT_MS
38 #define CHPP_GNSS_DISCOVERY_TIMEOUT_MS CHPP_DISCOVERY_DEFAULT_TIMEOUT_MS
39 #endif
40 
41 /************************************************
42  *  Prototypes
43  ***********************************************/
44 
45 static enum ChppAppErrorCode chppDispatchGnssResponse(void *clientContext,
46                                                       uint8_t *buf, size_t len);
47 static enum ChppAppErrorCode chppDispatchGnssNotification(void *clientContext,
48                                                           uint8_t *buf,
49                                                           size_t len);
50 static bool chppGnssClientInit(void *clientContext, uint8_t handle,
51                                struct ChppVersion serviceVersion);
52 static void chppGnssClientDeinit(void *clientContext);
53 static void chppGnssClientNotifyReset(void *clientContext);
54 static void chppGnssClientNotifyMatch(void *clientContext);
55 
56 /************************************************
57  *  Private Definitions
58  ***********************************************/
59 
60 /**
61  * Structure to maintain state for the GNSS client and its Request/Response
62  * (RR) functionality.
63  */
64 struct ChppGnssClientState {
65   struct ChppEndpointState client;   // CHPP client state
66   const struct chrePalGnssApi *api;  // GNSS PAL API
67 
68   struct ChppOutgoingRequestState
69       outReqStates[CHPP_GNSS_CLIENT_REQUEST_MAX + 1];
70 
71   uint32_t capabilities;           // Cached GetCapabilities result
72   bool requestStateResyncPending;  // requestStateResync() is waiting to be
73                                    // processed
74   bool capabilitiesValid;  // Flag to indicate if the capabilities result
75                            // is valid
76 };
77 
78 // Note: This global definition of gGnssClientContext supports only one
79 // instance of the CHPP GNSS client at a time.
80 struct ChppGnssClientState gGnssClientContext;
81 static const struct chrePalSystemApi *gSystemApi;
82 static const struct chrePalGnssCallbacks *gCallbacks;
83 
84 /**
85  * Configuration parameters for this client
86  */
87 static const struct ChppClient kGnssClientConfig = {
88     .descriptor.uuid = CHPP_UUID_GNSS_STANDARD,
89 
90     // Version
91     .descriptor.version.major = 1,
92     .descriptor.version.minor = 0,
93     .descriptor.version.patch = 0,
94 
95     // Notifies client if CHPP is reset
96     .resetNotifierFunctionPtr = &chppGnssClientNotifyReset,
97 
98     // Notifies client if they are matched to a service
99     .matchNotifierFunctionPtr = &chppGnssClientNotifyMatch,
100 
101     // Service response dispatch function pointer
102     .responseDispatchFunctionPtr = &chppDispatchGnssResponse,
103 
104     // Service notification dispatch function pointer
105     .notificationDispatchFunctionPtr = &chppDispatchGnssNotification,
106 
107     // Service response dispatch function pointer
108     .initFunctionPtr = &chppGnssClientInit,
109 
110     // Service notification dispatch function pointer
111     .deinitFunctionPtr = &chppGnssClientDeinit,
112 
113     // Client timeout function pointer
114     .timeoutFunctionPtr = NULL,  // Not used
115 
116     // Number of request-response states in the outReqStates array.
117     .outReqCount = ARRAY_SIZE(gGnssClientContext.outReqStates),
118 
119     // Min length is the entire header
120     .minLength = sizeof(struct ChppAppHeader),
121 };
122 
123 /************************************************
124  *  Prototypes
125  ***********************************************/
126 
127 static bool chppGnssClientOpen(const struct chrePalSystemApi *systemApi,
128                                const struct chrePalGnssCallbacks *callbacks);
129 static void chppGnssClientClose(void);
130 static uint32_t chppGnssClientGetCapabilities(void);
131 static bool chppGnssClientControlLocationSession(bool enable,
132                                                  uint32_t minIntervalMs,
133                                                  uint32_t minTimeToNextFixMs);
134 static void chppGnssClientReleaseLocationEvent(
135     struct chreGnssLocationEvent *event);
136 static bool chppGnssClientControlMeasurementSession(bool enable,
137                                                     uint32_t minIntervalMs);
138 static void chppGnssClientReleaseMeasurementDataEvent(
139     struct chreGnssDataEvent *event);
140 static bool chppGnssClientConfigurePassiveLocationListener(bool enable);
141 
142 static void chppGnssCloseResult(struct ChppGnssClientState *clientContext,
143                                 uint8_t *buf, size_t len);
144 static void chppGnssGetCapabilitiesResult(
145     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
146 static void chppGnssControlLocationSessionResult(
147     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
148 static void chppGnssControlMeasurementSessionResult(
149     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
150 static void chppGnssConfigurePassiveLocationListenerResult(
151     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
152 
153 static void chppGnssStateResyncNotification(
154     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
155 static void chppGnssLocationResultNotification(
156     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len);
157 static void chppGnssMeasurementResultNotification(
158     struct ChppGnssClientState *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 GNSS 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  */
chppDispatchGnssResponse(void * clientContext,uint8_t * buf,size_t len)177 static enum ChppAppErrorCode chppDispatchGnssResponse(void *clientContext,
178                                                       uint8_t *buf,
179                                                       size_t len) {
180   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
181   struct ChppGnssClientState *gnssClientContext =
182       (struct ChppGnssClientState *)clientContext;
183   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
184 
185   if (rxHeader->command > CHPP_GNSS_CLIENT_REQUEST_MAX) {
186     error = CHPP_APP_ERROR_INVALID_COMMAND;
187 
188   } else if (!chppTimestampIncomingResponse(
189                  gnssClientContext->client.appContext,
190                  &gnssClientContext->outReqStates[rxHeader->command],
191                  rxHeader)) {
192     error = CHPP_APP_ERROR_UNEXPECTED_RESPONSE;
193 
194   } else {
195     switch (rxHeader->command) {
196       case CHPP_GNSS_OPEN: {
197         chppClientProcessOpenResponse(&gnssClientContext->client, buf, len);
198         if (rxHeader->error == CHPP_APP_ERROR_NONE &&
199             gnssClientContext->requestStateResyncPending) {
200           gCallbacks->requestStateResync();
201           gnssClientContext->requestStateResyncPending = false;
202         }
203         break;
204       }
205 
206       case CHPP_GNSS_CLOSE: {
207         chppGnssCloseResult(gnssClientContext, buf, len);
208         break;
209       }
210 
211       case CHPP_GNSS_GET_CAPABILITIES: {
212         chppGnssGetCapabilitiesResult(gnssClientContext, buf, len);
213         break;
214       }
215 
216       case CHPP_GNSS_CONTROL_LOCATION_SESSION: {
217         chppGnssControlLocationSessionResult(gnssClientContext, buf, len);
218         break;
219       }
220 
221       case CHPP_GNSS_CONTROL_MEASUREMENT_SESSION: {
222         chppGnssControlMeasurementSessionResult(gnssClientContext, buf, len);
223         break;
224       }
225 
226       case CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER: {
227         chppGnssConfigurePassiveLocationListenerResult(gnssClientContext, buf,
228                                                        len);
229         break;
230       }
231 
232       default: {
233         error = CHPP_APP_ERROR_INVALID_COMMAND;
234         break;
235       }
236     }
237   }
238 
239   return error;
240 }
241 
242 /**
243  * Dispatches a service notification from the transport layer that is determined
244  * to be for the GNSS client.
245  *
246  * This function is called from the app layer using its function pointer given
247  * during client registration.
248  *
249  * @param clientContext Maintains status for each client instance.
250  * @param buf Input data. Cannot be null.
251  * @param len Length of input data in bytes.
252  *
253  * @return Indicates the result of this function call.
254  */
chppDispatchGnssNotification(void * clientContext,uint8_t * buf,size_t len)255 static enum ChppAppErrorCode chppDispatchGnssNotification(void *clientContext,
256                                                           uint8_t *buf,
257                                                           size_t len) {
258   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
259   struct ChppGnssClientState *gnssClientContext =
260       (struct ChppGnssClientState *)clientContext;
261   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
262 
263   switch (rxHeader->command) {
264     case CHPP_GNSS_REQUEST_STATE_RESYNC_NOTIFICATION: {
265       chppGnssStateResyncNotification(gnssClientContext, buf, len);
266       break;
267     }
268 
269     case CHPP_GNSS_LOCATION_RESULT_NOTIFICATION: {
270       chppGnssLocationResultNotification(gnssClientContext, buf, len);
271       break;
272     }
273 
274     case CHPP_GNSS_MEASUREMENT_RESULT_NOTIFICATION: {
275       chppGnssMeasurementResultNotification(gnssClientContext, buf, len);
276       break;
277     }
278 
279     default: {
280       error = CHPP_APP_ERROR_INVALID_COMMAND;
281       break;
282     }
283   }
284 
285   return error;
286 }
287 
288 /**
289  * Initializes the client and provides its handle number and the version of the
290  * matched service when/if it the client is matched with a service during
291  * discovery.
292  *
293  * @param clientContext Maintains status for each client instance.
294  * @param handle Handle number for this client.
295  * @param serviceVersion Version of the matched service.
296  *
297  * @return True if client is compatible and successfully initialized.
298  */
chppGnssClientInit(void * clientContext,uint8_t handle,struct ChppVersion serviceVersion)299 static bool chppGnssClientInit(void *clientContext, uint8_t handle,
300                                struct ChppVersion serviceVersion) {
301   UNUSED_VAR(serviceVersion);
302 
303   struct ChppGnssClientState *gnssClientContext =
304       (struct ChppGnssClientState *)clientContext;
305   chppClientInit(&gnssClientContext->client, handle);
306 
307   return true;
308 }
309 
310 /**
311  * Deinitializes the client.
312  *
313  * @param clientContext Maintains status for each client instance.
314  */
chppGnssClientDeinit(void * clientContext)315 static void chppGnssClientDeinit(void *clientContext) {
316   struct ChppGnssClientState *gnssClientContext =
317       (struct ChppGnssClientState *)clientContext;
318   chppClientDeinit(&gnssClientContext->client);
319 }
320 
321 /**
322  * Notifies the client of an incoming reset.
323  *
324  * @param clientContext Maintains status for each client instance.
325  */
chppGnssClientNotifyReset(void * clientContext)326 static void chppGnssClientNotifyReset(void *clientContext) {
327   struct ChppGnssClientState *gnssClientContext =
328       (struct ChppGnssClientState *)clientContext;
329 
330   chppClientCloseOpenRequests(&gnssClientContext->client, &kGnssClientConfig,
331                               false /* clearOnly */);
332 
333   CHPP_LOGD("GNSS client reopening from state=%" PRIu8,
334             gnssClientContext->client.openState);
335   gnssClientContext->requestStateResyncPending = true;
336   chppClientSendOpenRequest(&gGnssClientContext.client,
337                             &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN],
338                             CHPP_GNSS_OPEN,
339                             /*blocking=*/false);
340 }
341 
342 /**
343  * Notifies the client of being matched to a service.
344  *
345  * @param clientContext Maintains status for each client instance.
346  */
chppGnssClientNotifyMatch(void * clientContext)347 static void chppGnssClientNotifyMatch(void *clientContext) {
348   struct ChppGnssClientState *gnssClientContext =
349       (struct ChppGnssClientState *)clientContext;
350 
351   if (gnssClientContext->client.pseudoOpen) {
352     CHPP_LOGD("Pseudo-open GNSS client opening");
353     chppClientSendOpenRequest(&gGnssClientContext.client,
354                               &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN],
355                               CHPP_GNSS_OPEN,
356                               /*blocking=*/false);
357   }
358 }
359 
360 /**
361  * Handles the service response for the close client request.
362  *
363  * This function is called from chppDispatchGnssResponse().
364  *
365  * @param clientContext Maintains status for each client instance.
366  * @param buf Input data. Cannot be null.
367  * @param len Length of input data in bytes.
368  */
chppGnssCloseResult(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)369 static void chppGnssCloseResult(struct ChppGnssClientState *clientContext,
370                                 uint8_t *buf, size_t len) {
371   // TODO
372   UNUSED_VAR(clientContext);
373   UNUSED_VAR(buf);
374   UNUSED_VAR(len);
375 }
376 
377 /**
378  * Handles the service response for the get capabilities client request.
379  *
380  * This function is called from chppDispatchGnssResponse().
381  *
382  * @param clientContext Maintains status for each client instance.
383  * @param buf Input data. Cannot be null.
384  * @param len Length of input data in bytes.
385  */
chppGnssGetCapabilitiesResult(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)386 static void chppGnssGetCapabilitiesResult(
387     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
388   if (len < sizeof(struct ChppGnssGetCapabilitiesResponse)) {
389     CHPP_LOGE("Bad GNSS capabilities len=%" PRIuSIZE, len);
390 
391   } else {
392     struct ChppGnssGetCapabilitiesParameters *result =
393         &((struct ChppGnssGetCapabilitiesResponse *)buf)->params;
394 
395     CHPP_LOGD("chppGnssGetCapabilitiesResult received capabilities=0x%" PRIx32,
396               result->capabilities);
397 
398     CHPP_ASSERT((result->capabilities & CHPP_GNSS_DEFAULT_CAPABILITIES) ==
399                 CHPP_GNSS_DEFAULT_CAPABILITIES);
400     if (result->capabilities != CHPP_GNSS_DEFAULT_CAPABILITIES) {
401       CHPP_LOGE("GNSS capabilities 0x%" PRIx32 " != 0x%" PRIx32,
402                 result->capabilities, (uint32_t)CHPP_GNSS_DEFAULT_CAPABILITIES);
403     }
404 
405     clientContext->capabilitiesValid = true;
406     clientContext->capabilities = result->capabilities;
407   }
408 }
409 
410 /**
411  * Handles the service response for the Control Location Session client request.
412  *
413  * This function is called from chppDispatchGnssResponse().
414  *
415  * @param clientContext Maintains status for each client instance.
416  * @param buf Input data. Cannot be null.
417  * @param len Length of input data in bytes.
418  */
chppGnssControlLocationSessionResult(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)419 static void chppGnssControlLocationSessionResult(
420     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
421   UNUSED_VAR(clientContext);
422 
423   if (len < sizeof(struct ChppGnssControlLocationSessionResponse)) {
424     // Short response length indicates an error
425     gCallbacks->locationStatusChangeCallback(
426         false, chppAppShortResponseErrorHandler(buf, len, "ControlLocation"));
427 
428   } else {
429     struct ChppGnssControlLocationSessionResponse *result =
430         (struct ChppGnssControlLocationSessionResponse *)buf;
431 
432     CHPP_LOGD(
433         "chppGnssControlLocationSessionResult received enable=%d, "
434         "errorCode=%" PRIu8,
435         result->enabled, result->errorCode);
436 
437     gCallbacks->locationStatusChangeCallback(result->enabled,
438                                              result->errorCode);
439   }
440 }
441 
442 /**
443  * Handles the service response for the Control Measurement Session client
444  * request.
445  *
446  * This function is called from chppDispatchGnssResponse().
447  *
448  * @param clientContext Maintains status for each client instance.
449  * @param buf Input data. Cannot be null.
450  * @param len Length of input data in bytes.
451  */
chppGnssControlMeasurementSessionResult(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)452 static void chppGnssControlMeasurementSessionResult(
453     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
454   UNUSED_VAR(clientContext);
455 
456   if (len < sizeof(struct ChppGnssControlMeasurementSessionResponse)) {
457     // Short response length indicates an error
458     gCallbacks->measurementStatusChangeCallback(
459         false, chppAppShortResponseErrorHandler(buf, len, "Measurement"));
460 
461   } else {
462     struct ChppGnssControlMeasurementSessionResponse *result =
463         (struct ChppGnssControlMeasurementSessionResponse *)buf;
464 
465     CHPP_LOGD(
466         "chppGnssControlMeasurementSessionResult received enable=%d, "
467         "errorCode=%" PRIu8,
468         result->enabled, result->errorCode);
469 
470     gCallbacks->measurementStatusChangeCallback(result->enabled,
471                                                 result->errorCode);
472   }
473 }
474 
475 /**
476  * Handles the service response for the Configure Passive Location Listener
477  * client request.
478  *
479  * This function is called from chppDispatchGnssResponse().
480  *
481  * @param clientContext Maintains status for each client instance.
482  * @param buf Input data. Cannot be null.
483  * @param len Length of input data in bytes.
484  */
chppGnssConfigurePassiveLocationListenerResult(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)485 static void chppGnssConfigurePassiveLocationListenerResult(
486     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
487   UNUSED_VAR(clientContext);
488   UNUSED_VAR(len);
489 
490   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
491 
492   if (rxHeader->error != CHPP_APP_ERROR_NONE) {
493     CHPP_DEBUG_ASSERT_LOG(false, "Passive scan failed at service");
494 
495   } else {
496     CHPP_LOGD(
497         "WiFi ConfigurePassiveLocationListener request accepted at service");
498   }
499 }
500 
501 /**
502  * Handles the State Resync service notification.
503  *
504  * This function is called from chppDispatchGnssNotification().
505  *
506  * @param clientContext Maintains status for each client instance.
507  * @param buf Input data. Cannot be null.
508  * @param len Length of input data in bytes.
509  */
chppGnssStateResyncNotification(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)510 static void chppGnssStateResyncNotification(
511     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
512   UNUSED_VAR(buf);
513   UNUSED_VAR(len);
514   if (clientContext->client.openState == CHPP_OPEN_STATE_WAITING_TO_OPEN) {
515     // If the GNSS client is waiting for the open to proceed, the CHRE handler
516     // for requestStateResync() may fail, so we set a flag to process it later
517     // when the open has succeeded.
518     clientContext->requestStateResyncPending = true;
519   } else {
520     gCallbacks->requestStateResync();
521     clientContext->requestStateResyncPending = false;
522   }
523 }
524 
525 /**
526  * Handles the Location Result service notification.
527  *
528  * This function is called from chppDispatchGnssNotification().
529  *
530  * @param clientContext Maintains status for each client instance.
531  * @param buf Input data. Cannot be null.
532  * @param len Length of input data in bytes.
533  */
chppGnssLocationResultNotification(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)534 static void chppGnssLocationResultNotification(
535     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
536   UNUSED_VAR(clientContext);
537   CHPP_LOGD("chppGnssLocationResultNotification received data len=%" PRIuSIZE,
538             len);
539 
540   buf += sizeof(struct ChppAppHeader);
541   len -= sizeof(struct ChppAppHeader);
542 
543   struct chreGnssLocationEvent *chre =
544       chppGnssLocationEventToChre((struct ChppGnssLocationEvent *)buf, len);
545 
546   if (chre == NULL) {
547     CHPP_LOGE("Location result conversion failed: len=%" PRIuSIZE, len);
548   } else {
549     gCallbacks->locationEventCallback(chre);
550   }
551 }
552 
553 /**
554  * Handles the Measurement Result service notification.
555  *
556  * This function is called from chppDispatchGnssNotification().
557  *
558  * @param clientContext Maintains status for each client instance.
559  * @param buf Input data. Cannot be null.
560  * @param len Length of input data in bytes.
561  */
chppGnssMeasurementResultNotification(struct ChppGnssClientState * clientContext,uint8_t * buf,size_t len)562 static void chppGnssMeasurementResultNotification(
563     struct ChppGnssClientState *clientContext, uint8_t *buf, size_t len) {
564   UNUSED_VAR(clientContext);
565   CHPP_LOGD(
566       "chppGnssMeasurementResultNotification received data len=%" PRIuSIZE,
567       len);
568 
569   buf += sizeof(struct ChppAppHeader);
570   len -= sizeof(struct ChppAppHeader);
571 
572   struct chreGnssDataEvent *chre =
573       chppGnssDataEventToChre((struct ChppGnssDataEvent *)buf, len);
574 
575   if (chre == NULL) {
576     CHPP_LOGE("Measurement result conversion failed len=%" PRIuSIZE, len);
577   } else {
578     gCallbacks->measurementEventCallback(chre);
579   }
580 }
581 
582 /**
583  * Initializes the GNSS client upon an open request from CHRE and responds
584  * with the result.
585  *
586  * @param systemApi CHRE system function pointers.
587  * @param callbacks CHRE entry points.
588  *
589  * @return True if successful. False otherwise.
590  */
chppGnssClientOpen(const struct chrePalSystemApi * systemApi,const struct chrePalGnssCallbacks * callbacks)591 static bool chppGnssClientOpen(const struct chrePalSystemApi *systemApi,
592                                const struct chrePalGnssCallbacks *callbacks) {
593   CHPP_DEBUG_NOT_NULL(systemApi);
594   CHPP_DEBUG_NOT_NULL(callbacks);
595 
596   bool result = false;
597   gSystemApi = systemApi;
598   gCallbacks = callbacks;
599 
600   CHPP_LOGD("GNSS client opening");
601   if (gGnssClientContext.client.appContext == NULL) {
602     CHPP_LOGE("GNSS client app is null");
603   } else {
604     if (chppWaitForDiscoveryComplete(gGnssClientContext.client.appContext,
605                                      CHPP_GNSS_DISCOVERY_TIMEOUT_MS)) {
606       result = chppClientSendOpenRequest(
607           &gGnssClientContext.client,
608           &gGnssClientContext.outReqStates[CHPP_GNSS_OPEN], CHPP_GNSS_OPEN,
609           /*blocking=*/true);
610     }
611 
612     // Since CHPP_GNSS_DEFAULT_CAPABILITIES is mandatory, we can always
613     // pseudo-open and return true. Otherwise, these should have been gated.
614     chppClientPseudoOpen(&gGnssClientContext.client);
615     result = true;
616   }
617 
618   return result;
619 }
620 
621 /**
622  * Deinitializes the GNSS client.
623  */
chppGnssClientClose(void)624 static void chppGnssClientClose(void) {
625   // Remote
626   struct ChppAppHeader *request = chppAllocClientRequestCommand(
627       &gGnssClientContext.client, CHPP_GNSS_CLOSE);
628 
629   if (request == NULL) {
630     CHPP_LOG_OOM();
631   } else if (chppClientSendTimestampedRequestAndWait(
632                  &gGnssClientContext.client,
633                  &gGnssClientContext.outReqStates[CHPP_GNSS_CLOSE], request,
634                  sizeof(*request))) {
635     gGnssClientContext.client.openState = CHPP_OPEN_STATE_CLOSED;
636     gGnssClientContext.capabilities = CHRE_GNSS_CAPABILITIES_NONE;
637     gGnssClientContext.capabilitiesValid = false;
638     chppClientCloseOpenRequests(&gGnssClientContext.client, &kGnssClientConfig,
639                                 true /* clearOnly */);
640   }
641 }
642 
643 /**
644  * Retrieves a set of flags indicating the GNSS features supported by the
645  * current implementation.
646  *
647  * @return Capabilities flags.
648  */
chppGnssClientGetCapabilities(void)649 static uint32_t chppGnssClientGetCapabilities(void) {
650   uint32_t capabilities = CHPP_GNSS_DEFAULT_CAPABILITIES;
651 
652   if (gGnssClientContext.capabilitiesValid) {
653     // Result already cached
654     capabilities = gGnssClientContext.capabilities;
655 
656   } else {
657     struct ChppAppHeader *request = chppAllocClientRequestCommand(
658         &gGnssClientContext.client, CHPP_GNSS_GET_CAPABILITIES);
659 
660     if (request == NULL) {
661       CHPP_LOG_OOM();
662     } else {
663       if (chppClientSendTimestampedRequestAndWait(
664               &gGnssClientContext.client,
665               &gGnssClientContext.outReqStates[CHPP_GNSS_GET_CAPABILITIES],
666               request, sizeof(*request))) {
667         // Success. gGnssClientContext.capabilities is now populated
668         if (gGnssClientContext.capabilitiesValid) {
669           capabilities = gGnssClientContext.capabilities;
670         }
671       }
672     }
673   }
674 
675   return capabilities;
676 }
677 
678 /**
679  * Start/stop/modify the GNSS location session used for clients of the CHRE
680  * API.
681  *
682  * @param enable true to start/modify the session, false to stop the
683  *        session. If false, other parameters are ignored.
684  * @param minIntervalMs See chreGnssLocationSessionStartAsync()
685  * @param minTimeToNextFixMs See chreGnssLocationSessionStartAsync()
686  *
687  * @return True indicates the request was sent off to the service.
688  */
689 
chppGnssClientControlLocationSession(bool enable,uint32_t minIntervalMs,uint32_t minTimeToNextFixMs)690 static bool chppGnssClientControlLocationSession(bool enable,
691                                                  uint32_t minIntervalMs,
692                                                  uint32_t minTimeToNextFixMs) {
693   bool result = false;
694 
695   struct ChppGnssControlLocationSessionRequest *request =
696       chppAllocClientRequestFixed(&gGnssClientContext.client,
697                                   struct ChppGnssControlLocationSessionRequest);
698 
699   if (request == NULL) {
700     CHPP_LOG_OOM();
701   } else {
702     request->header.command = CHPP_GNSS_CONTROL_LOCATION_SESSION;
703     request->params.enable = enable;
704     request->params.minIntervalMs = minIntervalMs;
705     request->params.minTimeToNextFixMs = minTimeToNextFixMs;
706 
707     result = chppClientSendTimestampedRequestOrFail(
708         &gGnssClientContext.client,
709         &gGnssClientContext.outReqStates[CHPP_GNSS_CONTROL_LOCATION_SESSION],
710         request, sizeof(*request), CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS);
711   }
712 
713   return result;
714 }
715 
716 /**
717  * Releases the memory held for the location event callback.
718  *
719  * @param event Location event to be released.
720  */
chppGnssClientReleaseLocationEvent(struct chreGnssLocationEvent * event)721 static void chppGnssClientReleaseLocationEvent(
722     struct chreGnssLocationEvent *event) {
723   CHPP_FREE_AND_NULLIFY(event);
724 }
725 
726 /**
727  * Start/stop/modify the raw GNSS measurement session used for clients of the
728  * CHRE API.
729  *
730  * @param enable true to start/modify the session, false to stop the
731  *        session. If false, other parameters are ignored.
732  * @param minIntervalMs See chreGnssMeasurementSessionStartAsync()
733  *
734  * @return True indicates the request was sent off to the service.
735  */
736 
chppGnssClientControlMeasurementSession(bool enable,uint32_t minIntervalMs)737 static bool chppGnssClientControlMeasurementSession(bool enable,
738                                                     uint32_t minIntervalMs) {
739   bool result = false;
740 
741   struct ChppGnssControlMeasurementSessionRequest *request =
742       chppAllocClientRequestFixed(
743           &gGnssClientContext.client,
744           struct ChppGnssControlMeasurementSessionRequest);
745 
746   if (request == NULL) {
747     CHPP_LOG_OOM();
748   } else {
749     request->header.command = CHPP_GNSS_CONTROL_MEASUREMENT_SESSION;
750     request->params.enable = enable;
751     request->params.minIntervalMs = minIntervalMs;
752 
753     result = chppClientSendTimestampedRequestOrFail(
754         &gGnssClientContext.client,
755         &gGnssClientContext.outReqStates[CHPP_GNSS_CONTROL_MEASUREMENT_SESSION],
756         request, sizeof(*request), CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS);
757   }
758 
759   return result;
760 }
761 
762 /**
763  * Releases the memory held for the measurement event callback.
764  *
765  * @param event Measurement event to be released.
766  */
chppGnssClientReleaseMeasurementDataEvent(struct chreGnssDataEvent * event)767 static void chppGnssClientReleaseMeasurementDataEvent(
768     struct chreGnssDataEvent *event) {
769   if (event->measurement_count > 0) {
770     void *measurements = CHPP_CONST_CAST_POINTER(event->measurements);
771     CHPP_FREE_AND_NULLIFY(measurements);
772   }
773 
774   CHPP_FREE_AND_NULLIFY(event);
775 }
776 
777 /**
778  * Starts/stops opportunistic delivery of location fixes.
779  *
780  * @param enable true to turn the passive location listener on, false to
781  *        turn it off.
782  *
783  * @return True indicates the request was sent off to the service.
784  */
chppGnssClientConfigurePassiveLocationListener(bool enable)785 static bool chppGnssClientConfigurePassiveLocationListener(bool enable) {
786   bool result = false;
787 
788   struct ChppGnssConfigurePassiveLocationListenerRequest *request =
789       chppAllocClientRequestFixed(
790           &gGnssClientContext.client,
791           struct ChppGnssConfigurePassiveLocationListenerRequest);
792 
793   if (request == NULL) {
794     CHPP_LOG_OOM();
795   } else {
796     request->header.command = CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER;
797     request->params.enable = enable;
798 
799     result = chppClientSendTimestampedRequestOrFail(
800         &gGnssClientContext.client,
801         &gGnssClientContext
802              .outReqStates[CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER],
803         request, sizeof(*request), CHPP_REQUEST_TIMEOUT_DEFAULT);
804   }
805 
806   return result;
807 }
808 
809 /************************************************
810  *  Public Functions
811  ***********************************************/
812 
chppRegisterGnssClient(struct ChppAppState * appContext)813 void chppRegisterGnssClient(struct ChppAppState *appContext) {
814   memset(&gGnssClientContext, 0, sizeof(gGnssClientContext));
815   chppRegisterClient(appContext, (void *)&gGnssClientContext,
816                      &gGnssClientContext.client,
817                      gGnssClientContext.outReqStates, &kGnssClientConfig);
818 }
819 
chppDeregisterGnssClient(struct ChppAppState * appContext)820 void chppDeregisterGnssClient(struct ChppAppState *appContext) {
821   // TODO
822 
823   UNUSED_VAR(appContext);
824 }
825 
getChppGnssClientState(void)826 struct ChppEndpointState *getChppGnssClientState(void) {
827   return &gGnssClientContext.client;
828 }
829 
830 #ifdef CHPP_CLIENT_ENABLED_GNSS
831 
832 #ifdef CHPP_CLIENT_ENABLED_CHRE_GNSS
chrePalGnssGetApi(uint32_t requestedApiVersion)833 const struct chrePalGnssApi *chrePalGnssGetApi(uint32_t requestedApiVersion) {
834 #else
835 const struct chrePalGnssApi *chppPalGnssGetApi(uint32_t requestedApiVersion) {
836 #endif
837 
838   static const struct chrePalGnssApi api = {
839       .moduleVersion = CHPP_PAL_GNSS_API_VERSION,
840       .open = chppGnssClientOpen,
841       .close = chppGnssClientClose,
842       .getCapabilities = chppGnssClientGetCapabilities,
843       .controlLocationSession = chppGnssClientControlLocationSession,
844       .releaseLocationEvent = chppGnssClientReleaseLocationEvent,
845       .controlMeasurementSession = chppGnssClientControlMeasurementSession,
846       .releaseMeasurementDataEvent = chppGnssClientReleaseMeasurementDataEvent,
847       .configurePassiveLocationListener =
848           chppGnssClientConfigurePassiveLocationListener,
849   };
850 
851   CHPP_STATIC_ASSERT(
852       CHRE_PAL_GNSS_API_CURRENT_VERSION == CHPP_PAL_GNSS_API_VERSION,
853       "A newer CHRE PAL API version is available. Please update.");
854 
855   if (!CHRE_PAL_VERSIONS_ARE_COMPATIBLE(api.moduleVersion,
856                                         requestedApiVersion)) {
857     return NULL;
858   } else {
859     return &api;
860   }
861 }
862 
863 #endif
864