1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "chre/core/gnss_manager.h"
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/core/settings.h"
21 #include "chre/platform/assert.h"
22 #include "chre/platform/fatal_error.h"
23 #include "chre/util/system/debug_dump.h"
24
25 namespace chre {
26
27 namespace {
28
getCallbackType(uint16_t eventType,SystemCallbackType * callbackType)29 bool getCallbackType(uint16_t eventType, SystemCallbackType *callbackType) {
30 bool success = true;
31 switch (eventType) {
32 case CHRE_EVENT_GNSS_LOCATION: {
33 *callbackType = SystemCallbackType::GnssLocationReportEvent;
34 break;
35 }
36 case CHRE_EVENT_GNSS_DATA: {
37 *callbackType = SystemCallbackType::GnssMeasurementReportEvent;
38 break;
39 }
40 default: {
41 LOGE("Unknown event type %" PRIu16, eventType);
42 success = false;
43 }
44 }
45
46 return success;
47 }
48
getReportEventType(SystemCallbackType callbackType,uint16_t * eventType)49 bool getReportEventType(SystemCallbackType callbackType, uint16_t *eventType) {
50 bool success = true;
51 switch (callbackType) {
52 case SystemCallbackType::GnssLocationReportEvent: {
53 *eventType = CHRE_EVENT_GNSS_LOCATION;
54 break;
55 }
56 case SystemCallbackType::GnssMeasurementReportEvent: {
57 *eventType = CHRE_EVENT_GNSS_DATA;
58 break;
59 }
60 default: {
61 LOGE("Unknown callback type %" PRIu16,
62 static_cast<uint16_t>(callbackType));
63 success = false;
64 }
65 }
66
67 return success;
68 }
69
70 } // anonymous namespace
71
GnssManager()72 GnssManager::GnssManager()
73 : mLocationSession(CHRE_EVENT_GNSS_LOCATION),
74 mMeasurementSession(CHRE_EVENT_GNSS_DATA) {}
75
init()76 void GnssManager::init() {
77 mPlatformGnss.init();
78 }
79
getCapabilities()80 uint32_t GnssManager::getCapabilities() {
81 return mPlatformGnss.getCapabilities();
82 }
83
onSettingChanged(Setting setting,SettingState state)84 void GnssManager::onSettingChanged(Setting setting, SettingState state) {
85 mLocationSession.onSettingChanged(setting, state);
86 mMeasurementSession.onSettingChanged(setting, state);
87 }
88
logStateToBuffer(DebugDumpWrapper & debugDump) const89 void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const {
90 debugDump.print("\nGNSS:");
91 mLocationSession.logStateToBuffer(debugDump);
92 mMeasurementSession.logStateToBuffer(debugDump);
93 }
94
GnssSession(uint16_t reportEventType)95 GnssSession::GnssSession(uint16_t reportEventType)
96 : mReportEventType(reportEventType) {
97 switch (mReportEventType) {
98 case CHRE_EVENT_GNSS_LOCATION:
99 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START;
100 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP;
101 mName = "Location";
102 break;
103
104 case CHRE_EVENT_GNSS_DATA:
105 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START;
106 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP;
107 mName = "Measurement";
108 break;
109
110 default:
111 CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType);
112 }
113
114 if (!mRequests.reserve(1)) {
115 FATAL_ERROR_OOM();
116 }
117 }
118
addRequest(Nanoapp * nanoapp,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)119 bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval,
120 Milliseconds minTimeToNext, const void *cookie) {
121 CHRE_ASSERT(nanoapp);
122 return configure(nanoapp, true /* enable */, minInterval, minTimeToNext,
123 cookie);
124 }
125
removeRequest(Nanoapp * nanoapp,const void * cookie)126 bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) {
127 CHRE_ASSERT(nanoapp);
128 return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX),
129 Milliseconds(UINT64_MAX), cookie);
130 }
131
handleStatusChange(bool enabled,uint8_t errorCode)132 void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) {
133 struct CallbackState {
134 bool enabled;
135 uint8_t errorCode;
136 GnssSession *session;
137 };
138
139 auto *cbState = memoryAlloc<CallbackState>();
140 if (cbState == nullptr) {
141 LOG_OOM();
142 } else {
143 cbState->enabled = enabled;
144 cbState->errorCode = errorCode;
145 cbState->session = this;
146
147 auto callback = [](uint16_t /* eventType */, void *eventData) {
148 auto *state = static_cast<CallbackState *>(eventData);
149 state->session->handleStatusChangeSync(state->enabled, state->errorCode);
150 memoryFree(state);
151 };
152
153 EventLoopManagerSingleton::get()->deferCallback(
154 SystemCallbackType::GnssSessionStatusChange, cbState, callback);
155 }
156 }
157
handleReportEvent(void * event)158 void GnssSession::handleReportEvent(void *event) {
159 auto callback = [](uint16_t type, void *eventData) {
160 uint16_t reportEventType;
161 if (!getReportEventType(static_cast<SystemCallbackType>(type),
162 &reportEventType) ||
163 (getSettingState(Setting::LOCATION) == SettingState::DISABLED)) {
164 freeReportEventCallback(reportEventType, eventData);
165 } else {
166 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
167 reportEventType, eventData, freeReportEventCallback);
168 }
169 };
170
171 SystemCallbackType type;
172 if (!getCallbackType(mReportEventType, &type)) {
173 freeReportEventCallback(mReportEventType, event);
174 } else {
175 EventLoopManagerSingleton::get()->deferCallback(type, event, callback);
176 }
177 }
178
onSettingChanged(Setting setting,SettingState state)179 void GnssSession::onSettingChanged(Setting setting, SettingState state) {
180 if (setting == Setting::LOCATION) {
181 if (!mStateTransitions.empty()) {
182 // A request is in progress, so we wait until the async response arrives
183 // to handle the state change.
184 mSettingChangePending = true;
185 } else {
186 handleLocationSettingChange(state);
187 mSettingChangePending = false;
188 }
189 }
190 }
191
handleLocationSettingChange(SettingState state)192 void GnssSession::handleLocationSettingChange(SettingState state) {
193 bool chreDisable = ((state == SettingState::DISABLED) && mPlatformEnabled);
194 bool chreEnable = ((state == SettingState::ENABLED) && !mPlatformEnabled &&
195 !mRequests.empty());
196
197 if (chreEnable || chreDisable) {
198 if (controlPlatform(chreEnable, mCurrentInterval,
199 Milliseconds(0) /* minTimeToNext */)) {
200 LOGD("Configured GNSS %s: setting state %" PRIu8, mName,
201 static_cast<uint8_t>(state));
202 addSessionRequestLog(CHRE_INSTANCE_ID, mCurrentInterval, chreEnable);
203 mInternalRequestPending = true;
204 } else {
205 LOGE("Failed to configure GNSS %s: setting state %" PRIu8, mName,
206 static_cast<uint8_t>(state));
207 }
208 }
209 }
210
logStateToBuffer(DebugDumpWrapper & debugDump) const211 void GnssSession::logStateToBuffer(DebugDumpWrapper &debugDump) const {
212 // TODO: have all interval values print as INVALID if they are the max
213 // unsigned value
214 debugDump.print("\n %s: Curr int(ms)=%" PRIu64 "\n", mName,
215 mCurrentInterval.getMilliseconds());
216 debugDump.print(" Requests:\n");
217 for (const auto &request : mRequests) {
218 debugDump.print(" minInt(ms)=%" PRIu64 " nappId=%" PRIu32 "\n",
219 request.minInterval.getMilliseconds(),
220 request.nanoappInstanceId);
221 }
222
223 if (!mStateTransitions.empty()) {
224 debugDump.print(" Transition queue:\n");
225 for (const auto &transition : mStateTransitions) {
226 debugDump.print(" minInt(ms)=%" PRIu64 " enable=%d nappId=%" PRIu32
227 "\n",
228 transition.minInterval.getMilliseconds(),
229 transition.enable, transition.nanoappInstanceId);
230 }
231 }
232
233 debugDump.print(" Last %zu session requests:\n", mSessionRequestLogs.size());
234 static_assert(kNumSessionRequestLogs <= INT8_MAX,
235 "kNumSessionRequestLogs must be less than INT8_MAX.");
236 for (int8_t i = static_cast<int8_t>(mSessionRequestLogs.size()) - 1; i >= 0;
237 i--) {
238 const auto &log = mSessionRequestLogs[static_cast<size_t>(i)];
239 debugDump.print(" ts=%" PRIu64 " nappId=%" PRIu32 " %s",
240 log.timestamp.toRawNanoseconds(), log.instanceId,
241 log.start ? "start" : "stop\n");
242 if (log.start) {
243 debugDump.print(" int(ms)=%" PRIu64 "\n", log.interval.getMilliseconds());
244 }
245 }
246 }
247
configure(Nanoapp * nanoapp,bool enable,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)248 bool GnssSession::configure(Nanoapp *nanoapp, bool enable,
249 Milliseconds minInterval,
250 Milliseconds minTimeToNext, const void *cookie) {
251 bool success = false;
252 uint32_t instanceId = nanoapp->getInstanceId();
253 size_t requestIndex = 0;
254 bool hasRequest = nanoappHasRequest(instanceId, &requestIndex);
255 if (!mStateTransitions.empty()) {
256 success = addRequestToQueue(instanceId, enable, minInterval, cookie);
257 } else if (stateTransitionIsRequired(enable, minInterval, hasRequest,
258 requestIndex)) {
259 if (enable &&
260 getSettingState(Setting::LOCATION) == SettingState::DISABLED) {
261 // Treat as success but post async failure per API.
262 success = postAsyncResultEvent(instanceId, false /* success */, enable,
263 minInterval, CHRE_ERROR_FUNCTION_DISABLED,
264 cookie);
265 } else if (addRequestToQueue(instanceId, enable, minInterval, cookie)) {
266 success = controlPlatform(enable, minInterval, minTimeToNext);
267 if (!success) {
268 mStateTransitions.pop_back();
269 LOGE("Failed to request a GNSS session for nanoapp instance %" PRIu32
270 " enable %d",
271 instanceId, enable);
272 }
273 }
274 } else {
275 success = postAsyncResultEvent(instanceId, true /* success */, enable,
276 minInterval, CHRE_ERROR_NONE, cookie);
277 }
278
279 if (success) {
280 addSessionRequestLog(nanoapp->getInstanceId(), minInterval, enable);
281 }
282
283 return success;
284 }
285
nanoappHasRequest(uint32_t instanceId,size_t * requestIndex) const286 bool GnssSession::nanoappHasRequest(uint32_t instanceId,
287 size_t *requestIndex) const {
288 bool hasRequest = false;
289 for (size_t i = 0; i < mRequests.size(); i++) {
290 if (mRequests[i].nanoappInstanceId == instanceId) {
291 hasRequest = true;
292 if (requestIndex != nullptr) {
293 *requestIndex = i;
294 }
295
296 break;
297 }
298 }
299
300 return hasRequest;
301 }
302
addRequestToQueue(uint32_t instanceId,bool enable,Milliseconds minInterval,const void * cookie)303 bool GnssSession::addRequestToQueue(uint32_t instanceId, bool enable,
304 Milliseconds minInterval,
305 const void *cookie) {
306 StateTransition stateTransition;
307 stateTransition.nanoappInstanceId = instanceId;
308 stateTransition.enable = enable;
309 stateTransition.minInterval = minInterval;
310 stateTransition.cookie = cookie;
311
312 bool success = mStateTransitions.push(stateTransition);
313 if (!success) {
314 LOGW("Too many session state transitions");
315 }
316
317 return success;
318 }
319
isEnabled() const320 bool GnssSession::isEnabled() const {
321 return !mRequests.empty();
322 }
323
stateTransitionIsRequired(bool requestedState,Milliseconds minInterval,bool nanoappHasRequest,size_t requestIndex) const324 bool GnssSession::stateTransitionIsRequired(bool requestedState,
325 Milliseconds minInterval,
326 bool nanoappHasRequest,
327 size_t requestIndex) const {
328 bool requestToEnable = (requestedState && !isEnabled());
329 bool requestToIncreaseRate =
330 (requestedState && isEnabled() && minInterval < mCurrentInterval);
331 bool requestToDisable =
332 (!requestedState && nanoappHasRequest && mRequests.size() == 1);
333
334 // An effective rate decrease for the session can only occur if the nanoapp
335 // has an existing request.
336 bool requestToDecreaseRate = false;
337 if (nanoappHasRequest) {
338 // The nanoapp has an existing request. Check that the request does not
339 // result in a rate decrease by checking if no other nanoapps have the
340 // same request, the nanoapp's existing request is not equal to the current
341 // requested interval and the new request is slower than the current
342 // requested rate.
343 size_t requestCount = 0;
344 const auto ¤tRequest = mRequests[requestIndex];
345 for (size_t i = 0; i < mRequests.size(); i++) {
346 const Request &request = mRequests[i];
347 if (i != requestIndex &&
348 request.minInterval == currentRequest.minInterval) {
349 requestCount++;
350 }
351 }
352
353 requestToDecreaseRate =
354 (minInterval > mCurrentInterval &&
355 currentRequest.minInterval == mCurrentInterval && requestCount == 0);
356 }
357
358 return (requestToEnable || requestToDisable || requestToIncreaseRate ||
359 requestToDecreaseRate);
360 }
361
updateRequests(bool enable,Milliseconds minInterval,uint32_t instanceId)362 bool GnssSession::updateRequests(bool enable, Milliseconds minInterval,
363 uint32_t instanceId) {
364 bool success = true;
365 Nanoapp *nanoapp =
366 EventLoopManagerSingleton::get()->getEventLoop().findNanoappByInstanceId(
367 instanceId);
368 if (nanoapp == nullptr) {
369 LOGW("Failed to update GNSS session request list for non-existent nanoapp");
370 } else {
371 size_t requestIndex;
372 bool hasExistingRequest = nanoappHasRequest(instanceId, &requestIndex);
373 if (enable) {
374 if (hasExistingRequest) {
375 // If the nanoapp has an open request ensure that the minInterval is
376 // kept up to date.
377 mRequests[requestIndex].minInterval = minInterval;
378 } else {
379 // The GNSS session was successfully enabled for this nanoapp and
380 // there is no existing request. Add it to the list of GNSS session
381 // nanoapps.
382 Request request;
383 request.nanoappInstanceId = instanceId;
384 request.minInterval = minInterval;
385 success = mRequests.push_back(request);
386 if (!success) {
387 LOG_OOM();
388 } else {
389 nanoapp->registerForBroadcastEvent(mReportEventType);
390 }
391 }
392 } else if (hasExistingRequest) {
393 // The session was successfully disabled for a previously enabled
394 // nanoapp. Remove it from the list of requests.
395 mRequests.erase(requestIndex);
396 nanoapp->unregisterForBroadcastEvent(mReportEventType);
397 } // else disabling an inactive request, treat as success per CHRE API
398 }
399
400 return success;
401 }
402
postAsyncResultEvent(uint32_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)403 bool GnssSession::postAsyncResultEvent(uint32_t instanceId, bool success,
404 bool enable, Milliseconds minInterval,
405 uint8_t errorCode, const void *cookie) {
406 bool eventPosted = false;
407 if (!success || updateRequests(enable, minInterval, instanceId)) {
408 chreAsyncResult *event = memoryAlloc<chreAsyncResult>();
409 if (event == nullptr) {
410 LOG_OOM();
411 } else {
412 event->requestType = enable ? mStartRequestType : mStopRequestType;
413 event->success = success;
414 event->errorCode = errorCode;
415 event->reserved = 0;
416 event->cookie = cookie;
417
418 eventPosted =
419 EventLoopManagerSingleton::get()->getEventLoop().postEventOrDie(
420 CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback,
421 instanceId);
422
423 if (!eventPosted) {
424 memoryFree(event);
425 }
426 }
427 }
428
429 return eventPosted;
430 }
431
postAsyncResultEventFatal(uint32_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)432 void GnssSession::postAsyncResultEventFatal(uint32_t instanceId, bool success,
433 bool enable,
434 Milliseconds minInterval,
435 uint8_t errorCode,
436 const void *cookie) {
437 if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode,
438 cookie)) {
439 FATAL_ERROR("Failed to send GNSS session request async result event");
440 }
441 }
442
handleStatusChangeSync(bool enabled,uint8_t errorCode)443 void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) {
444 bool success = (errorCode == CHRE_ERROR_NONE);
445
446 CHRE_ASSERT_LOG(!mStateTransitions.empty() || mInternalRequestPending,
447 "handleStatusChangeSync called with no transitions");
448 if (mInternalRequestPending) {
449 // Silently handle internal requests from CHRE, since they are not pushed
450 // to the mStateTransitions queue.
451 mInternalRequestPending = false;
452 } else if (!mStateTransitions.empty()) {
453 const auto &stateTransition = mStateTransitions.front();
454
455 if (success) {
456 mCurrentInterval = stateTransition.minInterval;
457 }
458
459 success &= (stateTransition.enable == enabled);
460 postAsyncResultEventFatal(
461 stateTransition.nanoappInstanceId, success, stateTransition.enable,
462 stateTransition.minInterval, errorCode, stateTransition.cookie);
463 mStateTransitions.pop();
464 }
465
466 // If a previous setting change event is pending process, do that first.
467 if (mSettingChangePending) {
468 handleLocationSettingChange(getSettingState(Setting::LOCATION));
469 mSettingChangePending = false;
470 } else {
471 // Dispatch pending state transition until first one succeeds
472 dispatchQueuedStateTransitions();
473 }
474 }
475
freeReportEventCallback(uint16_t eventType,void * eventData)476 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
477 switch (eventType) {
478 case CHRE_EVENT_GNSS_LOCATION:
479 EventLoopManagerSingleton::get()
480 ->getGnssManager()
481 .mPlatformGnss.releaseLocationEvent(
482 static_cast<chreGnssLocationEvent *>(eventData));
483 break;
484
485 case CHRE_EVENT_GNSS_DATA:
486 EventLoopManagerSingleton::get()
487 ->getGnssManager()
488 .mPlatformGnss.releaseMeasurementDataEvent(
489 static_cast<chreGnssDataEvent *>(eventData));
490 break;
491
492 default:
493 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
494 }
495 }
496
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)497 bool GnssSession::controlPlatform(bool enable, Milliseconds minInterval,
498 Milliseconds /* minTimeToNext */) {
499 bool success = false;
500
501 switch (mReportEventType) {
502 case CHRE_EVENT_GNSS_LOCATION:
503 // TODO: Provide support for min time to next report. It is currently sent
504 // to the platform as zero.
505 success = EventLoopManagerSingleton::get()
506 ->getGnssManager()
507 .mPlatformGnss.controlLocationSession(enable, minInterval,
508 Milliseconds(0));
509 break;
510
511 case CHRE_EVENT_GNSS_DATA:
512 success =
513 EventLoopManagerSingleton::get()
514 ->getGnssManager()
515 .mPlatformGnss.controlMeasurementSession(enable, minInterval);
516 break;
517
518 default:
519 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, mReportEventType);
520 }
521
522 if (success) {
523 mPlatformEnabled = enable;
524 }
525
526 return success;
527 }
528
addSessionRequestLog(uint32_t nanoappInstanceId,Milliseconds interval,bool start)529 void GnssSession::addSessionRequestLog(uint32_t nanoappInstanceId,
530 Milliseconds interval, bool start) {
531 mSessionRequestLogs.kick_push(SessionRequestLog(
532 SystemTime::getMonotonicTime(), nanoappInstanceId, interval, start));
533 }
534
dispatchQueuedStateTransitions()535 void GnssSession::dispatchQueuedStateTransitions() {
536 while (!mStateTransitions.empty()) {
537 const auto &stateTransition = mStateTransitions.front();
538
539 size_t requestIndex;
540 bool hasRequest =
541 nanoappHasRequest(stateTransition.nanoappInstanceId, &requestIndex);
542
543 if (stateTransitionIsRequired(stateTransition.enable,
544 stateTransition.minInterval, hasRequest,
545 requestIndex)) {
546 if (getSettingState(Setting::LOCATION) == SettingState::DISABLED) {
547 postAsyncResultEventFatal(
548 stateTransition.nanoappInstanceId, false /* success */,
549 stateTransition.enable, stateTransition.minInterval,
550 CHRE_ERROR_FUNCTION_DISABLED, stateTransition.cookie);
551 mStateTransitions.pop();
552 } else if (controlPlatform(stateTransition.enable,
553 stateTransition.minInterval,
554 Milliseconds(0))) {
555 break;
556 } else {
557 LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu32,
558 stateTransition.nanoappInstanceId);
559 postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
560 false /* success */, stateTransition.enable,
561 stateTransition.minInterval, CHRE_ERROR,
562 stateTransition.cookie);
563 mStateTransitions.pop();
564 }
565 } else {
566 postAsyncResultEventFatal(stateTransition.nanoappInstanceId,
567 true /* success */, stateTransition.enable,
568 stateTransition.minInterval, CHRE_ERROR_NONE,
569 stateTransition.cookie);
570 mStateTransitions.pop();
571 }
572 }
573 }
574
575 } // namespace chre
576