• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GnssManager()26 GnssManager::GnssManager()
27     : mLocationSession(CHRE_EVENT_GNSS_LOCATION),
28       mMeasurementSession(CHRE_EVENT_GNSS_DATA) {
29 }
30 
init()31 void GnssManager::init() {
32   mPlatformGnss.init();
33 }
34 
getCapabilities()35 uint32_t GnssManager::getCapabilities() {
36   return mPlatformGnss.getCapabilities();
37 }
38 
logStateToBuffer(char * buffer,size_t * bufferPos,size_t bufferSize) const39 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 
GnssSession(uint16_t reportEventType)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 
addRequest(Nanoapp * nanoapp,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)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 
removeRequest(Nanoapp * nanoapp,const void * cookie)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 
handleStatusChange(bool enabled,uint8_t errorCode)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 
handleReportEvent(void * event)111 void GnssSession::handleReportEvent(void *event) {
112   EventLoopManagerSingleton::get()->getEventLoop()
113       .postEvent(mReportEventType, event, freeReportEventCallback);
114 }
115 
logStateToBuffer(char * buffer,size_t * bufferPos,size_t bufferSize) const116 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 
configure(Nanoapp * nanoapp,bool enable,Milliseconds minInterval,Milliseconds minTimeToNext,const void * cookie)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 
nanoappHasRequest(uint32_t instanceId,size_t * requestIndex) const175 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 
addRequestToQueue(uint32_t instanceId,bool enable,Milliseconds minInterval,const void * cookie)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 
isEnabled() const209 bool GnssSession::isEnabled() const {
210   return !mRequests.empty();
211 }
212 
isInRequestedState(bool requestedState,Milliseconds minInterval,bool nanoappHasRequest) const213 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 
stateTransitionIsRequired(bool requestedState,Milliseconds minInterval,bool nanoappHasRequest,size_t requestIndex) const223 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 
updateRequests(bool enable,Milliseconds minInterval,uint32_t instanceId)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 
postAsyncResultEvent(uint32_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)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 
postAsyncResultEventFatal(uint32_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)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 
handleStatusChangeSync(bool enabled,uint8_t errorCode)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 
freeReportEventCallback(uint16_t eventType,void * eventData)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 
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)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