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/services/gnss.h"
18 
19 #include <inttypes.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 
23 #include "chpp/common/gnss.h"
24 #include "chpp/common/gnss_types.h"
25 #include "chpp/common/standard_uuids.h"
26 #include "chpp/log.h"
27 #include "chpp/macros.h"
28 #include "chpp/services.h"
29 #include "chre/pal/gnss.h"
30 
31 /************************************************
32  *  Prototypes
33  ***********************************************/
34 
35 static enum ChppAppErrorCode chppDispatchGnssRequest(void *serviceContext,
36                                                      uint8_t *buf, size_t len);
37 static void chppGnssServiceNotifyReset(void *serviceContext);
38 
39 /************************************************
40  *  Private Definitions
41  ***********************************************/
42 
43 /**
44  * Configuration parameters for this service
45  */
46 static const struct ChppService kGnssServiceConfig = {
47     .descriptor.uuid = CHPP_UUID_GNSS_STANDARD,
48 
49     // Human-readable name
50     .descriptor.name = "GNSS",
51 
52     // Version
53     .descriptor.version.major = 1,
54     .descriptor.version.minor = 0,
55     .descriptor.version.patch = 0,
56 
57     // Notifies service if CHPP is reset
58     .resetNotifierFunctionPtr = &chppGnssServiceNotifyReset,
59 
60     // Client request dispatch function pointer
61     .requestDispatchFunctionPtr = &chppDispatchGnssRequest,
62 
63     // Client notification dispatch function pointer
64     .notificationDispatchFunctionPtr = NULL,  // Not supported
65 
66     // Min length is the entire header
67     .minLength = sizeof(struct ChppAppHeader),
68 };
69 
70 /**
71  * Structure to maintain state for the GNSS service and its Request/Response
72  * (RR) functionality.
73  */
74 struct ChppGnssServiceState {
75   struct ChppServiceState service;   // GNSS service state
76   const struct chrePalGnssApi *api;  // GNSS PAL API
77 
78   // Based on chre/pal/gnss.h and chrePalGnssApi
79   struct ChppRequestResponseState open;             // Service init state
80   struct ChppRequestResponseState close;            // Service deinit state
81   struct ChppRequestResponseState getCapabilities;  // Get Capabilities state
82   struct ChppRequestResponseState
83       controlLocationSession;  // Control Location measurement state
84   struct ChppRequestResponseState
85       controlMeasurementSession;  // Control Raw GNSS measurement state
86   struct ChppRequestResponseState
87       configurePassiveLocationListener;  // Configure Passive location receiving
88                                          // state
89 };
90 
91 // Note: The CHRE PAL API only allows for one definition - see comment in WWAN
92 // service for details.
93 // Note: There is no notion of a cookie in the CHRE GNSS API so we need to use
94 // the global service state (gGnssServiceContext) directly in all callbacks.
95 struct ChppGnssServiceState gGnssServiceContext;
96 
97 /************************************************
98  *  Prototypes
99  ***********************************************/
100 
101 static enum ChppAppErrorCode chppGnssServiceOpen(
102     struct ChppGnssServiceState *gnssServiceContext,
103     struct ChppAppHeader *requestHeader);
104 static enum ChppAppErrorCode chppGnssServiceClose(
105     struct ChppGnssServiceState *gnssServiceContext,
106     struct ChppAppHeader *requestHeader);
107 static enum ChppAppErrorCode chppGnssServiceGetCapabilities(
108     struct ChppGnssServiceState *gnssServiceContext,
109     struct ChppAppHeader *requestHeader);
110 static enum ChppAppErrorCode chppGnssServiceControlLocationSession(
111     struct ChppGnssServiceState *gnssServiceContext,
112     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
113 static enum ChppAppErrorCode chppGnssServiceControlMeasurementSession(
114     struct ChppGnssServiceState *gnssServiceContext,
115     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
116 static enum ChppAppErrorCode chppGnssServiceConfigurePassiveLocationListener(
117     struct ChppGnssServiceState *gnssServiceContext,
118     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len);
119 
120 static void chppGnssServiceRequestStateResyncCallback(void);
121 static void chppGnssServiceLocationStatusChangeCallback(bool enabled,
122                                                         uint8_t errorCode);
123 static void chppGnssServiceLocationEventCallback(
124     struct chreGnssLocationEvent *event);
125 static void chppGnssServiceMeasurementStatusChangeCallback(bool enabled,
126                                                            uint8_t errorCode);
127 static void chppGnssServiceMeasurementEventCallback(
128     struct chreGnssDataEvent *event);
129 
130 /************************************************
131  *  Private Functions
132  ***********************************************/
133 
134 /**
135  * Dispatches a client request from the transport layer that is determined to be
136  * for the GNSS service. If the result of the dispatch is an error, this
137  * function responds to the client with the same error.
138  *
139  * This function is called from the app layer using its function pointer given
140  * during service registration.
141  *
142  * @param serviceContext Maintains status for each service instance.
143  * @param buf Input data. Cannot be null.
144  * @param len Length of input data in bytes.
145  *
146  * @return Indicates the result of this function call.
147  */
chppDispatchGnssRequest(void * serviceContext,uint8_t * buf,size_t len)148 static enum ChppAppErrorCode chppDispatchGnssRequest(void *serviceContext,
149                                                      uint8_t *buf, size_t len) {
150   struct ChppAppHeader *rxHeader = (struct ChppAppHeader *)buf;
151   buf += sizeof(struct ChppAppHeader);
152   len -= sizeof(struct ChppAppHeader);
153 
154   struct ChppGnssServiceState *gnssServiceContext =
155       (struct ChppGnssServiceState *)serviceContext;
156   struct ChppRequestResponseState *rRState = NULL;
157   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
158   bool dispatched = true;
159 
160   switch (rxHeader->command) {
161     case CHPP_GNSS_OPEN: {
162       rRState = &gnssServiceContext->open;
163       chppServiceTimestampRequest(rRState, rxHeader);
164       error = chppGnssServiceOpen(gnssServiceContext, rxHeader);
165       break;
166     }
167 
168     case CHPP_GNSS_CLOSE: {
169       rRState = &gnssServiceContext->close;
170       chppServiceTimestampRequest(rRState, rxHeader);
171       error = chppGnssServiceClose(gnssServiceContext, rxHeader);
172       break;
173     }
174 
175     case CHPP_GNSS_GET_CAPABILITIES: {
176       rRState = &gnssServiceContext->getCapabilities;
177       chppServiceTimestampRequest(rRState, rxHeader);
178       error = chppGnssServiceGetCapabilities(gnssServiceContext, rxHeader);
179       break;
180     }
181 
182     case CHPP_GNSS_CONTROL_LOCATION_SESSION: {
183       rRState = &gnssServiceContext->controlLocationSession;
184       chppServiceTimestampRequest(rRState, rxHeader);
185       error = chppGnssServiceControlLocationSession(gnssServiceContext,
186                                                     rxHeader, buf, len);
187       break;
188     }
189 
190     case CHPP_GNSS_CONTROL_MEASUREMENT_SESSION: {
191       rRState = &gnssServiceContext->controlMeasurementSession;
192       chppServiceTimestampRequest(rRState, rxHeader);
193       error = chppGnssServiceControlMeasurementSession(gnssServiceContext,
194                                                        rxHeader, buf, len);
195       break;
196     }
197 
198     case CHPP_GNSS_CONFIGURE_PASSIVE_LOCATION_LISTENER: {
199       rRState = &gnssServiceContext->configurePassiveLocationListener;
200       chppServiceTimestampRequest(rRState, rxHeader);
201       error = chppGnssServiceConfigurePassiveLocationListener(
202           gnssServiceContext, rxHeader, buf, len);
203       break;
204     }
205 
206     default: {
207       dispatched = false;
208       error = CHPP_APP_ERROR_INVALID_COMMAND;
209       break;
210     }
211   }
212 
213   if (dispatched == true && error != CHPP_APP_ERROR_NONE) {
214     // Request was dispatched but an error was returned. Close out
215     // chppServiceTimestampRequest()
216     chppServiceTimestampResponse(rRState);
217   }
218 
219   return error;
220 }
221 
222 /**
223  * Initializes the GNSS service upon an open request from the client and
224  * responds to the client with the result.
225  *
226  * @param serviceContext Maintains status for each service instance.
227  * @param requestHeader App layer header of the request.
228  *
229  * @return Indicates the result of this function call.
230  */
chppGnssServiceOpen(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader)231 static enum ChppAppErrorCode chppGnssServiceOpen(
232     struct ChppGnssServiceState *gnssServiceContext,
233     struct ChppAppHeader *requestHeader) {
234   static const struct chrePalGnssCallbacks palCallbacks = {
235       .requestStateResync = chppGnssServiceRequestStateResyncCallback,
236       .locationStatusChangeCallback =
237           chppGnssServiceLocationStatusChangeCallback,
238       .locationEventCallback = chppGnssServiceLocationEventCallback,
239       .measurementStatusChangeCallback =
240           chppGnssServiceMeasurementStatusChangeCallback,
241       .measurementEventCallback = chppGnssServiceMeasurementEventCallback,
242   };
243 
244   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
245 
246   if (gnssServiceContext->service.openState == CHPP_OPEN_STATE_OPENED) {
247     CHPP_LOGE("GNSS service already open");
248     CHPP_DEBUG_ASSERT(false);
249     error = CHPP_APP_ERROR_INVALID_COMMAND;
250 
251   } else if (!gnssServiceContext->api->open(
252                  gnssServiceContext->service.appContext->systemApi,
253                  &palCallbacks)) {
254     CHPP_LOGE("GNSS PAL open failed");
255     CHPP_DEBUG_ASSERT(false);
256     error = CHPP_APP_ERROR_BEYOND_CHPP;
257 
258   } else {
259     CHPP_LOGI("GNSS service opened");
260     gnssServiceContext->service.openState = CHPP_OPEN_STATE_OPENED;
261 
262     struct ChppAppHeader *response =
263         chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
264     size_t responseLen = sizeof(*response);
265 
266     if (response == NULL) {
267       CHPP_LOG_OOM();
268       error = CHPP_APP_ERROR_OOM;
269     } else {
270       chppSendTimestampedResponseOrFail(&gnssServiceContext->service,
271                                         &gnssServiceContext->open, response,
272                                         responseLen);
273     }
274   }
275 
276   return error;
277 }
278 
279 /**
280  * Deinitializes the GNSS service.
281  *
282  * @param serviceContext Maintains status for each service instance.
283  * @param requestHeader App layer header of the request.
284  *
285  * @return Indicates the result of this function call.
286  */
chppGnssServiceClose(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader)287 static enum ChppAppErrorCode chppGnssServiceClose(
288     struct ChppGnssServiceState *gnssServiceContext,
289     struct ChppAppHeader *requestHeader) {
290   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
291 
292   gnssServiceContext->api->close();
293   gnssServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
294 
295   CHPP_LOGI("GNSS service closed");
296 
297   struct ChppAppHeader *response =
298       chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
299   size_t responseLen = sizeof(*response);
300 
301   if (response == NULL) {
302     CHPP_LOG_OOM();
303     error = CHPP_APP_ERROR_OOM;
304   } else {
305     chppSendTimestampedResponseOrFail(&gnssServiceContext->service,
306                                       &gnssServiceContext->close, response,
307                                       responseLen);
308   }
309 
310   return error;
311 }
312 
313 /**
314  * Notifies the service of an incoming reset.
315  *
316  * @param serviceContext Maintains status for each service instance.
317  */
chppGnssServiceNotifyReset(void * serviceContext)318 static void chppGnssServiceNotifyReset(void *serviceContext) {
319   struct ChppGnssServiceState *gnssServiceContext =
320       (struct ChppGnssServiceState *)serviceContext;
321 
322   if (gnssServiceContext->service.openState != CHPP_OPEN_STATE_OPENED) {
323     CHPP_LOGW("GNSS service reset but wasn't open");
324   } else {
325     CHPP_LOGI("GNSS service reset. Closing");
326     gnssServiceContext->service.openState = CHPP_OPEN_STATE_CLOSED;
327     gnssServiceContext->api->close();
328   }
329 }
330 
331 /**
332  * Retrieves a set of flags indicating the GNSS features supported by the
333  * current implementation.
334  *
335  * @param serviceContext Maintains status for each service instance.
336  * @param requestHeader App layer header of the request.
337  *
338  * @return Indicates the result of this function call.
339  */
chppGnssServiceGetCapabilities(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader)340 static enum ChppAppErrorCode chppGnssServiceGetCapabilities(
341     struct ChppGnssServiceState *gnssServiceContext,
342     struct ChppAppHeader *requestHeader) {
343   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
344 
345   struct ChppGnssGetCapabilitiesResponse *response =
346       chppAllocServiceResponseFixed(requestHeader,
347                                     struct ChppGnssGetCapabilitiesResponse);
348   size_t responseLen = sizeof(*response);
349 
350   if (response == NULL) {
351     CHPP_LOG_OOM();
352     error = CHPP_APP_ERROR_OOM;
353   } else {
354     response->params.capabilities = gnssServiceContext->api->getCapabilities();
355 
356     CHPP_LOGD("chppGnssServiceGetCapabilities returning 0x%" PRIx32
357               ", %" PRIuSIZE " bytes",
358               response->params.capabilities, responseLen);
359     chppSendTimestampedResponseOrFail(&gnssServiceContext->service,
360                                       &gnssServiceContext->getCapabilities,
361                                       response, responseLen);
362   }
363 
364   return error;
365 }
366 
367 /**
368  * Start/stop/modify the GNSS location session.
369  *
370  * This function returns an error code synchronously.
371  * A subsequent call to chppGnssServiceLocationStatusChangeCallback() will be
372  * used to communicate the result of this request (as a service response).
373  * A subsequent call to chppGnssServiceLocationEventCallback() will be used to
374  * communicate the location fixes (as service notifications).
375  *
376  * @param serviceContext Maintains status for each service instance.
377  * @param requestHeader App layer header of the request.
378  * @param buf Input data. Cannot be null.
379  * @param len Length of input data in bytes.
380  *
381  * @return Indicates the result of this function call.
382  */
chppGnssServiceControlLocationSession(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader,uint8_t * buf,size_t len)383 static enum ChppAppErrorCode chppGnssServiceControlLocationSession(
384     struct ChppGnssServiceState *gnssServiceContext,
385     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
386   UNUSED_VAR(requestHeader);
387   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
388 
389   if (len < sizeof(struct ChppGnssControlLocationSessionParameters)) {
390     error = CHPP_APP_ERROR_INVALID_ARG;
391 
392   } else {
393     struct ChppGnssControlLocationSessionParameters *parameters =
394         (struct ChppGnssControlLocationSessionParameters *)buf;
395 
396     if (!gnssServiceContext->api->controlLocationSession(
397             parameters->enable, parameters->minIntervalMs,
398             parameters->minTimeToNextFixMs)) {
399       error = CHPP_APP_ERROR_UNSPECIFIED;
400     }
401   }
402 
403   return error;
404 }
405 
406 /**
407  * Start/stop/modify the raw GNSS measurement session.
408  *
409  * This function returns an error code synchronously.
410  * A subsequent call to chppGnssServiceMeasurementStatusChangeCallback() will be
411  * used to communicate the result of this request (as a service response).
412  * A subsequent call to chppGnssServiceMeasurementEventCallback() will be used
413  * to communicate the measurements (as service notifications).
414  *
415  * @param serviceContext Maintains status for each service instance.
416  * @param requestHeader App layer header of the request.
417  * @param buf Input data. Cannot be null.
418  * @param len Length of input data in bytes.
419  *
420  * @return Indicates the result of this function call.
421  */
chppGnssServiceControlMeasurementSession(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader,uint8_t * buf,size_t len)422 static enum ChppAppErrorCode chppGnssServiceControlMeasurementSession(
423     struct ChppGnssServiceState *gnssServiceContext,
424     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
425   UNUSED_VAR(requestHeader);
426   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
427 
428   if (len < sizeof(struct ChppGnssControlMeasurementSessionParameters)) {
429     error = CHPP_APP_ERROR_INVALID_ARG;
430 
431   } else {
432     struct ChppGnssControlMeasurementSessionParameters *parameters =
433         (struct ChppGnssControlMeasurementSessionParameters *)buf;
434 
435     if (!gnssServiceContext->api->controlMeasurementSession(
436             parameters->enable, parameters->minIntervalMs)) {
437       error = CHPP_APP_ERROR_UNSPECIFIED;
438     }
439   }
440 
441   return error;
442 }
443 
444 /**
445  * Configures whether to opportunistically deliver any location fixes produced
446  * for other clients of the GNSS engine.
447  *
448  * This function returns an error code synchronously.
449  * A subsequent call to chppGnssServiceLocationEventCallback() will be used to
450  * communicate the location fixes (as service notifications).
451  *
452  * @param serviceContext Maintains status for each service instance.
453  * @param requestHeader App layer header of the request.
454  * @param buf Input data. Cannot be null.
455  * @param len Length of input data in bytes.
456  *
457  * @return Indicates the result of this function call.
458  */
chppGnssServiceConfigurePassiveLocationListener(struct ChppGnssServiceState * gnssServiceContext,struct ChppAppHeader * requestHeader,uint8_t * buf,size_t len)459 static enum ChppAppErrorCode chppGnssServiceConfigurePassiveLocationListener(
460     struct ChppGnssServiceState *gnssServiceContext,
461     struct ChppAppHeader *requestHeader, uint8_t *buf, size_t len) {
462   UNUSED_VAR(requestHeader);
463   enum ChppAppErrorCode error = CHPP_APP_ERROR_NONE;
464 
465   if (len < sizeof(struct ChppGnssConfigurePassiveLocationListenerParameters)) {
466     error = CHPP_APP_ERROR_INVALID_ARG;
467   } else {
468     struct ChppGnssConfigurePassiveLocationListenerParameters *parameters =
469         (struct ChppGnssConfigurePassiveLocationListenerParameters *)buf;
470 
471     if (!gnssServiceContext->api->configurePassiveLocationListener(
472             parameters->enable)) {
473       error = CHPP_APP_ERROR_UNSPECIFIED;
474 
475     } else {
476       struct ChppAppHeader *response =
477           chppAllocServiceResponseFixed(requestHeader, struct ChppAppHeader);
478       size_t responseLen = sizeof(*response);
479 
480       if (response == NULL) {
481         CHPP_LOG_OOM();
482         error = CHPP_APP_ERROR_OOM;
483       } else {
484         chppSendTimestampedResponseOrFail(
485             &gnssServiceContext->service,
486             &gnssServiceContext->configurePassiveLocationListener, response,
487             responseLen);
488       }
489     }
490   }
491 
492   return error;
493 }
494 
495 /**
496  * GNSS PAL callback to request that the core CHRE system re-send requests for
497  * any active sessions and its current passive location listener setting.
498  */
chppGnssServiceRequestStateResyncCallback(void)499 static void chppGnssServiceRequestStateResyncCallback(void) {
500   struct ChppAppHeader *notification =
501       chppAllocServiceNotificationFixed(struct ChppAppHeader);
502   size_t notificationLen = sizeof(*notification);
503 
504   if (notification == NULL) {
505     CHPP_LOG_OOM();
506     CHPP_ASSERT(false);
507 
508   } else {
509     notification->handle = gGnssServiceContext.service.handle;
510     notification->command = CHPP_GNSS_REQUEST_STATE_RESYNC_NOTIFICATION;
511 
512     chppEnqueueTxDatagramOrFail(
513         gGnssServiceContext.service.appContext->transportContext, notification,
514         notificationLen);
515   }
516 }
517 
518 /**
519  * GNSS PAL callback to inform the CHRE of the result of changes to the location
520  * session status.
521  */
chppGnssServiceLocationStatusChangeCallback(bool enabled,uint8_t errorCode)522 static void chppGnssServiceLocationStatusChangeCallback(bool enabled,
523                                                         uint8_t errorCode) {
524   // Recreate request header
525   struct ChppAppHeader requestHeader = {
526       .handle = gGnssServiceContext.service.handle,
527       .transaction = gGnssServiceContext.controlLocationSession.transaction,
528       .command = CHPP_GNSS_CONTROL_LOCATION_SESSION,
529   };
530 
531   struct ChppGnssControlLocationSessionResponse *response =
532       chppAllocServiceResponseFixed(
533           &requestHeader, struct ChppGnssControlLocationSessionResponse);
534   size_t responseLen = sizeof(*response);
535 
536   if (response == NULL) {
537     CHPP_LOG_OOM();
538     CHPP_ASSERT(false);
539 
540   } else {
541     response->enabled = enabled;
542     response->errorCode = errorCode;
543 
544     chppSendTimestampedResponseOrFail(
545         &gGnssServiceContext.service,
546         &gGnssServiceContext.controlLocationSession, response, responseLen);
547   }
548 }
549 
550 /**
551  * GNSS PAL callback to pass GNSS location fixes to the core CHRE system.
552  */
chppGnssServiceLocationEventCallback(struct chreGnssLocationEvent * event)553 static void chppGnssServiceLocationEventCallback(
554     struct chreGnssLocationEvent *event) {
555   // Craft response per parser script
556   struct ChppGnssLocationEventWithHeader *notification = NULL;
557   size_t notificationLen = 0;
558 
559   if (!chppGnssLocationEventFromChre(event, &notification, &notificationLen)) {
560     CHPP_LOGE("LocationEvent conversion failed (OOM?)");
561 
562     notification = chppMalloc(sizeof(struct ChppAppHeader));
563     if (notification == NULL) {
564       CHPP_LOG_OOM();
565     } else {
566       notificationLen = sizeof(struct ChppAppHeader);
567     }
568   }
569 
570   if (notification != NULL) {
571     notification->header.handle = gGnssServiceContext.service.handle;
572     notification->header.type = CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION;
573     notification->header.transaction =
574         0;  // Because we don't know this is in response to a Location Session
575             // or Passive Location Listener
576     notification->header.error =
577         (notificationLen > sizeof(struct ChppAppHeader))
578             ? CHPP_APP_ERROR_NONE
579             : CHPP_APP_ERROR_CONVERSION_FAILED;
580     notification->header.command = CHPP_GNSS_LOCATION_RESULT_NOTIFICATION;
581 
582     chppEnqueueTxDatagramOrFail(
583         gGnssServiceContext.service.appContext->transportContext, notification,
584         notificationLen);
585   }
586 
587   gGnssServiceContext.api->releaseLocationEvent(event);
588 }
589 
590 /**
591  * GNSS PAL callback to inform the CHRE of the result of changes to the raw GNSS
592  * measurement session status.
593  */
chppGnssServiceMeasurementStatusChangeCallback(bool enabled,uint8_t errorCode)594 static void chppGnssServiceMeasurementStatusChangeCallback(bool enabled,
595                                                            uint8_t errorCode) {
596   // Recreate request header
597   struct ChppAppHeader requestHeader = {
598       .handle = gGnssServiceContext.service.handle,
599       .transaction = gGnssServiceContext.controlMeasurementSession.transaction,
600       .command = CHPP_GNSS_CONTROL_MEASUREMENT_SESSION,
601   };
602 
603   struct ChppGnssControlMeasurementSessionResponse *response =
604       chppAllocServiceResponseFixed(
605           &requestHeader, struct ChppGnssControlMeasurementSessionResponse);
606   size_t responseLen = sizeof(*response);
607 
608   if (response == NULL) {
609     CHPP_LOG_OOM();
610     CHPP_ASSERT(false);
611 
612   } else {
613     response->enabled = enabled;
614     response->errorCode = errorCode;
615 
616     chppSendTimestampedResponseOrFail(
617         &gGnssServiceContext.service,
618         &gGnssServiceContext.controlMeasurementSession, response, responseLen);
619   }
620 }
621 
622 /**
623  * GNSS PAL callback to pass raw GNSS measurement data to the core CHRE system.
624  */
chppGnssServiceMeasurementEventCallback(struct chreGnssDataEvent * event)625 static void chppGnssServiceMeasurementEventCallback(
626     struct chreGnssDataEvent *event) {
627   // Craft response per parser script
628   struct ChppGnssDataEventWithHeader *notification = NULL;
629   size_t notificationLen = 0;
630 
631   if (!chppGnssDataEventFromChre(event, &notification, &notificationLen)) {
632     CHPP_LOGE("DataEvent conversion failed (OOM?) ID=%" PRIu8,
633               gGnssServiceContext.controlMeasurementSession.transaction);
634 
635     notification = chppMalloc(sizeof(struct ChppAppHeader));
636     if (notification == NULL) {
637       CHPP_LOG_OOM();
638     } else {
639       notificationLen = sizeof(struct ChppAppHeader);
640     }
641   }
642 
643   if (notification != NULL) {
644     notification->header.handle = gGnssServiceContext.service.handle;
645     notification->header.type = CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION;
646     notification->header.transaction =
647         gGnssServiceContext.controlMeasurementSession.transaction;
648     notification->header.error =
649         (notificationLen > sizeof(struct ChppAppHeader))
650             ? CHPP_APP_ERROR_NONE
651             : CHPP_APP_ERROR_CONVERSION_FAILED;
652     notification->header.command = CHPP_GNSS_MEASUREMENT_RESULT_NOTIFICATION;
653 
654     chppEnqueueTxDatagramOrFail(
655         gGnssServiceContext.service.appContext->transportContext, notification,
656         notificationLen);
657   }
658 
659   gGnssServiceContext.api->releaseMeasurementDataEvent(event);
660 }
661 
662 /************************************************
663  *  Public Functions
664  ***********************************************/
665 
chppRegisterGnssService(struct ChppAppState * appContext)666 void chppRegisterGnssService(struct ChppAppState *appContext) {
667   gGnssServiceContext.api = chrePalGnssGetApi(CHPP_PAL_GNSS_API_VERSION);
668 
669   if (gGnssServiceContext.api == NULL) {
670     CHPP_LOGE(
671         "GNSS PAL API version not compatible with CHPP. Cannot register GNSS "
672         "service");
673     CHPP_DEBUG_ASSERT(false);
674 
675   } else {
676     gGnssServiceContext.service.appContext = appContext;
677     gGnssServiceContext.service.handle = chppRegisterService(
678         appContext, (void *)&gGnssServiceContext, &kGnssServiceConfig);
679     CHPP_DEBUG_ASSERT(gGnssServiceContext.service.handle);
680   }
681 }
682 
chppDeregisterGnssService(struct ChppAppState * appContext)683 void chppDeregisterGnssService(struct ChppAppState *appContext) {
684   // TODO
685 
686   UNUSED_VAR(appContext);
687 }
688