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