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.h>
18 #include <cinttypes>
19
20 #include "chre/util/macros.h"
21 #include "chre/util/nanoapp/log.h"
22 #include "chre/util/time.h"
23
24 #define LOG_TAG "[GnssWorld]"
25
26 #ifdef CHRE_NANOAPP_INTERNAL
27 namespace chre {
28 namespace {
29 #endif // CHRE_NANOAPP_INTERNAL
30
31 //! Control which test(s) to run
32 constexpr bool kEnableLocationTest = true;
33 constexpr bool kEnableMeasurementTest = true;
34
35 //! A dummy cookie to pass into the session async and timer request.
36 const uint32_t kLocationSessionCookie = 0x1337;
37 const uint32_t kMeasurementSessionCookie = 0xdaad;
38
39 //! The minimum time to the next fix for a location.
40 constexpr chre::Milliseconds kLocationMinTimeToNextFix(0);
41
42 //! The interval in seconds between updates.
43 const uint32_t kReportIntervals[] = {
44 30,
45 15,
46 30,
47 15,
48 0,
49 10,
50 };
51
52 //! Whether a specific Gnss capability is supported by the platform
53 bool gLocationSupported = false;
54 bool gMeasurementSupported = false;
55
56 uint32_t gLocationTimerHandle;
57 uint32_t gLocationTimerCount = 0;
58
59 uint32_t gMeasurementTimerHandle;
60 uint32_t gMeasurementTimerCount = 0;
61
62 //! Whether an async result has been received.
63 bool gLocationAsyncResultReceived = false;
64 bool gMeasurementAsyncResultReceived = false;
65
makeLocationRequest()66 void makeLocationRequest() {
67 uint32_t interval = kReportIntervals[gLocationTimerCount++];
68 LOGI("Modifying location update interval to %" PRIu32 " sec", interval);
69
70 if (interval > 0) {
71 if (chreGnssLocationSessionStartAsync(
72 interval * 1000,
73 kLocationMinTimeToNextFix.getMilliseconds(),
74 &kLocationSessionCookie)) {
75 LOGI("Location session start request sent");
76 } else {
77 LOGE("Error sending location session start request");
78 }
79 } else {
80 if (chreGnssLocationSessionStopAsync(
81 &kLocationSessionCookie)) {
82 LOGI("Location session stop request sent");
83 } else {
84 LOGE("Error sending location session stop request");
85 }
86 }
87
88 // set a timer to verify reception of async result.
89 gLocationTimerHandle = chreTimerSet(
90 CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS, /* 5 sec in CHRE 1.1 */
91 &kLocationSessionCookie, true /* oneShot */);
92 }
93
makeMeasurementRequest()94 void makeMeasurementRequest() {
95 uint32_t interval = kReportIntervals[gMeasurementTimerCount++];
96 LOGI("Modifying measurement update interval to %" PRIu32 " sec", interval);
97
98 if (interval > 0) {
99 if (chreGnssMeasurementSessionStartAsync(
100 interval * 1000, &kMeasurementSessionCookie)) {
101 LOGI("Measurement session start request sent");
102 } else {
103 LOGE("Error sending measurement session start request");
104 }
105 } else {
106 if (chreGnssMeasurementSessionStopAsync(
107 &kMeasurementSessionCookie)) {
108 LOGI("Measurement session stop request sent");
109 } else {
110 LOGE("Error sending measurement session stop request");
111 }
112 }
113
114 // set a timer to verify reception of async result.
115 gMeasurementTimerHandle = chreTimerSet(
116 CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS, /* 5 sec in CHRE 1.1 */
117 &kMeasurementSessionCookie, true /* oneShot */);
118 }
119
handleTimerEvent(const void * eventData)120 void handleTimerEvent(const void *eventData) {
121 bool validData = true;
122
123 bool supported;
124 const char *name;
125 uint32_t timerCount;
126 bool *asyncResultReceived;
127 void (*makeRequest)();
128
129 if (eventData == &kLocationSessionCookie) {
130 supported = gLocationSupported;
131 name = "location";
132 timerCount = gLocationTimerCount;
133 asyncResultReceived = &gLocationAsyncResultReceived;
134 makeRequest = makeLocationRequest;
135 } else if (eventData == &kMeasurementSessionCookie) {
136 supported = gMeasurementSupported;
137 name = "measurement";
138 timerCount = gMeasurementTimerCount;
139 asyncResultReceived = &gMeasurementAsyncResultReceived;
140 makeRequest = makeMeasurementRequest;
141 } else {
142 validData = false;
143 LOGE("Invalid timer cookie");
144 }
145
146 if (validData) {
147 LOGI("%s timer event received, count %" PRIu32, name, timerCount);
148 if (!*asyncResultReceived) {
149 LOGE("%s async result not received!", name);
150 }
151 *asyncResultReceived = false;
152
153 if (supported && timerCount < ARRAY_SIZE(kReportIntervals)) {
154 makeRequest();
155 }
156 }
157 }
158
getNameStringFromRequestType(uint8_t requestType)159 const char *getNameStringFromRequestType(uint8_t requestType) {
160 switch (requestType) {
161 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START:
162 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP:
163 return "location";
164 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START:
165 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP:
166 return "measurement";
167 default:
168 return nullptr;
169 }
170 }
171
getActionStringFromRequestType(uint8_t requestType)172 const char *getActionStringFromRequestType(uint8_t requestType) {
173 switch (requestType) {
174 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START:
175 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START:
176 return "start";
177 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP:
178 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP:
179 return "stop";
180 default:
181 return nullptr;
182 }
183 }
184
handleGnssAsyncResult(const chreAsyncResult * result)185 void handleGnssAsyncResult(const chreAsyncResult *result) {
186 const char *name = getNameStringFromRequestType(result->requestType);
187 const char *action = getActionStringFromRequestType(result->requestType);
188 bool *received = nullptr;
189 const uint32_t *cookie;
190
191 switch (result->requestType) {
192 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START:
193 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP:
194 received = &gLocationAsyncResultReceived;
195 cookie = &kLocationSessionCookie;
196 break;
197
198 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START:
199 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP:
200 received = &gMeasurementAsyncResultReceived;
201 cookie = &kMeasurementSessionCookie;
202 break;
203
204 default:
205 LOGE("Received invalid async result %" PRIu8, result->requestType);
206 break;
207 }
208
209 if (received != nullptr) {
210 *received = true;
211 if (result->success) {
212 LOGI("GNSS %s %s success", name, action);
213 } else {
214 LOGE("GNSS %s %s failure: %" PRIu8, name, action, result->errorCode);
215 }
216
217 if (result->cookie != cookie) {
218 LOGE("GNSS %s session %s request cookie mismatch", name, action);
219 }
220 }
221 }
222
handleGnssLocationEvent(const chreGnssLocationEvent * event)223 void handleGnssLocationEvent(const chreGnssLocationEvent *event) {
224 LOGI("Received location: %" PRId32 ", %" PRId32, event->latitude_deg_e7,
225 event->longitude_deg_e7);
226 LOGI(" timestamp (ms): %" PRIu64, event->timestamp);
227 LOGI(" altitude (m): %f", event->altitude);
228 LOGI(" speed (m/s): %f", event->speed);
229 LOGI(" bearing (deg): %f", event->bearing);
230 LOGI(" accuracy: %f", event->accuracy);
231 LOGI(" flags: %" PRIx16, event->flags);
232 LOGI(" altitude_accuracy: %f", event->altitude_accuracy);
233 LOGI(" speed_accuracy: %f", event->speed_accuracy);
234 LOGI(" bearing_accuracy: %f", event->bearing_accuracy);
235 }
236
handleGnssDataEvent(const chreGnssDataEvent * event)237 void handleGnssDataEvent(const chreGnssDataEvent *event) {
238 LOGI("Received data: %" PRIu8 " measurements", event->measurement_count);
239
240 const struct chreGnssMeasurement *measurement = event->measurements;
241 for (uint8_t i = 0; i < event->measurement_count; i++) {
242 LOGI("%" PRIu8 ": const %" PRIu8 ", cn0 %f",
243 i, measurement->constellation, measurement->c_n0_dbhz);
244 measurement++;
245 }
246 }
247
nanoappStart()248 bool nanoappStart() {
249 LOGI("App started as instance %" PRIu32, chreGetInstanceId());
250
251 const char *gnssCapabilitiesStr;
252 uint32_t gnssCapabilities = chreGnssGetCapabilities();
253 switch (gnssCapabilities) {
254 case CHRE_GNSS_CAPABILITIES_LOCATION
255 | CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
256 gnssCapabilitiesStr = "LOCATION | MEASUREMENTS";
257 gLocationSupported = true;
258 gMeasurementSupported = true;
259 break;
260 case CHRE_GNSS_CAPABILITIES_LOCATION:
261 gnssCapabilitiesStr = "LOCATION";
262 gLocationSupported = true;
263 break;
264 case CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
265 gnssCapabilitiesStr = "MEASUREMENTS";
266 gMeasurementSupported = true;
267 break;
268 case CHRE_GNSS_CAPABILITIES_NONE:
269 gnssCapabilitiesStr = "NONE";
270 break;
271 default:
272 gnssCapabilitiesStr = "INVALID";
273 }
274
275 LOGI("Detected GNSS support as: %s (%" PRIu32 ")",
276 gnssCapabilitiesStr, gnssCapabilities);
277
278 if (gLocationSupported && kEnableLocationTest) {
279 makeLocationRequest();
280 }
281
282 if (gMeasurementSupported && kEnableMeasurementTest) {
283 makeMeasurementRequest();
284 }
285
286 return true;
287 }
288
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)289 void nanoappHandleEvent(uint32_t senderInstanceId,
290 uint16_t eventType,
291 const void *eventData) {
292 switch (eventType) {
293 case CHRE_EVENT_GNSS_ASYNC_RESULT:
294 handleGnssAsyncResult(static_cast<const chreAsyncResult *>(eventData));
295 break;
296 case CHRE_EVENT_GNSS_LOCATION:
297 handleGnssLocationEvent(
298 static_cast<const chreGnssLocationEvent *>(eventData));
299 break;
300 case CHRE_EVENT_GNSS_DATA:
301 handleGnssDataEvent(static_cast<const chreGnssDataEvent *>(eventData));
302 break;
303 case CHRE_EVENT_TIMER:
304 handleTimerEvent(eventData);
305 break;
306 default:
307 LOGW("Unhandled event type %" PRIu16, eventType);
308 }
309 }
310
nanoappEnd()311 void nanoappEnd() {
312 LOGI("Stopped");
313 }
314
315 #ifdef CHRE_NANOAPP_INTERNAL
316 } // anonymous namespace
317 } // namespace chre
318
319 #include "chre/util/nanoapp/app_id.h"
320 #include "chre/platform/static_nanoapp_init.h"
321
322 CHRE_STATIC_NANOAPP_INIT(GnssWorld, chre::kGnssWorldAppId, 0);
323 #endif // CHRE_NANOAPP_INTERNAL
324