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