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 #ifdef CHRE_GNSS_SUPPORT_ENABLED
18
19 #include "chre/core/gnss_manager.h"
20
21 #include <cstddef>
22
23 #include "chre/core/event_loop_manager.h"
24 #include "chre/core/settings.h"
25 #include "chre/platform/assert.h"
26 #include "chre/platform/fatal_error.h"
27 #include "chre/util/nested_data_ptr.h"
28 #include "chre/util/system/debug_dump.h"
29 #include "chre/util/system/event_callbacks.h"
30
31 namespace chre {
32
33 namespace {
34
getCallbackType(uint16_t eventType,SystemCallbackType * callbackType)35 bool getCallbackType(uint16_t eventType, SystemCallbackType *callbackType) {
36 bool success = true;
37 switch (eventType) {
38 case CHRE_EVENT_GNSS_LOCATION: {
39 *callbackType = SystemCallbackType::GnssLocationReportEvent;
40 break;
41 }
42 case CHRE_EVENT_GNSS_DATA: {
43 *callbackType = SystemCallbackType::GnssMeasurementReportEvent;
44 break;
45 }
46 default: {
47 LOGE("Unknown event type %" PRIu16, eventType);
48 success = false;
49 }
50 }
51
52 return success;
53 }
54
getReportEventType(SystemCallbackType callbackType,uint16_t * eventType)55 bool getReportEventType(SystemCallbackType callbackType, uint16_t *eventType) {
56 bool success = true;
57 switch (callbackType) {
58 case SystemCallbackType::GnssLocationReportEvent: {
59 *eventType = CHRE_EVENT_GNSS_LOCATION;
60 break;
61 }
62 case SystemCallbackType::GnssMeasurementReportEvent: {
63 *eventType = CHRE_EVENT_GNSS_DATA;
64 break;
65 }
66 default: {
67 LOGE("Unknown callback type %" PRIu16,
68 static_cast<uint16_t>(callbackType));
69 success = false;
70 }
71 }
72
73 return success;
74 }
75
76 } // anonymous namespace
77
GnssManager()78 GnssManager::GnssManager()
79 : mLocationSession(CHRE_EVENT_GNSS_LOCATION),
80 mMeasurementSession(CHRE_EVENT_GNSS_DATA) {}
81
init()82 void GnssManager::init() {
83 mPlatformGnss.init();
84 }
85
getCapabilities()86 uint32_t GnssManager::getCapabilities() {
87 return mPlatformGnss.getCapabilities();
88 }
89
onSettingChanged(Setting setting,bool enabled)90 void GnssManager::onSettingChanged(Setting setting, bool enabled) {
91 mLocationSession.onSettingChanged(setting, enabled);
92 mMeasurementSession.onSettingChanged(setting, enabled);
93 }
94
handleRequestStateResyncCallback()95 void GnssManager::handleRequestStateResyncCallback() {
96 auto callback = [](uint16_t /* eventType */, void * /* eventData */,
97 void * /* extraData */) {
98 EventLoopManagerSingleton::get()
99 ->getGnssManager()
100 .handleRequestStateResyncCallbackSync();
101 };
102 EventLoopManagerSingleton::get()->deferCallback(
103 SystemCallbackType::GnssRequestResyncEvent, nullptr /* data */, callback);
104 }
105
configurePassiveLocationListener(Nanoapp * nanoapp,bool enable)106 bool GnssManager::configurePassiveLocationListener(Nanoapp *nanoapp,
107 bool enable) {
108 bool success = false;
109 uint16_t instanceId = nanoapp->getInstanceId();
110
111 size_t index;
112 if (nanoappHasPassiveLocationListener(instanceId, &index) != enable) {
113 uint32_t capabilities = getCapabilities();
114 bool locationSupported =
115 (capabilities & CHRE_GNSS_CAPABILITIES_LOCATION) != 0;
116 bool passiveLocationListenerSupported =
117 (capabilities &
118 CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER) != 0;
119
120 if (!locationSupported) {
121 LOGE("Platform does not have the location capability");
122 } else if (enable && !mPassiveLocationListenerNanoapps.prepareForPush()) {
123 LOG_OOM();
124 } else {
125 bool platformEnable = enable && mPassiveLocationListenerNanoapps.empty();
126 bool platformDisable =
127 !enable && (mPassiveLocationListenerNanoapps.size() == 1);
128
129 if (!passiveLocationListenerSupported) {
130 // Silently succeed per API, since listener capability will occur within
131 // CHRE (nanoapp requests).
132 success = true;
133 } else if (platformEnable || platformDisable) {
134 success = platformConfigurePassiveLocationListener(enable);
135 } else {
136 // Platform was already in the configured state.
137 success = true;
138 }
139
140 if (success) {
141 if (enable) {
142 mPassiveLocationListenerNanoapps.push_back(instanceId);
143 nanoapp->registerForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
144 } else {
145 mPassiveLocationListenerNanoapps.erase(index);
146 if (!mLocationSession.nanoappHasRequest(instanceId)) {
147 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION);
148 }
149 }
150 }
151 }
152 } else { // else nanoapp request is already at the desired state.
153 success = true;
154 }
155
156 return success;
157 }
158
nanoappHasPassiveLocationListener(uint16_t nanoappInstanceId,size_t * index)159 bool GnssManager::nanoappHasPassiveLocationListener(uint16_t nanoappInstanceId,
160 size_t *index) {
161 size_t foundIndex = mPassiveLocationListenerNanoapps.find(nanoappInstanceId);
162 bool found = (foundIndex != mPassiveLocationListenerNanoapps.size());
163 if (found && index != nullptr) {
164 *index = foundIndex;
165 }
166
167 return found;
168 }
169
platformConfigurePassiveLocationListener(bool enable)170 bool GnssManager::platformConfigurePassiveLocationListener(bool enable) {
171 bool success = mPlatformGnss.configurePassiveLocationListener(enable);
172 if (!success) {
173 LOGE("Platform failed to %s passive location listener",
174 enable ? "enable" : "disable");
175 } else {
176 mPlatformPassiveLocationListenerEnabled = enable;
177 }
178
179 return success;
180 }
181
handleRequestStateResyncCallbackSync()182 void GnssManager::handleRequestStateResyncCallbackSync() {
183 mLocationSession.handleRequestStateResyncCallbackSync();
184 mMeasurementSession.handleRequestStateResyncCallbackSync();
185
186 mPlatformPassiveLocationListenerEnabled = false;
187 if (!mPassiveLocationListenerNanoapps.empty()) {
188 if (!platformConfigurePassiveLocationListener(true /* enable */)) {
189 // TODO(b/330789214): Change LOGE back to FATAL_ERROR once the odd
190 // peripheral behavior is resolved.
191 LOGE("Failed to resync passive location listener");
192 }
193 }
194 }
195
logStateToBuffer(DebugDumpWrapper & debugDump) const196 void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
197 debugDump.print("\nGNSS:");
198 mLocationSession.logStateToBuffer(debugDump);
199 mMeasurementSession.logStateToBuffer(debugDump);
200
201 debugDump.print("\n API error distribution (error-code indexed):\n");
202 debugDump.print(" GNSS Location:\n");
203 debugDump.logErrorHistogram(mLocationSession.mGnssErrorHistogram,
204 ARRAY_SIZE(mLocationSession.mGnssErrorHistogram));
205 debugDump.print(" GNSS Measurement:\n");
206 debugDump.logErrorHistogram(
207 mMeasurementSession.mGnssErrorHistogram,
208 ARRAY_SIZE(mMeasurementSession.mGnssErrorHistogram));
209
210 debugDump.print(
211 "\n Passive location listener %s\n",
212 mPlatformPassiveLocationListenerEnabled ? "enabled" : "disabled");
213 for (uint16_t instanceId : mPassiveLocationListenerNanoapps) {
214 debugDump.print(" nappId=%" PRIu16 "\n", instanceId);
215 }
216 }
217
disableAllSubscriptions(Nanoapp * nanoapp)218 uint32_t GnssManager::disableAllSubscriptions(Nanoapp *nanoapp) {
219 uint32_t numDisabledSubscriptions = 0;
220 size_t index;
221
222 if (mLocationSession.nanoappHasRequest(nanoapp)) {
223 numDisabledSubscriptions++;
224 mLocationSession.removeRequest(nanoapp, nullptr /*cookie*/);
225 }
226
227 if (mMeasurementSession.nanoappHasRequest(nanoapp)) {
228 numDisabledSubscriptions++;
229 mMeasurementSession.removeRequest(nanoapp, nullptr /*cookie*/);
230 }
231
232 if (nanoappHasPassiveLocationListener(nanoapp->getInstanceId(), &index)) {
233 numDisabledSubscriptions++;
234 configurePassiveLocationListener(nanoapp, false /*enable*/);
235 }
236
237 return numDisabledSubscriptions;
238 }
239
GnssSession(uint16_t reportEventType)240 GnssSession::GnssSession(uint16_t reportEventType)
241 : kReportEventType(reportEventType) {
242 switch (kReportEventType) {
243 case CHRE_EVENT_GNSS_LOCATION:
244 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START;
245 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP;
246 mName = "Location";
247 break;
248
249 case CHRE_EVENT_GNSS_DATA:
250 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START;
251 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP;
252 mName = "Measurement";
253 break;
254
255 default:
256 CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType);
257 }
258
259 if (!mRequests.reserve(1)) {
260 FATAL_ERROR_OOM();
261 }
262 }
263
addRequest(Nanoapp * nanoapp,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)264 bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
265 Milliseconds minTimeToNext, const void *cookie) {
266 CHRE_ASSERT(nanoapp);
267 return configure(nanoapp, true /* enable */, minInterval, minTimeToNext,
268 cookie);
269 }
270
removeRequest(Nanoapp * nanoapp,const void * cookie)271 bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) {
272 CHRE_ASSERT(nanoapp);
273 return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX),
274 Milliseconds(UINT64_MAX), cookie);
275 }
276
handleStatusChange(bool enabled,uint8_t errorCode)277 void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) {
278 struct CallbackState {
279 bool enabled;
280 uint8_t errorCode;
281 };
282
283 auto callback = [](uint16_t /*type*/, void *data, void *extraData) {
284 auto *session = static_cast<GnssSession *>(data);
285 CallbackState cbState = NestedDataPtr<CallbackState>(extraData);
286 session->handleStatusChangeSync(cbState.enabled, cbState.errorCode);
287 };
288
289 CallbackState cbState = {};
290 cbState.enabled = enabled;
291 cbState.errorCode = errorCode;
292 EventLoopManagerSingleton::get()->deferCallback(
293 SystemCallbackType::GnssSessionStatusChange, /*data=*/this, callback,
294 NestedDataPtr<CallbackState>(cbState));
295 }
296
handleReportEvent(void * event)297 void GnssSession::handleReportEvent(void *event) {
298 if (mRequests.empty()) {
299 LOGW("Unexpected %s event", mName);
300 }
301
302 auto callback = [](uint16_t type, void *data, void * /*extraData*/) {
303 uint16_t reportEventType = 0;
304 if (!getReportEventType(static_cast<SystemCallbackType>(type),
305 &reportEventType) ||
306 !EventLoopManagerSingleton::get()
307 ->getSettingManager()
308 .getSettingEnabled(Setting::LOCATION)) {
309 freeReportEventCallback(reportEventType, data);
310 } else {
311 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
312 reportEventType, data, freeReportEventCallback);
313 }
314 };
315
316 SystemCallbackType type;
317 if (!getCallbackType(kReportEventType, &type) ||
318 !EventLoopManagerSingleton::get()->deferCallback(type, event, callback)) {
319 freeReportEventCallback(kReportEventType, event);
320 }
321 }
322
onSettingChanged(Setting setting,bool)323 void GnssSession::onSettingChanged(Setting setting, bool /*enabled*/) {
324 if (setting == Setting::LOCATION) {
325 if (asyncResponsePending()) {
326 // A request is in progress, so we wait until the async response arrives
327 // to handle the state change.
328 mSettingChangePending = true;
329 } else {
330 mInternalRequestPending = updatePlatformRequest();
331 mSettingChangePending = false;
332 }
333 }
334 }
335
updatePlatformRequest(bool forceUpdate)336 bool GnssSession::updatePlatformRequest(bool forceUpdate) {
337 bool enabled =
338 EventLoopManagerSingleton::get()->getSettingManager().getSettingEnabled(
339 Setting::LOCATION);
340
341 bool desiredPlatformState = enabled && !mRequests.empty();
342 bool shouldUpdatePlatform =
343 forceUpdate ||
344 (desiredPlatformState != mPlatformEnabled) /* (enable/disable) */;
345
346 bool requestPending = false;
347 if (shouldUpdatePlatform) {
348 if (controlPlatform(desiredPlatformState, mCurrentInterval,
349 Milliseconds(0) /* minTimeToNext */)) {
350 LOGD("Configured GNSS %s: enable %d", mName, desiredPlatformState);
351 addSessionRequestLog(CHRE_INSTANCE_ID, mCurrentInterval,
352 desiredPlatformState);
353 requestPending = true;
354 } else {
355 LOGE("Failed to configure GNSS %s: enable %d", mName,
356 desiredPlatformState);
357 }
358 }
359
360 return requestPending;
361 }
362
handleRequestStateResyncCallbackSync()363 void GnssSession::handleRequestStateResyncCallbackSync() {
364 if (asyncResponsePending()) {
365 // A request is in progress, so we wait until the async response arrives
366 // to handle the resync callback.
367 mResyncPending = true;
368 } else {
369 mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
370 }
371 }
372
logStateToBuffer(DebugDumpWrapper & debugDump) const373 void GnssSession::logStateToBuffer(DebugDumpWrapper &debugDump) const {
374 // TODO: have all interval values print as INVALID if they are the max
375 // unsigned value
376 debugDump.print("\n %s: Curr int(ms)=%" PRIu64 "\n", mName,
377 mCurrentInterval.getMilliseconds());
378 debugDump.print(" Requests:\n");
379 for (const auto &request : mRequests) {
380 debugDump.print(" minInt(ms)=%" PRIu64 " nappId=%" PRIu32 "\n",
381 request.minInterval.getMilliseconds(),
382 request.nanoappInstanceId);
383 }
384
385 if (!mStateTransitions.empty()) {
386 debugDump.print(" Transition queue:\n");
387 for (const auto &transition : mStateTransitions) {
388 debugDump.print(" minInt(ms)=%" PRIu64 " enable=%d nappId=%" PRIu16
389 "\n",
390 transition.minInterval.getMilliseconds(),
391 transition.enable, transition.nanoappInstanceId);
392 }
393 }
394
395 debugDump.print(" Last %zu session requests:\n", mSessionRequestLogs.size());
396 static_assert(kNumSessionRequestLogs <= INT8_MAX,
397 "kNumSessionRequestLogs must be less than INT8_MAX.");
398 for (int8_t i = static_cast<int8_t>(mSessionRequestLogs.size()) - 1; i >= 0;
399 i--) {
400 const auto &log = mSessionRequestLogs[static_cast<size_t>(i)];
401 debugDump.print(" ts=%" PRIu64 " nappId=%" PRIu16 " %s",
402 log.timestamp.toRawNanoseconds(), log.instanceId,
403 log.start ? "start" : "stop\n");
404 if (log.start) {
405 debugDump.print(" int(ms)=%" PRIu64 "\n", log.interval.getMilliseconds());
406 }
407 }
408 }
409
configure(Nanoapp * nanoapp,bool enable,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)410 bool GnssSession::configure(Nanoapp *nanoapp, bool enable,
411 Milliseconds minInterval,
412 Milliseconds minTimeToNext, const void *cookie) {
413 bool success = false;
414 uint16_t instanceId = nanoapp->getInstanceId();
415 size_t requestIndex = 0;
416 bool hasRequest = nanoappHasRequest(instanceId, &requestIndex);
417
418 if (asyncResponsePending()) {
419 success = addRequestToQueue(instanceId, enable, minInterval, cookie);
420 } else if (stateTransitionIsRequired(enable, minInterval, hasRequest,
421 requestIndex)) {
422 if (enable && !EventLoopManagerSingleton::get()
423 ->getSettingManager()
424 .getSettingEnabled(Setting::LOCATION)) {
425 // Treat as success but post async failure per API.
426 success = postAsyncResultEvent(instanceId, false /* success */, enable,
427 minInterval, CHRE_ERROR_FUNCTION_DISABLED,
428 cookie);
429 } else if (addRequestToQueue(instanceId, enable, minInterval, cookie)) {
430 success = controlPlatform(enable, minInterval, minTimeToNext);
431 if (!success) {
432 mStateTransitions.pop_back();
433 LOGE("Failed to request a GNSS session for nanoapp instance %" PRIu16
434 " enable %d",
435 instanceId, enable);
436 }
437 }
438 } else {
439 success = postAsyncResultEvent(instanceId, true /* success */, enable,
440 minInterval, CHRE_ERROR_NONE, cookie);
441 }
442
443 if (success) {
444 addSessionRequestLog(nanoapp->getInstanceId(), minInterval, enable);
445 }
446
447 return success;
448 }
449
nanoappHasRequest(uint16_t instanceId,size_t * requestIndex) const450 bool GnssSession::nanoappHasRequest(uint16_t instanceId,
451 size_t *requestIndex) const {
452 bool hasRequest = false;
453 for (size_t i = 0; i < mRequests.size(); i++) {
454 if (mRequests[i].nanoappInstanceId == instanceId) {
455 hasRequest = true;
456 if (requestIndex != nullptr) {
457 *requestIndex = i;
458 }
459
460 break;
461 }
462 }
463
464 return hasRequest;
465 }
466
nanoappHasRequest(Nanoapp * nanoapp) const467 bool GnssSession::nanoappHasRequest(Nanoapp *nanoapp) const {
468 return nanoappHasRequest(nanoapp->getInstanceId(), nullptr /*requestIndex*/);
469 }
470
addRequestToQueue(uint16_t instanceId,bool enable,Milliseconds minInterval,const void * cookie)471 bool GnssSession::addRequestToQueue(uint16_t instanceId, bool enable,
472 Milliseconds minInterval,
473 const void *cookie) {
474 StateTransition stateTransition;
475 stateTransition.nanoappInstanceId = instanceId;
476 stateTransition.enable = enable;
477 stateTransition.minInterval = minInterval;
478 stateTransition.cookie = cookie;
479
480 bool success = mStateTransitions.push(stateTransition);
481 if (!success) {
482 LOGW("Too many session state transitions");
483 }
484
485 return success;
486 }
487
isEnabled() const488 bool GnssSession::isEnabled() const {
489 return !mRequests.empty();
490 }
491
stateTransitionIsRequired(bool requestedState,Milliseconds minInterval,bool nanoappHasRequest,size_t requestIndex) const492 bool GnssSession::stateTransitionIsRequired(bool requestedState,
493 Milliseconds minInterval,
494 bool nanoappHasRequest,
495 size_t requestIndex) const {
496 bool requestToEnable = (requestedState && !isEnabled());
497 bool requestToIncreaseRate =
498 (requestedState && isEnabled() && minInterval < mCurrentInterval);
499 bool requestToDisable =
500 (!requestedState && nanoappHasRequest && mRequests.size() == 1);
501
502 // An effective rate decrease for the session can only occur if the nanoapp
503 // has an existing request.
504 bool requestToDecreaseRate = false;
505 if (nanoappHasRequest) {
506 // The nanoapp has an existing request. Check that the request does not
507 // result in a rate decrease by checking if no other nanoapps have the
508 // same request, the nanoapp's existing request is not equal to the current
509 // requested interval and the new request is slower than the current
510 // requested rate.
511 size_t requestCount = 0;
512 const auto ¤tRequest = mRequests[requestIndex];
513 for (size_t i = 0; i < mRequests.size(); i++) {
514 const Request &request = mRequests[i];
515 if (i != requestIndex &&
516 request.minInterval == currentRequest.minInterval) {
517 requestCount++;
518 }
519 }
520
521 requestToDecreaseRate =
522 (minInterval > mCurrentInterval &&
523 currentRequest.minInterval == mCurrentInterval && requestCount == 0);
524 }
525
526 return (requestToEnable || requestToDisable || requestToIncreaseRate ||
527 requestToDecreaseRate);
528 }
529
updateRequests(bool enable,Milliseconds minInterval,uint16_t instanceId)530 bool GnssSession::updateRequests(bool enable, Milliseconds minInterval,
531 uint16_t instanceId) {
532 bool success = true;
533 Nanoapp *nanoapp =
534 EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
535 instanceId);
536 if (nanoapp == nullptr) {
537 LOGW("Failed to update GNSS session request list for non-existent nanoapp");
538 } else {
539 size_t requestIndex;
540 bool hasExistingRequest = nanoappHasRequest(instanceId, &requestIndex);
541 if (enable) {
542 if (hasExistingRequest) {
543 // If the nanoapp has an open request ensure that the minInterval is
544 // kept up to date.
545 mRequests[requestIndex].minInterval = minInterval;
546 } else {
547 // The GNSS session was successfully enabled for this nanoapp and
548 // there is no existing request. Add it to the list of GNSS session
549 // nanoapps.
550 Request request;
551 request.nanoappInstanceId = instanceId;
552 request.minInterval = minInterval;
553 success = mRequests.push_back(request);
554 if (!success) {
555 LOG_OOM();
556 } else {
557 nanoapp->registerForBroadcastEvent(kReportEventType);
558 }
559 }
560 } else if (hasExistingRequest) {
561 // The session was successfully disabled for a previously enabled
562 // nanoapp. Remove it from the list of requests.
563 mRequests.erase(requestIndex);
564
565 // We can only unregister the location events from nanoapps if it has no
566 // request and has not configured the passive listener.
567 if ((kReportEventType != CHRE_EVENT_GNSS_LOCATION) ||
568 !EventLoopManagerSingleton::get()
569 ->getGnssManager()
570 .nanoappHasPassiveLocationListener(instanceId)) {
571 nanoapp->unregisterForBroadcastEvent(kReportEventType);
572 }
573 } // else disabling an inactive request, treat as success per CHRE API
574 }
575
576 return success;
577 }
578
postAsyncResultEvent(uint16_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)579 bool GnssSession::postAsyncResultEvent(uint16_t instanceId, bool success,
580 bool enable, Milliseconds minInterval,
581 uint8_t errorCode, const void *cookie) {
582 bool eventPosted = false;
583 if (!success || updateRequests(enable, minInterval, instanceId)) {
584 chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
585 if (event == nullptr) {
586 LOG_OOM();
587 } else {
588 event->requestType = enable ? mStartRequestType : mStopRequestType;
589 event->success = success;
590 event->errorCode = errorCode;
591 event->reserved = 0;
592 event->cookie = cookie;
593
594 if (errorCode < CHRE_ERROR_SIZE) {
595 mGnssErrorHistogram[errorCode]++;
596 } else {
597 LOGE("Undefined error in gnssAsyncResult: %" PRIu8, errorCode);
598 }
599
600 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
601 CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback,
602 instanceId);
603 eventPosted = true;
604 }
605 }
606
607 return eventPosted;
608 }
609
postAsyncResultEventFatal(uint16_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)610 void GnssSession::postAsyncResultEventFatal(uint16_t instanceId, bool success,
611 bool enable,
612 Milliseconds minInterval,
613 uint8_t errorCode,
614 const void *cookie) {
615 if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode,
616 cookie)) {
617 FATAL_ERROR("Failed to send GNSS session request async result event");
618 }
619 }
620
handleStatusChangeSync(bool enabled,uint8_t errorCode)621 void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) {
622 bool success = (errorCode == CHRE_ERROR_NONE);
623
624 if (mInternalRequestPending) {
625 // Silently handle internal requests from CHRE, since they are not pushed
626 // to the mStateTransitions queue.
627 mInternalRequestPending = false;
628 } else if (!mStateTransitions.empty()) {
629 const auto &stateTransition = mStateTransitions.front();
630
631 if (success) {
632 mCurrentInterval = stateTransition.minInterval;
633 }
634
635 if (success && stateTransition.enable != enabled) {
636 success = false;
637 errorCode = CHRE_ERROR;
638 LOGE("GNSS PAL did not transition to expected state");
639 }
640 postAsyncResultEventFatal(
641 stateTransition.nanoappInstanceId, success, stateTransition.enable,
642 stateTransition.minInterval, errorCode, stateTransition.cookie);
643 mStateTransitions.pop();
644 } else {
645 // TODO(b/296222493): change this back to an assert once issue resolved
646 LOGE("GnssSession::handleStatusChangeSync called with no transitions");
647 return;
648 }
649
650 // If a previous setting change or resync event is pending process, do that
651 // first.
652 if (mResyncPending && !success) {
653 // We only send a platform request on resync if a pending request failed,
654 // because we still need to restore the previous request state.
655 mInternalRequestPending = updatePlatformRequest(true /* forceUpdate */);
656 } else if (mSettingChangePending) {
657 mInternalRequestPending = updatePlatformRequest();
658 }
659
660 mResyncPending = false;
661 mSettingChangePending = false;
662
663 // If we didn't issue an internally-generated update via
664 // updatePlatformRequest(), process pending nanoapp requests (otherwise,
665 // wait for it to finish, then process any pending requests)
666 if (!mInternalRequestPending) {
667 dispatchQueuedStateTransitions();
668 }
669 }
670
freeReportEventCallback(uint16_t eventType,void * eventData)671 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
672 switch (eventType) {
673 case CHRE_EVENT_GNSS_LOCATION:
674 EventLoopManagerSingleton::get()
675 ->getGnssManager()
676 .mPlatformGnss.releaseLocationEvent(
677 static_cast<chreGnssLocationEvent *>(eventData));
678 break;
679
680 case CHRE_EVENT_GNSS_DATA:
681 EventLoopManagerSingleton::get()
682 ->getGnssManager()
683 .mPlatformGnss.releaseMeasurementDataEvent(
684 static_cast<chreGnssDataEvent *>(eventData));
685 break;
686
687 default:
688 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
689 }
690 }
691
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)692 bool GnssSession::controlPlatform(bool enable, Milliseconds minInterval,
693 Milliseconds /* minTimeToNext */) {
694 bool success = false;
695
696 switch (kReportEventType) {
697 case CHRE_EVENT_GNSS_LOCATION:
698 // TODO: Provide support for min time to next report. It is currently sent
699 // to the platform as zero.
700 success = EventLoopManagerSingleton::get()
701 ->getGnssManager()
702 .mPlatformGnss.controlLocationSession(enable, minInterval,
703 Milliseconds(0));
704 break;
705
706 case CHRE_EVENT_GNSS_DATA:
707 success =
708 EventLoopManagerSingleton::get()
709 ->getGnssManager()
710 .mPlatformGnss.controlMeasurementSession(enable, minInterval);
711 break;
712
713 default:
714 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, kReportEventType);
715 }
716
717 if (success) {
718 mPlatformEnabled = enable;
719 }
720
721 return success;
722 }
723
addSessionRequestLog(uint16_t nanoappInstanceId,Milliseconds interval,bool start)724 void GnssSession::addSessionRequestLog(uint16_t nanoappInstanceId,
725 Milliseconds interval, bool start) {
726 mSessionRequestLogs.kick_push(SessionRequestLog(
727 SystemTime::getMonotonicTime(), nanoappInstanceId, interval, start));
728 }
729
dispatchQueuedStateTransitions()730 void GnssSession::dispatchQueuedStateTransitions() {
731 while (!mStateTransitions.empty()) {
732 const auto &stateTransition = mStateTransitions.front();
733
734 size_t requestIndex = 0;
735 bool hasRequest =
736 nanoappHasRequest(stateTransition.nanoappInstanceId, &requestIndex);
737
738 if (stateTransitionIsRequired(stateTransition.enable,
739 stateTransition.minInterval, hasRequest,
740 requestIndex)) {
741 if (!EventLoopManagerSingleton::get()
742 ->getSettingManager()
743 .getSettingEnabled(Setting::LOCATION)) {
744 postAsyncResultEventFatal(
745 stateTransition.nanoappInstanceId, false /* success */,
746 stateTransition.enable, stateTransition.minInterval,
747 CHRE_ERROR_FUNCTION_DISABLED, stateTransition.cookie);
748 mStateTransitions.pop();
749 } else if (controlPlatform(stateTransition.enable,
750 stateTransition.minInterval,
751 Milliseconds(0))) {
752 break;
753 } else {
754 LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu16,
755 stateTransition.nanoappInstanceId);
756 postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
757 false /* success */, stateTransition.enable,
758 stateTransition.minInterval, CHRE_ERROR,
759 stateTransition.cookie);
760 mStateTransitions.pop();
761 }
762 } else {
763 postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
764 true /* success */, stateTransition.enable,
765 stateTransition.minInterval, CHRE_ERROR_NONE,
766 stateTransition.cookie);
767 mStateTransitions.pop();
768 }
769 }
770 }
771
772 } // namespace chre
773
774 #endif // CHRE_GNSS_SUPPORT_ENABLED
775