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