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