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