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