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