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