gnss_request_manager.cc revision 51954e2f8592b3bb37bb3a7b52a30830382d3b0a
1/* 2 * Copyright (C) 2017 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_request_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 23namespace chre { 24 25GnssRequestManager::GnssRequestManager() 26 : mCurrentLocationSessionInterval(UINT64_MAX) { 27 if (!mLocationSessionRequests.reserve(1)) { 28 FATAL_ERROR("Failed to allocate GNSS requests list at startup"); 29 } 30} 31 32uint32_t GnssRequestManager::getCapabilities() { 33 return mPlatformGnss.getCapabilities(); 34} 35 36bool GnssRequestManager::startLocationSession(Nanoapp *nanoapp, 37 Milliseconds minInterval, 38 Milliseconds minTimeToNextFix, 39 const void *cookie) { 40 CHRE_ASSERT(nanoapp); 41 return configureLocationSession(nanoapp, true /* enable */, minInterval, 42 minTimeToNextFix, cookie); 43} 44 45bool GnssRequestManager::stopLocationSession(Nanoapp *nanoapp, 46 const void *cookie) { 47 CHRE_ASSERT(nanoapp); 48 return configureLocationSession(nanoapp, false /* enable */, 49 Milliseconds(UINT64_MAX), 50 Milliseconds(UINT64_MAX), cookie); 51} 52 53void GnssRequestManager::handleLocationSessionStatusChange(bool enabled, 54 uint8_t errorCode) { 55 struct CallbackState { 56 bool enabled; 57 uint8_t errorCode; 58 }; 59 60 auto *cbState = memoryAlloc<CallbackState>(); 61 if (cbState == nullptr) { 62 LOGE("Failed to allocate callback state for location session state change"); 63 } else { 64 cbState->enabled = enabled; 65 cbState->errorCode = errorCode; 66 67 auto callback = [](uint16_t /* eventType */, void *eventData) { 68 auto *state = static_cast<CallbackState *>(eventData); 69 EventLoopManagerSingleton::get()->getGnssRequestManager() 70 .handleLocationSessionStatusChangeSync(state->enabled, 71 state->errorCode); 72 memoryFree(state); 73 }; 74 75 bool callbackDeferred = EventLoopManagerSingleton::get()->deferCallback( 76 SystemCallbackType::GnssLocationSessionStatusChange, cbState, callback); 77 if (!callbackDeferred) { 78 memoryFree(cbState); 79 } 80 } 81} 82 83void GnssRequestManager::handleLocationEvent(chreGnssLocationEvent *event) { 84 bool eventPosted = EventLoopManagerSingleton::get()->postEvent( 85 CHRE_EVENT_GNSS_LOCATION, event, freeLocationEventCallback, 86 kSystemInstanceId, kBroadcastInstanceId); 87 if (!eventPosted) { 88 FATAL_ERROR("Failed to send GNSS location event"); 89 } 90} 91 92bool GnssRequestManager::configureLocationSession( 93 Nanoapp *nanoapp, bool enable, Milliseconds minInterval, 94 Milliseconds minTimeToFirstFix, const void *cookie) { 95 bool success = false; 96 uint32_t instanceId = nanoapp->getInstanceId(); 97 size_t requestIndex = 0; 98 bool nanoappHasRequest = nanoappHasLocationSessionRequest(instanceId, 99 &requestIndex); 100 if (!mLocationSessionStateTransitions.empty()) { 101 success = addLocationSessionRequestToQueue(instanceId, enable, minInterval, 102 cookie); 103 } else if (locationSessionIsInRequestedState(enable, minInterval, 104 nanoappHasRequest)) { 105 success = postLocationSessionAsyncResultEvent( 106 instanceId, true /* success */, enable, minInterval, CHRE_ERROR_NONE, 107 cookie); 108 } else if (locationSessionStateTransitionIsRequired(enable, minInterval, 109 nanoappHasRequest, 110 requestIndex)) { 111 success = addLocationSessionRequestToQueue(instanceId, enable, 112 minInterval, cookie); 113 if (success) { 114 // TODO: Provide support for min time to next fix. It is currently sent 115 // to the platform as zero. 116 success = mPlatformGnss.controlLocationSession(enable, minInterval, 117 Milliseconds(0)); 118 if (!success) { 119 // TODO: Add a pop_back method. 120 mLocationSessionStateTransitions.remove( 121 mLocationSessionRequests.size() - 1); 122 LOGE("Failed to enable a GNSS location session for nanoapp instance " 123 "%" PRIu32, instanceId); 124 } 125 } 126 } else { 127 CHRE_ASSERT_LOG(false, "Invalid location session configuration"); 128 } 129 130 return success; 131} 132 133bool GnssRequestManager::nanoappHasLocationSessionRequest( 134 uint32_t instanceId, size_t *requestIndex) { 135 bool hasLocationSessionRequest = false; 136 for (size_t i = 0; i < mLocationSessionRequests.size(); i++) { 137 if (mLocationSessionRequests[i].nanoappInstanceId == instanceId) { 138 hasLocationSessionRequest = true; 139 if (requestIndex != nullptr) { 140 *requestIndex = i; 141 } 142 143 break; 144 } 145 } 146 147 return hasLocationSessionRequest; 148} 149 150bool GnssRequestManager::addLocationSessionRequestToQueue( 151 uint32_t instanceId, bool enable, Milliseconds minInterval, 152 const void *cookie) { 153 LocationSessionStateTransition stateTransition; 154 stateTransition.nanoappInstanceId = instanceId; 155 stateTransition.enable = enable; 156 stateTransition.minInterval = minInterval; 157 stateTransition.cookie = cookie; 158 159 bool success = mLocationSessionStateTransitions.push(stateTransition); 160 if (!success) { 161 LOGW("Too many location session state transitions"); 162 } 163 164 return success; 165} 166 167bool GnssRequestManager::locationSessionIsEnabled() { 168 return !mLocationSessionRequests.empty(); 169} 170 171bool GnssRequestManager::locationSessionIsInRequestedState( 172 bool requestedState, Milliseconds minInterval, bool nanoappHasRequest) { 173 bool inTargetState = (requestedState == locationSessionIsEnabled()); 174 bool meetsMinInterval = (minInterval >= mCurrentLocationSessionInterval); 175 bool hasMoreThanOneRequest = (mLocationSessionRequests.size() > 1); 176 return ((inTargetState && (!requestedState || meetsMinInterval)) 177 || (!requestedState && (!nanoappHasRequest || hasMoreThanOneRequest))); 178} 179 180bool GnssRequestManager::locationSessionStateTransitionIsRequired( 181 bool requestedState, Milliseconds minInterval, bool nanoappHasRequest, 182 size_t requestIndex) { 183 bool requestToEnable = (requestedState && !locationSessionIsEnabled()); 184 bool requestToIncreaseRate = (requestedState && locationSessionIsEnabled() 185 && minInterval < mCurrentLocationSessionInterval); 186 bool requestToDisable = (!requestedState && nanoappHasRequest 187 && mLocationSessionRequests.size() == 1); 188 189 // An effective rate decrease for the location session can only occur if the 190 // nanoapp has an existing request. 191 bool requestToDecreaseRate = false; 192 if (nanoappHasRequest) { 193 // The nanoapp has an existing request. Check that the request does not 194 // result in a rate decrease by checking if no other nanoapps have the 195 // same request, the nanoapp's existing request is not equal to the current 196 // requested interval and the new request is slower than the current 197 // requested rate. 198 size_t requestCount = 0; 199 const auto& currentRequest = mLocationSessionRequests[requestIndex]; 200 for (size_t i = 0; i < mLocationSessionRequests.size(); i++) { 201 LocationSessionRequest& request = mLocationSessionRequests[i]; 202 if (i != requestIndex 203 && request.minInterval == currentRequest.minInterval) { 204 requestCount++; 205 } 206 } 207 208 requestToDecreaseRate = (minInterval > mCurrentLocationSessionInterval 209 && currentRequest.minInterval == mCurrentLocationSessionInterval 210 && requestCount == 0); 211 } 212 213 return (requestToEnable || requestToDisable 214 || requestToIncreaseRate || requestToDecreaseRate); 215} 216 217bool GnssRequestManager::updateLocationSessionRequests( 218 bool enable, Milliseconds minInterval, uint32_t instanceId) { 219 bool success = true; 220 Nanoapp *nanoapp = EventLoopManagerSingleton::get()-> 221 findNanoappByInstanceId(instanceId); 222 if (nanoapp == nullptr) { 223 CHRE_ASSERT_LOG(false, "Failed to update location session request list for " 224 "non-existent nanoapp"); 225 } else { 226 size_t requestIndex; 227 bool hasExistingRequest = nanoappHasLocationSessionRequest(instanceId, 228 &requestIndex); 229 if (enable) { 230 if (hasExistingRequest) { 231 // If the nanoapp has an open request ensure that the minInterval is 232 // kept up to date. 233 mLocationSessionRequests[requestIndex].minInterval = minInterval; 234 } else { 235 success = nanoapp->registerForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION); 236 if (!success) { 237 LOGE("Failed to register nanoapp for GNSS location events"); 238 } else { 239 // The location session was successfully enabled for this nanoapp and 240 // there is no existing request. Add it to the list of location 241 // session nanoapps. 242 LocationSessionRequest locationSessionRequest; 243 locationSessionRequest.nanoappInstanceId = instanceId; 244 locationSessionRequest.minInterval = minInterval; 245 success = mLocationSessionRequests.push_back(locationSessionRequest); 246 if (!success) { 247 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION); 248 LOGE("Failed to add nanoapp to the list of location session " 249 "nanoapps"); 250 } 251 } 252 } 253 } else { 254 if (!hasExistingRequest) { 255 success = false; 256 LOGE("Received a location session state change for a non-existent " 257 "nanoapp"); 258 } else { 259 // The location session was successfully disabled for a previously 260 // enabled nanoapp. Remove it from the list of requests. 261 mLocationSessionRequests.erase(requestIndex); 262 nanoapp->unregisterForBroadcastEvent(CHRE_EVENT_GNSS_LOCATION); 263 } 264 } 265 } 266 267 return success; 268} 269 270bool GnssRequestManager::postLocationSessionAsyncResultEvent( 271 uint32_t instanceId, bool success, bool enable, Milliseconds minInterval, 272 uint8_t errorCode, const void *cookie) { 273 bool eventPosted = false; 274 if (!success || updateLocationSessionRequests(enable, minInterval, 275 instanceId)) { 276 chreAsyncResult *event = memoryAlloc<chreAsyncResult>(); 277 if (event == nullptr) { 278 LOGE("Failed to allocate location session async result event"); 279 } else { 280 if (enable) { 281 event->requestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START; 282 } else { 283 event->requestType = CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP; 284 } 285 286 event->success = success; 287 event->errorCode = errorCode; 288 event->reserved = 0; 289 event->cookie = cookie; 290 291 eventPosted = EventLoopManagerSingleton::get()->postEvent( 292 CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback, 293 kSystemInstanceId, instanceId); 294 295 if (!eventPosted) { 296 memoryFree(event); 297 } 298 } 299 } 300 301 return eventPosted; 302} 303 304void GnssRequestManager::postLocationSessionAsyncResultEventFatal( 305 uint32_t instanceId, bool success, bool enable, Milliseconds minInterval, 306 uint8_t errorCode, const void *cookie) { 307 if (!postLocationSessionAsyncResultEvent(instanceId, success, enable, 308 minInterval, errorCode, cookie)) { 309 FATAL_ERROR("Failed to send GNSS location request async result event"); 310 } 311} 312 313void GnssRequestManager::handleLocationSessionStatusChangeSync( 314 bool enabled, uint8_t errorCode) { 315 bool success = (errorCode == CHRE_ERROR_NONE); 316 317 CHRE_ASSERT_LOG(!mLocationSessionStateTransitions.empty(), 318 "handleLocationSessionStatusChangeSync called with no " 319 "transitions"); 320 if (!mLocationSessionStateTransitions.empty()) { 321 const auto& stateTransition = mLocationSessionStateTransitions.front(); 322 323 if (success) { 324 mCurrentLocationSessionInterval = stateTransition.minInterval; 325 } 326 327 success &= (stateTransition.enable == enabled); 328 postLocationSessionAsyncResultEventFatal(stateTransition.nanoappInstanceId, 329 success, stateTransition.enable, 330 stateTransition.minInterval, 331 errorCode, stateTransition.cookie); 332 mLocationSessionStateTransitions.pop(); 333 } 334 335 while (!mLocationSessionStateTransitions.empty()) { 336 const auto& stateTransition = mLocationSessionStateTransitions.front(); 337 338 size_t requestIndex; 339 bool nanoappHasRequest = nanoappHasLocationSessionRequest( 340 stateTransition.nanoappInstanceId, &requestIndex); 341 342 if (locationSessionStateTransitionIsRequired(stateTransition.enable, 343 stateTransition.minInterval, 344 nanoappHasRequest, 345 requestIndex)) { 346 if (mPlatformGnss.controlLocationSession(stateTransition.enable, 347 stateTransition.minInterval, 348 Milliseconds(0))) { 349 break; 350 } else { 351 LOGE("Failed to enable a GNSS location session for nanoapp instance " 352 "%" PRIu32, stateTransition.nanoappInstanceId); 353 postLocationSessionAsyncResultEventFatal( 354 stateTransition.nanoappInstanceId, false /* success */, 355 stateTransition.enable, stateTransition.minInterval, 356 CHRE_ERROR, stateTransition.cookie); 357 mLocationSessionStateTransitions.pop(); 358 } 359 } else { 360 postLocationSessionAsyncResultEventFatal( 361 stateTransition.nanoappInstanceId, true /* success */, 362 stateTransition.enable, stateTransition.minInterval, 363 errorCode, stateTransition.cookie); 364 mLocationSessionStateTransitions.pop(); 365 } 366 } 367} 368 369void GnssRequestManager::handleFreeLocationEvent(chreGnssLocationEvent *event) { 370 mPlatformGnss.releaseLocationEvent(event); 371} 372 373void GnssRequestManager::freeLocationEventCallback(uint16_t eventType, 374 void *eventData) { 375 auto *locationEvent = static_cast<chreGnssLocationEvent *>(eventData); 376 EventLoopManagerSingleton::get()->getGnssRequestManager() 377 .handleFreeLocationEvent(locationEvent); 378} 379 380} // namespace chre 381