Home | History | Annotate | Download | only in core
      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