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 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 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 72 GnssManager::GnssManager() 73 : mLocationSession(CHRE_EVENT_GNSS_LOCATION), 74 mMeasurementSession(CHRE_EVENT_GNSS_DATA) {} 75 76 void GnssManager::init() { 77 mPlatformGnss.init(); 78 } 79 80 uint32_t GnssManager::getCapabilities() { 81 return mPlatformGnss.getCapabilities(); 82 } 83 84 void GnssManager::onSettingChanged(Setting setting, SettingState state) { 85 mLocationSession.onSettingChanged(setting, state); 86 mMeasurementSession.onSettingChanged(setting, state); 87 } 88 89 void GnssManager::logStateToBuffer(DebugDumpWrapper &debugDump) const { 90 debugDump.print("\nGNSS:"); 91 mLocationSession.logStateToBuffer(debugDump); 92 mMeasurementSession.logStateToBuffer(debugDump); 93 } 94 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 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 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 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 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 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 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 211 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 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 286 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 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 320 bool GnssSession::isEnabled() const { 321 return !mRequests.empty(); 322 } 323 324 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 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 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 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 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 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 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 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 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