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/platform/assert.h" 21 #include "chre/platform/fatal_error.h" 22 #include "chre/util/system/debug_dump.h" 23 24 namespace chre { 25 26 GnssManager::GnssManager() 27 : mLocationSession(CHRE_EVENT_GNSS_LOCATION), 28 mMeasurementSession(CHRE_EVENT_GNSS_DATA) { 29 } 30 31 void GnssManager::init() { 32 mPlatformGnss.init(); 33 } 34 35 uint32_t GnssManager::getCapabilities() { 36 return mPlatformGnss.getCapabilities(); 37 } 38 39 bool GnssManager::logStateToBuffer( 40 char *buffer, size_t *bufferPos, size_t bufferSize) const { 41 bool success = debugDumpPrint(buffer, bufferPos, bufferSize,"\nGNSS:"); 42 success &= mLocationSession.logStateToBuffer(buffer, bufferPos, bufferSize); 43 success &= mMeasurementSession 44 .logStateToBuffer(buffer, bufferPos, bufferSize); 45 return success; 46 } 47 48 GnssSession::GnssSession(uint16_t reportEventType) 49 : mReportEventType(reportEventType) { 50 switch (mReportEventType) { 51 case CHRE_EVENT_GNSS_LOCATION: 52 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START; 53 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP; 54 mName = "Location"; 55 break; 56 57 case CHRE_EVENT_GNSS_DATA: 58 mStartRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START; 59 mStopRequestType = CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP; 60 mName = "Measurement"; 61 break; 62 63 default: 64 CHRE_ASSERT_LOG(false, "Unsupported eventType %" PRIu16, reportEventType); 65 } 66 67 if (!mRequests.reserve(1)) { 68 FATAL_ERROR_OOM(); 69 } 70 } 71 72 bool GnssSession::addRequest(Nanoapp *nanoapp, Milliseconds minInterval, 73 Milliseconds minTimeToNext, const void *cookie) { 74 CHRE_ASSERT(nanoapp); 75 return configure(nanoapp, true /* enable */, minInterval, minTimeToNext, 76 cookie); 77 } 78 79 bool GnssSession::removeRequest(Nanoapp *nanoapp, const void *cookie) { 80 CHRE_ASSERT(nanoapp); 81 return configure(nanoapp, false /* enable */, Milliseconds(UINT64_MAX), 82 Milliseconds(UINT64_MAX), cookie); 83 } 84 85 void GnssSession::handleStatusChange(bool enabled, uint8_t errorCode) { 86 struct CallbackState { 87 bool enabled; 88 uint8_t errorCode; 89 GnssSession *session; 90 }; 91 92 auto *cbState = memoryAlloc<CallbackState>(); 93 if (cbState == nullptr) { 94 LOGE("Failed to allocate callback state for GNSS session state change"); 95 } else { 96 cbState->enabled = enabled; 97 cbState->errorCode = errorCode; 98 cbState->session = this; 99 100 auto callback = [](uint16_t /* eventType */, void *eventData) { 101 auto *state = static_cast<CallbackState *>(eventData); 102 state->session->handleStatusChangeSync(state->enabled, state->errorCode); 103 memoryFree(state); 104 }; 105 106 EventLoopManagerSingleton::get()->deferCallback( 107 SystemCallbackType::GnssSessionStatusChange, cbState, callback); 108 } 109 } 110 111 void GnssSession::handleReportEvent(void *event) { 112 EventLoopManagerSingleton::get()->getEventLoop() 113 .postEvent(mReportEventType, event, freeReportEventCallback); 114 } 115 116 bool GnssSession::logStateToBuffer( 117 char *buffer, size_t *bufferPos, size_t bufferSize) const { 118 bool success = debugDumpPrint(buffer, bufferPos, bufferSize, 119 "\n %s: Current interval(ms)=%" PRIu64 "\n", 120 mName, mCurrentInterval.getMilliseconds()); 121 122 success &= debugDumpPrint(buffer, bufferPos, bufferSize, " Requests:\n"); 123 for (const auto& request : mRequests) { 124 success &= debugDumpPrint(buffer, bufferPos, bufferSize, 125 " minInterval(ms)=%" PRIu64 " nanoappId=%" 126 PRIu32 "\n", 127 request.minInterval.getMilliseconds(), 128 request.nanoappInstanceId); 129 } 130 131 success &= debugDumpPrint(buffer, bufferPos, bufferSize, 132 " Transition queue:\n"); 133 for (const auto& transition : mStateTransitions) { 134 success &= debugDumpPrint(buffer, bufferPos, bufferSize, 135 " minInterval(ms)=%" PRIu64 " enable=%d" 136 " nanoappId=%" PRIu32 "\n", 137 transition.minInterval.getMilliseconds(), 138 transition.enable, transition.nanoappInstanceId); 139 } 140 141 return success; 142 } 143 144 bool GnssSession::configure( 145 Nanoapp *nanoapp, bool enable, Milliseconds minInterval, 146 Milliseconds minTimeToNext, const void *cookie) { 147 bool success = false; 148 uint32_t instanceId = nanoapp->getInstanceId(); 149 size_t requestIndex = 0; 150 bool hasRequest = nanoappHasRequest(instanceId, &requestIndex); 151 if (!mStateTransitions.empty()) { 152 success = addRequestToQueue(instanceId, enable, minInterval, cookie); 153 } else if (isInRequestedState(enable, minInterval, hasRequest)) { 154 success = postAsyncResultEvent( 155 instanceId, true /* success */, enable, minInterval, CHRE_ERROR_NONE, 156 cookie); 157 } else if (stateTransitionIsRequired(enable, minInterval, hasRequest, 158 requestIndex)) { 159 success = addRequestToQueue(instanceId, enable, minInterval, cookie); 160 if (success) { 161 success = controlPlatform(enable, minInterval, minTimeToNext); 162 if (!success) { 163 mStateTransitions.pop_back(); 164 LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu32, 165 instanceId); 166 } 167 } 168 } else { 169 CHRE_ASSERT_LOG(false, "Invalid GNSS session configuration"); 170 } 171 172 return success; 173 } 174 175 bool GnssSession::nanoappHasRequest( 176 uint32_t instanceId, size_t *requestIndex) const { 177 bool hasRequest = false; 178 for (size_t i = 0; i < mRequests.size(); i++) { 179 if (mRequests[i].nanoappInstanceId == instanceId) { 180 hasRequest = true; 181 if (requestIndex != nullptr) { 182 *requestIndex = i; 183 } 184 185 break; 186 } 187 } 188 189 return hasRequest; 190 } 191 192 bool GnssSession::addRequestToQueue( 193 uint32_t instanceId, bool enable, Milliseconds minInterval, 194 const void *cookie) { 195 StateTransition stateTransition; 196 stateTransition.nanoappInstanceId = instanceId; 197 stateTransition.enable = enable; 198 stateTransition.minInterval = minInterval; 199 stateTransition.cookie = cookie; 200 201 bool success = mStateTransitions.push(stateTransition); 202 if (!success) { 203 LOGW("Too many session state transitions"); 204 } 205 206 return success; 207 } 208 209 bool GnssSession::isEnabled() const { 210 return !mRequests.empty(); 211 } 212 213 bool GnssSession::isInRequestedState( 214 bool requestedState, Milliseconds minInterval, bool nanoappHasRequest) 215 const { 216 bool inTargetState = (requestedState == isEnabled()); 217 bool meetsMinInterval = (minInterval >= mCurrentInterval); 218 bool hasMoreThanOneRequest = (mRequests.size() > 1); 219 return ((inTargetState && (!requestedState || meetsMinInterval)) 220 || (!requestedState && (!nanoappHasRequest || hasMoreThanOneRequest))); 221 } 222 223 bool GnssSession::stateTransitionIsRequired( 224 bool requestedState, Milliseconds minInterval, bool nanoappHasRequest, 225 size_t requestIndex) const { 226 bool requestToEnable = (requestedState && !isEnabled()); 227 bool requestToIncreaseRate = (requestedState && isEnabled() 228 && minInterval < mCurrentInterval); 229 bool requestToDisable = (!requestedState && nanoappHasRequest 230 && mRequests.size() == 1); 231 232 // An effective rate decrease for the session can only occur if the nanoapp 233 // has an existing request. 234 bool requestToDecreaseRate = false; 235 if (nanoappHasRequest) { 236 // The nanoapp has an existing request. Check that the request does not 237 // result in a rate decrease by checking if no other nanoapps have the 238 // same request, the nanoapp's existing request is not equal to the current 239 // requested interval and the new request is slower than the current 240 // requested rate. 241 size_t requestCount = 0; 242 const auto& currentRequest = mRequests[requestIndex]; 243 for (size_t i = 0; i < mRequests.size(); i++) { 244 const Request& request = mRequests[i]; 245 if (i != requestIndex 246 && request.minInterval == currentRequest.minInterval) { 247 requestCount++; 248 } 249 } 250 251 requestToDecreaseRate = (minInterval > mCurrentInterval 252 && currentRequest.minInterval == mCurrentInterval && requestCount == 0); 253 } 254 255 return (requestToEnable || requestToDisable || requestToIncreaseRate 256 || requestToDecreaseRate); 257 } 258 259 bool GnssSession::updateRequests( 260 bool enable, Milliseconds minInterval, uint32_t instanceId) { 261 bool success = true; 262 Nanoapp *nanoapp = EventLoopManagerSingleton::get()->getEventLoop() 263 .findNanoappByInstanceId(instanceId); 264 if (nanoapp == nullptr) { 265 LOGW("Failed to update GNSS session request list for non-existent nanoapp"); 266 } else { 267 size_t requestIndex; 268 bool hasExistingRequest = nanoappHasRequest(instanceId, &requestIndex); 269 if (enable) { 270 if (hasExistingRequest) { 271 // If the nanoapp has an open request ensure that the minInterval is 272 // kept up to date. 273 mRequests[requestIndex].minInterval = minInterval; 274 } else { 275 // The GNSS session was successfully enabled for this nanoapp and 276 // there is no existing request. Add it to the list of GNSS session 277 // nanoapps. 278 Request request; 279 request.nanoappInstanceId = instanceId; 280 request.minInterval = minInterval; 281 success = mRequests.push_back(request); 282 if (!success) { 283 LOGE("Failed to add nanoapp to the list of GNSS session nanoapps"); 284 } else { 285 nanoapp->registerForBroadcastEvent(mReportEventType); 286 } 287 } 288 } else { 289 if (!hasExistingRequest) { 290 success = false; 291 LOGE("Received a GNSS session state change for a non-existent nanoapp"); 292 } else { 293 // The session was successfully disabled for a previously enabled 294 // nanoapp. Remove it from the list of requests. 295 mRequests.erase(requestIndex); 296 nanoapp->unregisterForBroadcastEvent(mReportEventType); 297 } 298 } 299 } 300 301 return success; 302 } 303 304 bool GnssSession::postAsyncResultEvent( 305 uint32_t instanceId, bool success, bool enable, Milliseconds minInterval, 306 uint8_t errorCode, const void *cookie) { 307 bool eventPosted = false; 308 if (!success || updateRequests(enable, minInterval, instanceId)) { 309 chreAsyncResult *event = memoryAlloc<chreAsyncResult>(); 310 if (event == nullptr) { 311 LOGE("Failed to allocate GNSS session async result event"); 312 } else { 313 event->requestType = enable ? mStartRequestType : mStopRequestType; 314 event->success = success; 315 event->errorCode = errorCode; 316 event->reserved = 0; 317 event->cookie = cookie; 318 319 eventPosted = EventLoopManagerSingleton::get()->getEventLoop() 320 .postEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback, 321 kSystemInstanceId, instanceId); 322 323 if (!eventPosted) { 324 memoryFree(event); 325 } 326 } 327 } 328 329 return eventPosted; 330 } 331 332 void GnssSession::postAsyncResultEventFatal( 333 uint32_t instanceId, bool success, bool enable, Milliseconds minInterval, 334 uint8_t errorCode, const void *cookie) { 335 if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode, 336 cookie)) { 337 FATAL_ERROR("Failed to send GNSS session request async result event"); 338 } 339 } 340 341 void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) { 342 bool success = (errorCode == CHRE_ERROR_NONE); 343 344 CHRE_ASSERT_LOG(!mStateTransitions.empty(), 345 "handleStatusChangeSync called with no transitions"); 346 if (!mStateTransitions.empty()) { 347 const auto& stateTransition = mStateTransitions.front(); 348 349 if (success) { 350 mCurrentInterval = stateTransition.minInterval; 351 } 352 353 success &= (stateTransition.enable == enabled); 354 postAsyncResultEventFatal(stateTransition.nanoappInstanceId, success, 355 stateTransition.enable, 356 stateTransition.minInterval, 357 errorCode, stateTransition.cookie); 358 mStateTransitions.pop(); 359 } 360 361 while (!mStateTransitions.empty()) { 362 const auto& stateTransition = mStateTransitions.front(); 363 364 size_t requestIndex; 365 bool hasRequest = nanoappHasRequest( 366 stateTransition.nanoappInstanceId, &requestIndex); 367 368 if (stateTransitionIsRequired(stateTransition.enable, 369 stateTransition.minInterval, 370 hasRequest, requestIndex)) { 371 if (controlPlatform(stateTransition.enable, stateTransition.minInterval, 372 Milliseconds(0))) { 373 break; 374 } else { 375 LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu32, 376 stateTransition.nanoappInstanceId); 377 postAsyncResultEventFatal( 378 stateTransition.nanoappInstanceId, false /* success */, 379 stateTransition.enable, stateTransition.minInterval, 380 CHRE_ERROR, stateTransition.cookie); 381 mStateTransitions.pop(); 382 } 383 } else { 384 postAsyncResultEventFatal( 385 stateTransition.nanoappInstanceId, true /* success */, 386 stateTransition.enable, stateTransition.minInterval, 387 errorCode, stateTransition.cookie); 388 mStateTransitions.pop(); 389 } 390 } 391 } 392 393 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) { 394 switch (eventType) { 395 case CHRE_EVENT_GNSS_LOCATION: 396 EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss 397 .releaseLocationEvent( 398 static_cast<chreGnssLocationEvent *>(eventData)); 399 break; 400 401 case CHRE_EVENT_GNSS_DATA: 402 EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss 403 .releaseMeasurementDataEvent( 404 static_cast<chreGnssDataEvent *>(eventData)); 405 break; 406 407 default: 408 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType); 409 } 410 } 411 412 bool GnssSession::controlPlatform( 413 bool enable, Milliseconds minInterval, Milliseconds /* minTimeToNext */) { 414 bool success = false; 415 416 switch (mReportEventType) { 417 case CHRE_EVENT_GNSS_LOCATION: 418 // TODO: Provide support for min time to next report. It is currently sent 419 // to the platform as zero. 420 success = EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss 421 .controlLocationSession(enable, minInterval, Milliseconds(0)); 422 break; 423 424 case CHRE_EVENT_GNSS_DATA: 425 success = EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss 426 .controlMeasurementSession(enable, minInterval); 427 break; 428 429 default: 430 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, mReportEventType); 431 } 432 return success; 433 } 434 435 } // namespace chre 436