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