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()
111 .postEvent(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 = EventLoopManagerSingleton::get()->getEventLoop()
297 .postEvent(CHRE_EVENT_GNSS_ASYNC_RESULT, event, freeEventDataCallback,
298 kSystemInstanceId, instanceId);
299
300 if (!eventPosted) {
301 memoryFree(event);
302 }
303 }
304 }
305
306 return eventPosted;
307 }
308
postAsyncResultEventFatal(uint32_t instanceId,bool success,bool enable,Milliseconds minInterval,uint8_t errorCode,const void * cookie)309 void GnssSession::postAsyncResultEventFatal(
310 uint32_t instanceId, bool success, bool enable, Milliseconds minInterval,
311 uint8_t errorCode, const void *cookie) {
312 if (!postAsyncResultEvent(instanceId, success, enable, minInterval, errorCode,
313 cookie)) {
314 FATAL_ERROR("Failed to send GNSS session request async result event");
315 }
316 }
317
handleStatusChangeSync(bool enabled,uint8_t errorCode)318 void GnssSession::handleStatusChangeSync(bool enabled, uint8_t errorCode) {
319 bool success = (errorCode == CHRE_ERROR_NONE);
320
321 CHRE_ASSERT_LOG(!mStateTransitions.empty(),
322 "handleStatusChangeSync called with no transitions");
323 if (!mStateTransitions.empty()) {
324 const auto& stateTransition = mStateTransitions.front();
325
326 if (success) {
327 mCurrentInterval = stateTransition.minInterval;
328 }
329
330 success &= (stateTransition.enable == enabled);
331 postAsyncResultEventFatal(stateTransition.nanoappInstanceId, success,
332 stateTransition.enable,
333 stateTransition.minInterval,
334 errorCode, stateTransition.cookie);
335 mStateTransitions.pop();
336 }
337
338 while (!mStateTransitions.empty()) {
339 const auto& stateTransition = mStateTransitions.front();
340
341 size_t requestIndex;
342 bool hasRequest = nanoappHasRequest(
343 stateTransition.nanoappInstanceId, &requestIndex);
344
345 if (stateTransitionIsRequired(stateTransition.enable,
346 stateTransition.minInterval,
347 hasRequest, requestIndex)) {
348 if (controlPlatform(stateTransition.enable, stateTransition.minInterval,
349 Milliseconds(0))) {
350 break;
351 } else {
352 LOGE("Failed to enable a GNSS session for nanoapp instance %" PRIu32,
353 stateTransition.nanoappInstanceId);
354 postAsyncResultEventFatal(
355 stateTransition.nanoappInstanceId, false /* success */,
356 stateTransition.enable, stateTransition.minInterval,
357 CHRE_ERROR, stateTransition.cookie);
358 mStateTransitions.pop();
359 }
360 } else {
361 postAsyncResultEventFatal(
362 stateTransition.nanoappInstanceId, true /* success */,
363 stateTransition.enable, stateTransition.minInterval,
364 CHRE_ERROR_NONE, stateTransition.cookie);
365 mStateTransitions.pop();
366 }
367 }
368 }
369
freeReportEventCallback(uint16_t eventType,void * eventData)370 void GnssSession::freeReportEventCallback(uint16_t eventType, void *eventData) {
371 switch (eventType) {
372 case CHRE_EVENT_GNSS_LOCATION:
373 EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss
374 .releaseLocationEvent(
375 static_cast<chreGnssLocationEvent *>(eventData));
376 break;
377
378 case CHRE_EVENT_GNSS_DATA:
379 EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss
380 .releaseMeasurementDataEvent(
381 static_cast<chreGnssDataEvent *>(eventData));
382 break;
383
384 default:
385 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, eventType);
386 }
387 }
388
controlPlatform(bool enable,Milliseconds minInterval,Milliseconds)389 bool GnssSession::controlPlatform(
390 bool enable, Milliseconds minInterval, Milliseconds /* minTimeToNext */) {
391 bool success = false;
392
393 switch (mReportEventType) {
394 case CHRE_EVENT_GNSS_LOCATION:
395 // TODO: Provide support for min time to next report. It is currently sent
396 // to the platform as zero.
397 success = EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss
398 .controlLocationSession(enable, minInterval, Milliseconds(0));
399 break;
400
401 case CHRE_EVENT_GNSS_DATA:
402 success = EventLoopManagerSingleton::get()->getGnssManager().mPlatformGnss
403 .controlMeasurementSession(enable, minInterval);
404 break;
405
406 default:
407 CHRE_ASSERT_LOG(false, "Unhandled event type %" PRIu16, mReportEventType);
408 }
409 return success;
410 }
411
412 } // namespace chre
413