xref: /aosp_15_r20/system/chre/core/gnss_manager.cc (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2018 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 #ifdef CHRE_GNSS_SUPPORT_ENABLED
18 
19 #include "chre/core/gnss_manager.h"
20 
21 #include <cstddef>
22 
23 #include "chre/core/event_loop_manager.h"
24 #include "chre/core/settings.h"
25 #include "chre/platform/assert.h"
26 #include "chre/platform/fatal_error.h"
27 #include "chre/util/nested_data_ptr.h"
28 #include "chre/util/system/debug_dump.h"
29 #include "chre/util/system/event_callbacks.h"
30 
31 namespace chre {
32 
33 namespace {
34 
getCallbackType(uint16_t eventType,SystemCallbackType * callbackType)35 bool getCallbackType(uint16_t eventType, SystemCallbackType *callbackType) {
36   bool success = true;
37   switch (eventType) {
38     case CHRE_EVENT_GNSS_LOCATION: {
39       *callbackType = SystemCallbackType::GnssLocationReportEvent;
40       break;
41     }
42     case CHRE_EVENT_GNSS_DATA: {
43       *callbackType = SystemCallbackType::GnssMeasurementReportEvent;
44       break;
45     }
46     default: {
47       LOGE("Unknown event type %" PRIu16, eventType);
48       success = false;
49     }
50   }
51 
52   return success;
53 }
54 
getReportEventType(SystemCallbackType callbackType,uint16_t * eventType)55 bool getReportEventType(SystemCallbackType callbackType, uint16_t *eventType) {
56   bool success = true;
57   switch (callbackType) {
58     case SystemCallbackType::GnssLocationReportEvent: {
59       *eventType = CHRE_EVENT_GNSS_LOCATION;
60       break;
61     }
62     case SystemCallbackType::GnssMeasurementReportEvent: {
63       *eventType = CHRE_EVENT_GNSS_DATA;
64       break;
65     }
66     default: {
67       LOGE("Unknown callback type %" PRIu16,
68            static_cast<uint16_t>(callbackType));
69       success = false;
70     }
71   }
72 
73   return success;
74 }
75 
76 }  // anonymous namespace
77 
GnssManager()78 GnssManager::GnssManager()
79     : mLocationSession(CHRE_EVENT_GNSS_LOCATION),
80       mMeasurementSession(CHRE_EVENT_GNSS_DATA) {}
81 
init()82 void GnssManager::init() {
83   mPlatformGnss.init();
84 }
85 
getCapabilities()86 uint32_t GnssManager::getCapabilities() {
87   return mPlatformGnss.getCapabilities();
88 }
89 
onSettingChanged(Setting setting,bool enabled)90 void GnssManager::onSettingChanged(Setting setting, bool enabled) {
91   mLocationSession.onSettingChanged(setting, enabled);
92   mMeasurementSession.onSettingChanged(setting, enabled);
93 }
94 
handleRequestStateResyncCallback()95 void GnssManager::handleRequestStateResyncCallback() {
96   auto callback = [](uint16_t /* eventType */, void * /* eventData */,
97                      void * /* extraData */) {
98     EventLoopManagerSingleton::get()
99         ->getGnssManager()
100         .handleRequestStateResyncCallbackSync();
101   };
102   EventLoopManagerSingleton::get()->deferCallback(
103       SystemCallbackType::GnssRequestResyncEvent, nullptr /* data */, callback);
104 }
105 
configurePassiveLocationListener(Nanoapp * nanoapp,bool enable)106 bool GnssManager::configurePassiveLocationListener(Nanoapp *nanoapp,
107                                                    bool enable) {
108   bool success = false;
109   uint16_t instanceId = nanoapp->getInstanceId();
110 
111   size_t index;
112   if (nanoappHasPassiveLocationListener(instanceId, &index) != enable) {
113     uint32_t capabilities = getCapabilities();
114     bool locationSupported =
115         (capabilities & CHRE_GNSS_CAPABILITIES_LOCATION) != 0;
116     bool passiveLocationListenerSupported =
117         (capabilities &
118          CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER) != 0;
119 
120     if (!locationSupported) {
121       LOGE("Platform does not have the location capability");
122     } else if (enable && !mPassiveLocationListenerNanoapps.prepareForPush()) {
123       LOG_OOM();
124     } else {
125       bool platformEnable = enable && mPassiveLocationListenerNanoapps.empty();
126       bool platformDisable =
127           !enable && (mPassiveLocationListenerNanoapps.size() == 1);
128 
129       if (!passiveLocationListenerSupported) {
130         // Silently succeed per API, since listener capability will occur within
131         // CHRE (nanoapp requests).
132         success = true;
133       } else if (platformEnable || platformDisable) {
134         success = platformConfigurePassiveLocationListener(enable);
135       } else {
136         // Platform was already in the configured state.
137         success = true;
138       }
139 
140       if (success) {
141         if (enable) {
142           mPassiveLocationListenerNanoapps.push_back(instanceId);
143           nanoapp->registerForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
144         } else {
145           mPassiveLocationListenerNanoapps.erase(index);
146           if (!mLocationSession.nanoappHasRequest(instanceId)) {
147             nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
148           }
149         }
150       }
151     }
152   } else {  // else nanoapp request is already at the desired state.
153     success = true;
154   }
155 
156   return success;
157 }
158 
nanoappHasPassiveLocationListener(uint16_t nanoappInstanceId,size_t * index)159 bool GnssManager::nanoappHasPassiveLocationListener(uint16_t nanoappInstanceId,
160                                                     size_t *index) {
161   size_t foundIndex = mPassiveLocationListenerNanoapps.find(nanoappInstanceId);
162   bool found = (foundIndex != mPassiveLocationListenerNanoapps.size());
163   if (found && index != nullptr) {
164     *index = foundIndex;
165   }
166 
167   return found;
168 }
169 
platformConfigurePassiveLocationListener(bool enable)170 bool GnssManager::platformConfigurePassiveLocationListener(bool enable) {
171   bool success = mPlatformGnss.configurePassiveLocationListener(enable);
172   if (!success) {
173     LOGE("Platform failed to %s passive location listener",
174          enable ? "enable" : "disable");
175   } else {
176     mPlatformPassiveLocationListenerEnabled = enable;
177   }
178 
179   return success;
180 }
181 
handleRequestStateResyncCallbackSync()182 void GnssManager::handleRequestStateResyncCallbackSync() {
183   mLocationSession.handleRequestStateResyncCallbackSync();
184   mMeasurementSession.handleRequestStateResyncCallbackSync();
185 
186   mPlatformPassiveLocationListenerEnabled = false;
187   if (!mPassiveLocationListenerNanoapps.empty()) {
188     if (!platformConfigurePassiveLocationListener(true /* enable */)) {
189       // TODO(b/330789214): Change LOGE back to FATAL_ERROR once the odd
190       //                    peripheral behavior is resolved.
191       LOGE("Failed to resync passive location listener");
192     }
193   }
194 }
195 
logStateToBuffer(DebugDumpWrapper & debugDump) const196 void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
197   debugDump.print("\nGNSS:");
198   mLocationSession.logStateToBuffer(debugDump);
199   mMeasurementSession.logStateToBuffer(debugDump);
200 
201   debugDump.print("\n API error distribution (error-code indexed):\n");
202   debugDump.print("   GNSS Location:\n");
203   debugDump.logErrorHistogram(mLocationSession.mGnssErrorHistogram,
204                               ARRAY_SIZE(mLocationSession.mGnssErrorHistogram));
205   debugDump.print("   GNSS Measurement:\n");
206   debugDump.logErrorHistogram(
207       mMeasurementSession.mGnssErrorHistogram,
208       ARRAY_SIZE(mMeasurementSession.mGnssErrorHistogram));
209 
210   debugDump.print(
211       "\n Passive location listener %s\n",
212       mPlatformPassiveLocationListenerEnabled ? "enabled" : "disabled");
213   for (uint16_t instanceId : mPassiveLocationListenerNanoapps) {
214     debugDump.print("  nappId=%" PRIu16 "\n", instanceId);
215   }
216 }
217 
disableAllSubscriptions(Nanoapp * nanoapp)218 uint32_t GnssManager::disableAllSubscriptions(Nanoapp *nanoapp) {
219   uint32_t numDisabledSubscriptions = 0;
220   size_t index;
221 
222   if (mLocationSession.nanoappHasRequest(nanoapp)) {
223     numDisabledSubscriptions++;
224     mLocationSession.removeRequest(nanoapp, nullptr /*cookie*/);
225   }
226 
227   if (mMeasurementSession.nanoappHasRequest(nanoapp)) {
228     numDisabledSubscriptions++;
229     mMeasurementSession.removeRequest(nanoapp, nullptr /*cookie*/);
230   }
231 
232   if (nanoappHasPassiveLocationListener(nanoapp->getInstanceId(), &index)) {
233     numDisabledSubscriptions++;
234     configurePassiveLocationListener(nanoapp, false /*enable*/);
235   }
236 
237   return numDisabledSubscriptions;
238 }
239 
GnssSession(uint16_t reportEventType)240 GnssSession::GnssSession(uint16_t reportEventType)
241     : kReportEventType(reportEventType) {
242   switch (kReportEventType) {
243     case CHRE_EVENT_GNSS_LOCATION:
244       mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START;
245       mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP;
246       mName = "Location";
247       break;
248 
249     case CHRE_EVENT_GNSS_DATA:
250       mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START;
251       mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP;
252       mName = "Measurement";
253       break;
254 
255     default:
256       CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType);
257   }
258 
259   if (!mRequests.reserve(1)) {
260     FATAL_ERROR_OOM();
261   }
262 }
263 
addRequest(Nanoapp * nanoapp,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)264 bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
265                              Milliseconds minTimeToNext, const void *cookie) {
266   CHRE_ASSERT(nanoapp);
267   return configure(nanoapp, true /* enable */, minInterval, minTimeToNext,
268                    cookie);
269 }
270 
removeRequest(Nanoapp * nanoapp,const void * cookie)271 bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) {
272   CHRE_ASSERT(nanoapp);
273   return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX),
274                    Milliseconds(UINT64_MAX), cookie);
275 }
276 
handleStatusChange(bool enabled,uint8_t errorCode)277 void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) {
278   struct CallbackState {
279     bool enabled;
280     uint8_t errorCode;
281   };
282 
283   auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
284     auto *session = static_cast<GnssSession *>(data);
285     CallbackState cbState = NestedDataPtr<CallbackState>(extraData);
286     session->handleStatusChangeSync(cbState.enabled, cbState.errorCode);
287   };
288 
289   CallbackState cbState = {};
290   cbState.enabled = enabled;
291   cbState.errorCode = errorCode;
292   EventLoopManagerSingleton::get()->deferCallback(
293       SystemCallbackType::GnssSessionStatusChange, /*data=*/this, callback,
294       NestedDataPtr<CallbackState>(cbState));
295 }
296 
handleReportEvent(void * event)297 void GnssSession::handleReportEvent(void *event) {
298   if (mRequests.empty()) {
299     LOGW("Unexpected %s event", mName);
300   }
301 
302   auto callback = [](uint16_t type, void *data, void * /*extraData*/) {
303     uint16_t reportEventType = 0;
304     if (!getReportEventType(static_cast<SystemCallbackType>(type),
305                             &reportEventType) ||
306         !EventLoopManagerSingleton::get()
307              ->getSettingManager()
308              .getSettingEnabled(Setting::LOCATION)) {
309       freeReportEventCallback(reportEventType, data);
310     } else {
311       EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
312           reportEventType, data, freeReportEventCallback);
313     }
314   };
315 
316   SystemCallbackType type;
317   if (!getCallbackType(kReportEventType, &type) ||
318       !EventLoopManagerSingleton::get()->deferCallback(type, event, callback)) {
319     freeReportEventCallback(kReportEventType, event);
320   }
321 }
322 
onSettingChanged(Setting setting,bool)323 void GnssSession::onSettingChanged(Setting setting, bool /*enabled*/) {
324   if (setting == Setting::LOCATION) {
325     if (asyncResponsePending()) {
326       // A request is in progress, so we wait until the async response arrives
327       // to handle the state change.
328       mSettingChangePending = true;
329     } else {
330       mInternalRequestPending = updatePlatformRequest();
331       mSettingChangePending = false;
332     }
333   }
334 }
335 
updatePlatformRequest(bool forceUpdate)336 bool GnssSession::updatePlatformRequest(bool forceUpdate) {
337   bool enabled =
338       EventLoopManagerSingleton::get()->getSettingManager().getSettingEnabled(
339           Setting::LOCATION);
340 
341   bool desiredPlatformState = enabled && !mRequests.empty();
342   bool shouldUpdatePlatform =
343       forceUpdate ||
344       (desiredPlatformState != mPlatformEnabled) /* (enable/disable) */;
345 
346   bool requestPending = false;
347   if (shouldUpdatePlatform) {
348     if (controlPlatform(desiredPlatformState, mCurrentInterval,
349                         Milliseconds(0) /* minTimeToNext */)) {
350       LOGD("Configured GNSS %s: enable %d", mName, desiredPlatformState);
351       addSessionRequestLog(CHRE_INSTANCE_ID, mCurrentInterval,
352                            desiredPlatformState);
353       requestPending = true;
354     } else {
355       LOGE("Failed to configure GNSS %s: enable %d", mName,
356            desiredPlatformState);
357     }
358   }
359 
360   return requestPending;
361 }
362 
handleRequestStateResyncCallbackSync()363 void GnssSession::handleRequestStateResyncCallbackSync() {
364   if (asyncResponsePending()) {
365     // A request is in progress, so we wait until the async response arrives
366     // to handle the resync callback.
367     mResyncPending = true;
368   } else {
369     mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
370   }
371 }
372 
logStateToBuffer(DebugDumpWrapper & debugDump) const373 void GnssSession::logStateToBuffer(DebugDumpWrapper &debugDump) const {
374   // TODO: have all interval values print as INVALID if they are the max
375   // unsigned value
376   debugDump.print("\n %s: Curr int(ms)=%" PRIu64 "\n", mName,
377                   mCurrentInterval.getMilliseconds());
378   debugDump.print("  Requests:\n");
379   for (const auto &request : mRequests) {
380     debugDump.print("   minInt(ms)=%" PRIu64 " nappId=%" PRIu32 "\n",
381                     request.minInterval.getMilliseconds(),
382                     request.nanoappInstanceId);
383   }
384 
385   if (!mStateTransitions.empty()) {
386     debugDump.print("  Transition queue:\n");
387     for (const auto &transition : mStateTransitions) {
388       debugDump.print("   minInt(ms)=%" PRIu64 " enable=%d nappId=%" PRIu16
389                       "\n",
390                       transition.minInterval.getMilliseconds(),
391                       transition.enable, transition.nanoappInstanceId);
392     }
393   }
394 
395   debugDump.print("  Last %zu session requests:\n", mSessionRequestLogs.size());
396   static_assert(kNumSessionRequestLogs <= INT8_MAX,
397                 "kNumSessionRequestLogs must be less than INT8_MAX.");
398   for (int8_t i = static_cast<int8_t>(mSessionRequestLogs.size()) - 1; i >= 0;
399        i--) {
400     const auto &log = mSessionRequestLogs[static_cast<size_t>(i)];
401     debugDump.print("   ts=%" PRIu64 " nappId=%" PRIu16 " %s",
402                     log.timestamp.toRawNanoseconds(), log.instanceId,
403                     log.start ? "start" : "stop\n");
404     if (log.start) {
405       debugDump.print(" int(ms)=%" PRIu64 "\n", log.interval.getMilliseconds());
406     }
407   }
408 }
409 
configure(Nanoapp * nanoapp,bool enable,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)410 bool GnssSession::configure(Nanoapp *nanoapp, bool enable,
411                             Milliseconds minInterval,
412                             Milliseconds minTimeToNext, const void *cookie) {
413   bool success = false;
414   uint16_t instanceId = nanoapp->getInstanceId();
415   size_t requestIndex = 0;
416   bool hasRequest = nanoappHasRequest(instanceId, &requestIndex);
417 
418   if (asyncResponsePending()) {
419     success = addRequestToQueue(instanceId, enable, minInterval, cookie);
420   } else if (stateTransitionIsRequired(enable, minInterval, hasRequest,
421                                        requestIndex)) {
422     if (enable && !EventLoopManagerSingleton::get()
423                        ->getSettingManager()
424                        .getSettingEnabled(Setting::LOCATION)) {
425       // Treat as success but post async failure per API.
426       success = postAsyncResultEvent(instanceId, false /* success */, enable,
427                                      minInterval, CHRE_ERROR_FUNCTION_DISABLED,
428                                      cookie);
429     } else if (addRequestToQueue(instanceId, enable, minInterval, cookie)) {
430       success = controlPlatform(enable, minInterval, minTimeToNext);
431       if (!success) {
432         mStateTransitions.pop_back();
433         LOGE("Failed to request a GNSS session for nanoapp instance %" PRIu16
434              " enable %d",
435              instanceId, enable);
436       }
437     }
438   } else {
439     success = postAsyncResultEvent(instanceId, true /* success */, enable,
440                                    minInterval, CHRE_ERROR_NONE, cookie);
441   }
442 
443   if (success) {
444     addSessionRequestLog(nanoapp->getInstanceId(), minInterval, enable);
445   }
446 
447   return success;
448 }
449 
nanoappHasRequest(uint16_t instanceId,size_t * requestIndex) const450 bool GnssSession::nanoappHasRequest(uint16_t instanceId,
451                                     size_t *requestIndex) const {
452   bool hasRequest = false;
453   for (size_t i = 0; i < mRequests.size(); i++) {
454     if (mRequests[i].nanoappInstanceId == instanceId) {
455       hasRequest = true;
456       if (requestIndex != nullptr) {
457         *requestIndex = i;
458       }
459 
460       break;
461     }
462   }
463 
464   return hasRequest;
465 }
466 
nanoappHasRequest(Nanoapp * nanoapp) const467 bool GnssSession::nanoappHasRequest(Nanoapp *nanoapp) const {
468   return nanoappHasRequest(nanoapp->getInstanceId(), nullptr /*requestIndex*/);
469 }
470 
addRequestToQueue(uint16_t instanceId,bool enable,Milliseconds minInterval,const void * cookie)471 bool GnssSession::addRequestToQueue(uint16_t instanceId, bool enable,
472                                     Milliseconds minInterval,
473                                     const void *cookie) {
474   StateTransition stateTransition;
475   stateTransition.nanoappInstanceId = instanceId;
476   stateTransition.enable = enable;
477   stateTransition.minInterval = minInterval;
478   stateTransition.cookie = cookie;
479 
480   bool success = mStateTransitions.push(stateTransition);
481   if (!success) {
482     LOGW("Too many session state transitions");
483   }
484 
485   return success;
486 }
487 
isEnabled() const488 bool GnssSession::isEnabled() const {
489   return !mRequests.empty();
490 }
491 
stateTransitionIsRequired(bool requestedState,Milliseconds minInterval,bool nanoappHasRequest,size_t requestIndex) const492 bool GnssSession::stateTransitionIsRequired(bool requestedState,
493                                             Milliseconds minInterval,
494                                             bool nanoappHasRequest,
495                                             size_t requestIndex) const {
496   bool requestToEnable = (requestedState && !isEnabled());
497   bool requestToIncreaseRate =
498       (requestedState && isEnabled() && minInterval < mCurrentInterval);
499   bool requestToDisable =
500       (!requestedState && nanoappHasRequest && mRequests.size() == 1);
501 
502   // An effective rate decrease for the session can only occur if the nanoapp
503   // has an existing request.
504   bool requestToDecreaseRate = false;
505   if (nanoappHasRequest) {
506     // The nanoapp has an existing request. Check that the request does not
507     // result in a rate decrease by checking if no other nanoapps have the
508     // same request, the nanoapp's existing request is not equal to the current
509     // requested interval and the new request is slower than the current
510     // requested rate.
511     size_t requestCount = 0;
512     const auto &currentRequest = mRequests[requestIndex];
513     for (size_t i = 0; i < mRequests.size(); i++) {
514       const Request &request = mRequests[i];
515       if (i != requestIndex &&
516           request.minInterval == currentRequest.minInterval) {
517         requestCount++;
518       }
519     }
520 
521     requestToDecreaseRate =
522         (minInterval > mCurrentInterval &&
523          currentRequest.minInterval == mCurrentInterval && requestCount == 0);
524   }
525 
526   return (requestToEnable || requestToDisable || requestToIncreaseRate ||
527           requestToDecreaseRate);
528 }
529 
updateRequests(bool enable,Milliseconds minInterval,uint16_t instanceId)530 bool GnssSession::updateRequests(bool enable, Milliseconds minInterval,
531                                  uint16_t instanceId) {
532   bool success = true;
533   Nanoapp *nanoapp =
534       EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
535           instanceId);
536   if (nanoapp == nullptr) {
537     LOGW("Failed to update GNSS session request list for non-existent nanoapp");
538   } else {
539     size_t requestIndex;
540     bool hasExistingRequest = nanoappHasRequest(instanceId, &requestIndex);
541     if (enable) {
542       if (hasExistingRequest) {
543         // If the nanoapp has an open request ensure that the minInterval is
544         // kept up to date.
545         mRequests[requestIndex].minInterval = minInterval;
546       } else {
547         // The GNSS session was successfully enabled for this nanoapp and
548         // there is no existing request. Add it to the list of GNSS session
549         // nanoapps.
550         Request request;
551         request.nanoappInstanceId = instanceId;
552         request.minInterval = minInterval;
553         success = mRequests.push_back(request);
554         if (!success) {
555           LOG_OOM();
556         } else {
557           nanoapp->registerForBroadcastEvent(kReportEventType);
558         }
559       }
560     } else if (hasExistingRequest) {
561       // The session was successfully disabled for a previously enabled
562       // nanoapp. Remove it from the list of requests.
563       mRequests.erase(requestIndex);
564 
565       // We can only unregister the location events from nanoapps if it has no
566       // request and has not configured the passive listener.
567       if ((kReportEventType != CHRE_EVENT_GNSS_LOCATION) ||
568           !EventLoopManagerSingleton::get()
569                ->getGnssManager()
570                .nanoappHasPassiveLocationListener(instanceId)) {
571         nanoapp->unregisterForBroadcastEvent(kReportEventType);
572       }
573     }  // else disabling an inactive request, treat as success per CHRE API
574   }
575 
576   return success;
577 }
578 
postAsyncResultEvent(uint16_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)579 bool GnssSession::postAsyncResultEvent(uint16_t instanceId, bool success,
580                                        bool enable, Milliseconds minInterval,
581                                        uint8_t errorCode, const void *cookie) {
582   bool eventPosted = false;
583   if (!success || updateRequests(enable, minInterval, instanceId)) {
584     chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
585     if (event == nullptr) {
586       LOG_OOM();
587     } else {
588       event->requestType = enable ? mStartRequestType : mStopRequestType;
589       event->success = success;
590       event->errorCode = errorCode;
591       event->reserved = 0;
592       event->cookie = cookie;
593 
594       if (errorCode < CHRE_ERROR_SIZE) {
595         mGnssErrorHistogram[errorCode]++;
596       } else {
597         LOGE("Undefined error in gnssAsyncResult: %" PRIu8, errorCode);
598       }
599 
600       EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
601           CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback,
602           instanceId);
603       eventPosted = true;
604     }
605   }
606 
607   return eventPosted;
608 }
609 
postAsyncResultEventFatal(uint16_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)610 void GnssSession::postAsyncResultEventFatal(uint16_t instanceId, bool success,
611                                             bool enable,
612                                             Milliseconds minInterval,
613                                             uint8_t errorCode,
614                                             const void *cookie) {
615   if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode,
616                             cookie)) {
617     FATAL_ERROR("Failed to send GNSS session request async result event");
618   }
619 }
620 
handleStatusChangeSync(bool enabled,uint8_t errorCode)621 void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) {
622   bool success = (errorCode == CHRE_ERROR_NONE);
623 
624   if (mInternalRequestPending) {
625     // Silently handle internal requests from CHRE, since they are not pushed
626     // to the mStateTransitions queue.
627     mInternalRequestPending = false;
628   } else if (!mStateTransitions.empty()) {
629     const auto &stateTransition = mStateTransitions.front();
630 
631     if (success) {
632       mCurrentInterval = stateTransition.minInterval;
633     }
634 
635     if (success && stateTransition.enable != enabled) {
636       success = false;
637       errorCode = CHRE_ERROR;
638       LOGE("GNSS PAL did not transition to expected state");
639     }
640     postAsyncResultEventFatal(
641         stateTransition.nanoappInstanceId, success, stateTransition.enable,
642         stateTransition.minInterval, errorCode, stateTransition.cookie);
643     mStateTransitions.pop();
644   } else {
645     // TODO(b/296222493): change this back to an assert once issue resolved
646     LOGE("GnssSession::handleStatusChangeSync called with no transitions");
647     return;
648   }
649 
650   // If a previous setting change or resync event is pending process, do that
651   // first.
652   if (mResyncPending && !success) {
653     // We only send a platform request on resync if a pending request failed,
654     // because we still need to restore the previous request state.
655     mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
656   } else if (mSettingChangePending) {
657     mInternalRequestPending = updatePlatformRequest();
658   }
659 
660   mResyncPending = false;
661   mSettingChangePending = false;
662 
663   // If we didn't issue an internally-generated update via
664   // updatePlatformRequest(), process pending nanoapp requests (otherwise,
665   // wait for it to finish, then process any pending requests)
666   if (!mInternalRequestPending) {
667     dispatchQueuedStateTransitions();
668   }
669 }
670 
freeReportEventCallback(uint16_t eventType,void * eventData)671 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
672   switch (eventType) {
673     case CHRE_EVENT_GNSS_LOCATION:
674       EventLoopManagerSingleton::get()
675           ->getGnssManager()
676           .mPlatformGnss.releaseLocationEvent(
677               static_cast<chreGnssLocationEvent *>(eventData));
678       break;
679 
680     case CHRE_EVENT_GNSS_DATA:
681       EventLoopManagerSingleton::get()
682           ->getGnssManager()
683           .mPlatformGnss.releaseMeasurementDataEvent(
684               static_cast<chreGnssDataEvent *>(eventData));
685       break;
686 
687     default:
688       CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
689   }
690 }
691 
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)692 bool GnssSession::controlPlatform(bool enable, Milliseconds minInterval,
693                                   Milliseconds /* minTimeToNext */) {
694   bool success = false;
695 
696   switch (kReportEventType) {
697     case CHRE_EVENT_GNSS_LOCATION:
698       // TODO: Provide support for min time to next report. It is currently sent
699       // to the platform as zero.
700       success = EventLoopManagerSingleton::get()
701                     ->getGnssManager()
702                     .mPlatformGnss.controlLocationSession(enable, minInterval,
703                                                           Milliseconds(0));
704       break;
705 
706     case CHRE_EVENT_GNSS_DATA:
707       success =
708           EventLoopManagerSingleton::get()
709               ->getGnssManager()
710               .mPlatformGnss.controlMeasurementSession(enable, minInterval);
711       break;
712 
713     default:
714       CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, kReportEventType);
715   }
716 
717   if (success) {
718     mPlatformEnabled = enable;
719   }
720 
721   return success;
722 }
723 
addSessionRequestLog(uint16_t nanoappInstanceId,Milliseconds interval,bool start)724 void GnssSession::addSessionRequestLog(uint16_t nanoappInstanceId,
725                                        Milliseconds interval, bool start) {
726   mSessionRequestLogs.kick_push(SessionRequestLog(
727       SystemTime::getMonotonicTime(), nanoappInstanceId, interval, start));
728 }
729 
dispatchQueuedStateTransitions()730 void GnssSession::dispatchQueuedStateTransitions() {
731   while (!mStateTransitions.empty()) {
732     const auto &stateTransition = mStateTransitions.front();
733 
734     size_t requestIndex = 0;
735     bool hasRequest =
736         nanoappHasRequest(stateTransition.nanoappInstanceId, &requestIndex);
737 
738     if (stateTransitionIsRequired(stateTransition.enable,
739                                   stateTransition.minInterval, hasRequest,
740                                   requestIndex)) {
741       if (!EventLoopManagerSingleton::get()
742                ->getSettingManager()
743                .getSettingEnabled(Setting::LOCATION)) {
744         postAsyncResultEventFatal(
745             stateTransition.nanoappInstanceId, false /* success */,
746             stateTransition.enable, stateTransition.minInterval,
747             CHRE_ERROR_FUNCTION_DISABLED, stateTransition.cookie);
748         mStateTransitions.pop();
749       } else if (controlPlatform(stateTransition.enable,
750                                  stateTransition.minInterval,
751                                  Milliseconds(0))) {
752         break;
753       } else {
754         LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu16,
755              stateTransition.nanoappInstanceId);
756         postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
757                                   false /* success */, stateTransition.enable,
758                                   stateTransition.minInterval, CHRE_ERROR,
759                                   stateTransition.cookie);
760         mStateTransitions.pop();
761       }
762     } else {
763       postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
764                                 true /* success */, stateTransition.enable,
765                                 stateTransition.minInterval, CHRE_ERROR_NONE,
766                                 stateTransition.cookie);
767       mStateTransitions.pop();
768     }
769   }
770 }
771 
772 }  // namespace chre
773 
774 #endif  // CHRE_GNSS_SUPPORT_ENABLED
775