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 ¤tRequest = 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