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