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