• 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       FATAL_ERROR("Failed to resync passive location listener");
188     }
189   }
190 }
191 
logStateToBuffer(DebugDumpWrapper & debugDump) const192 void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
193   debugDump.print("\nGNSS:");
194   mLocationSession.logStateToBuffer(debugDump);
195   mMeasurementSession.logStateToBuffer(debugDump);
196 
197   debugDump.print("\n API error distribution (error-code indexed):\n");
198   debugDump.print("   GNSS Location:\n");
199   debugDump.logErrorHistogram(mLocationSession.mGnssErrorHistogram,
200                               ARRAY_SIZE(mLocationSession.mGnssErrorHistogram));
201   debugDump.print("   GNSS Measurement:\n");
202   debugDump.logErrorHistogram(
203       mMeasurementSession.mGnssErrorHistogram,
204       ARRAY_SIZE(mMeasurementSession.mGnssErrorHistogram));
205 
206   debugDump.print(
207       "\n Passive location listener %s\n",
208       mPlatformPassiveLocationListenerEnabled ? "enabled" : "disabled");
209   for (uint16_t instanceId : mPassiveLocationListenerNanoapps) {
210     debugDump.print("  nappId=%" PRIu16 "\n", instanceId);
211   }
212 }
213 
disableAllSubscriptions(Nanoapp * nanoapp)214 uint32_t GnssManager::disableAllSubscriptions(Nanoapp *nanoapp) {
215   uint32_t numDisabledSubscriptions = 0;
216   size_t index;
217 
218   if (mLocationSession.nanoappHasRequest(nanoapp)) {
219     numDisabledSubscriptions++;
220     mLocationSession.removeRequest(nanoapp, nullptr /*cookie*/);
221   }
222 
223   if (mMeasurementSession.nanoappHasRequest(nanoapp)) {
224     numDisabledSubscriptions++;
225     mMeasurementSession.removeRequest(nanoapp, nullptr /*cookie*/);
226   }
227 
228   if (nanoappHasPassiveLocationListener(nanoapp->getInstanceId(), &index)) {
229     numDisabledSubscriptions++;
230     configurePassiveLocationListener(nanoapp, false /*enable*/);
231   }
232 
233   return numDisabledSubscriptions;
234 }
235 
GnssSession(uint16_t reportEventType)236 GnssSession::GnssSession(uint16_t reportEventType)
237     : kReportEventType(reportEventType) {
238   switch (kReportEventType) {
239     case CHRE_EVENT_GNSS_LOCATION:
240       mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START;
241       mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP;
242       mName = "Location";
243       break;
244 
245     case CHRE_EVENT_GNSS_DATA:
246       mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START;
247       mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP;
248       mName = "Measurement";
249       break;
250 
251     default:
252       CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType);
253   }
254 
255   if (!mRequests.reserve(1)) {
256     FATAL_ERROR_OOM();
257   }
258 }
259 
addRequest(Nanoapp * nanoapp,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)260 bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
261                              Milliseconds minTimeToNext, const void *cookie) {
262   CHRE_ASSERT(nanoapp);
263   return configure(nanoapp, true /* enable */, minInterval, minTimeToNext,
264                    cookie);
265 }
266 
removeRequest(Nanoapp * nanoapp,const void * cookie)267 bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) {
268   CHRE_ASSERT(nanoapp);
269   return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX),
270                    Milliseconds(UINT64_MAX), cookie);
271 }
272 
handleStatusChange(bool enabled,uint8_t errorCode)273 void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) {
274   struct CallbackState {
275     bool enabled;
276     uint8_t errorCode;
277   };
278 
279   auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
280     auto *session = static_cast<GnssSession *>(data);
281     CallbackState cbState = NestedDataPtr<CallbackState>(extraData);
282     session->handleStatusChangeSync(cbState.enabled, cbState.errorCode);
283   };
284 
285   CallbackState cbState = {};
286   cbState.enabled = enabled;
287   cbState.errorCode = errorCode;
288   EventLoopManagerSingleton::get()->deferCallback(
289       SystemCallbackType::GnssSessionStatusChange, /*data=*/this, callback,
290       NestedDataPtr<CallbackState>(cbState));
291 }
292 
handleReportEvent(void * event)293 void GnssSession::handleReportEvent(void *event) {
294   if (mRequests.empty()) {
295     LOGW("Unexpected %s event", mName);
296   }
297 
298   auto callback = [](uint16_t type, void *data, void * /*extraData*/) {
299     uint16_t reportEventType = 0;
300     if (!getReportEventType(static_cast<SystemCallbackType>(type),
301                             &reportEventType) ||
302         !EventLoopManagerSingleton::get()
303              ->getSettingManager()
304              .getSettingEnabled(Setting::LOCATION)) {
305       freeReportEventCallback(reportEventType, data);
306     } else {
307       EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
308           reportEventType, data, freeReportEventCallback);
309     }
310   };
311 
312   SystemCallbackType type;
313   if (!getCallbackType(kReportEventType, &type) ||
314       !EventLoopManagerSingleton::get()->deferCallback(type, event, callback)) {
315     freeReportEventCallback(kReportEventType, event);
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   if (mInternalRequestPending) {
617     // Silently handle internal requests from CHRE, since they are not pushed
618     // to the mStateTransitions queue.
619     mInternalRequestPending = false;
620   } else if (!mStateTransitions.empty()) {
621     const auto &stateTransition = mStateTransitions.front();
622 
623     if (success) {
624       mCurrentInterval = stateTransition.minInterval;
625     }
626 
627     if (success && stateTransition.enable != enabled) {
628       success = false;
629       errorCode = CHRE_ERROR;
630       LOGE("GNSS PAL did not transition to expected state");
631     }
632     postAsyncResultEventFatal(
633         stateTransition.nanoappInstanceId, success, stateTransition.enable,
634         stateTransition.minInterval, errorCode, stateTransition.cookie);
635     mStateTransitions.pop();
636   } else {
637     // TODO(b/296222493): change this back to an assert once issue resolved
638     LOGE("GnssSession::handleStatusChangeSync called with no transitions");
639     return;
640   }
641 
642   // If a previous setting change or resync event is pending process, do that
643   // first.
644   if (mResyncPending && !success) {
645     // We only send a platform request on resync if a pending request failed,
646     // because we still need to restore the previous request state.
647     mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
648   } else if (mSettingChangePending) {
649     mInternalRequestPending = updatePlatformRequest();
650   }
651 
652   mResyncPending = false;
653   mSettingChangePending = false;
654 
655   // If we didn't issue an internally-generated update via
656   // updatePlatformRequest(), process pending nanoapp requests (otherwise,
657   // wait for it to finish, then process any pending requests)
658   if (!mInternalRequestPending) {
659     dispatchQueuedStateTransitions();
660   }
661 }
662 
freeReportEventCallback(uint16_t eventType,void * eventData)663 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
664   switch (eventType) {
665     case CHRE_EVENT_GNSS_LOCATION:
666       EventLoopManagerSingleton::get()
667           ->getGnssManager()
668           .mPlatformGnss.releaseLocationEvent(
669               static_cast<chreGnssLocationEvent *>(eventData));
670       break;
671 
672     case CHRE_EVENT_GNSS_DATA:
673       EventLoopManagerSingleton::get()
674           ->getGnssManager()
675           .mPlatformGnss.releaseMeasurementDataEvent(
676               static_cast<chreGnssDataEvent *>(eventData));
677       break;
678 
679     default:
680       CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
681   }
682 }
683 
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)684 bool GnssSession::controlPlatform(bool enable, Milliseconds minInterval,
685                                   Milliseconds /* minTimeToNext */) {
686   bool success = false;
687 
688   switch (kReportEventType) {
689     case CHRE_EVENT_GNSS_LOCATION:
690       // TODO: Provide support for min time to next report. It is currently sent
691       // to the platform as zero.
692       success = EventLoopManagerSingleton::get()
693                     ->getGnssManager()
694                     .mPlatformGnss.controlLocationSession(enable, minInterval,
695                                                           Milliseconds(0));
696       break;
697 
698     case CHRE_EVENT_GNSS_DATA:
699       success =
700           EventLoopManagerSingleton::get()
701               ->getGnssManager()
702               .mPlatformGnss.controlMeasurementSession(enable, minInterval);
703       break;
704 
705     default:
706       CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, kReportEventType);
707   }
708 
709   if (success) {
710     mPlatformEnabled = enable;
711   }
712 
713   return success;
714 }
715 
addSessionRequestLog(uint16_t nanoappInstanceId,Milliseconds interval,bool start)716 void GnssSession::addSessionRequestLog(uint16_t nanoappInstanceId,
717                                        Milliseconds interval, bool start) {
718   mSessionRequestLogs.kick_push(SessionRequestLog(
719       SystemTime::getMonotonicTime(), nanoappInstanceId, interval, start));
720 }
721 
dispatchQueuedStateTransitions()722 void GnssSession::dispatchQueuedStateTransitions() {
723   while (!mStateTransitions.empty()) {
724     const auto &stateTransition = mStateTransitions.front();
725 
726     size_t requestIndex = 0;
727     bool hasRequest =
728         nanoappHasRequest(stateTransition.nanoappInstanceId, &requestIndex);
729 
730     if (stateTransitionIsRequired(stateTransition.enable,
731                                   stateTransition.minInterval, hasRequest,
732                                   requestIndex)) {
733       if (!EventLoopManagerSingleton::get()
734                ->getSettingManager()
735                .getSettingEnabled(Setting::LOCATION)) {
736         postAsyncResultEventFatal(
737             stateTransition.nanoappInstanceId, false /* success */,
738             stateTransition.enable, stateTransition.minInterval,
739             CHRE_ERROR_FUNCTION_DISABLED, stateTransition.cookie);
740         mStateTransitions.pop();
741       } else if (controlPlatform(stateTransition.enable,
742                                  stateTransition.minInterval,
743                                  Milliseconds(0))) {
744         break;
745       } else {
746         LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu16,
747              stateTransition.nanoappInstanceId);
748         postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
749                                   false /* success */, stateTransition.enable,
750                                   stateTransition.minInterval, CHRE_ERROR,
751                                   stateTransition.cookie);
752         mStateTransitions.pop();
753       }
754     } else {
755       postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
756                                 true /* success */, stateTransition.enable,
757                                 stateTransition.minInterval, CHRE_ERROR_NONE,
758                                 stateTransition.cookie);
759       mStateTransitions.pop();
760     }
761   }
762 }
763 
764 }  // namespace chre
765