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 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
handleGnssAsyncResult(const chreAsyncResult * result)159 void handleGnssAsyncResult(const chreAsyncResult *result) {
160 bool validResult = true;
161 const char *action = nullptr;
162 const char *name;
163 bool *received;
164 const uint32_t *cookie;
165
166 switch (result->requestType) {
167 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START:
168 action = "start";
169 // fall through to CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP
170
171 case CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP:
172 if (action == nullptr) {
173 action = "stop";
174 }
175 name = "location";
176 received = &gLocationAsyncResultReceived;
177 cookie = &kLocationSessionCookie;
178 break;
179
180 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_START:
181 action = "start";
182 // fall through to CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP
183
184 case CHRE_GNSS_REQUEST_TYPE_MEASUREMENT_SESSION_STOP:
185 if (action == nullptr) {
186 action = "stop";
187 }
188 name = "measurement";
189 received = &gMeasurementAsyncResultReceived;
190 cookie = &kMeasurementSessionCookie;
191 break;
192
193 default:
194 LOGE("Received invalid async result %" PRIu8, result->requestType);
195 validResult = false;
196 break;
197 }
198
199 if (validResult) {
200 *received = true;
201 if (result->success) {
202 LOGI("GNSS %s %s success", name, action);
203 } else {
204 LOGE("GNSS %s %s failure: %" PRIu8, name, action, result->errorCode);
205 }
206
207 if (result->cookie != cookie) {
208 LOGE("GNSS %s session %s request cookie mismatch", name, action);
209 }
210 }
211 }
212
handleGnssLocationEvent(const chreGnssLocationEvent * event)213 void handleGnssLocationEvent(const chreGnssLocationEvent *event) {
214 LOGI("Received location: %" PRId32 ", %" PRId32, event->latitude_deg_e7,
215 event->longitude_deg_e7);
216 LOGI(" timestamp (ms): %" PRIu64, event->timestamp);
217 LOGI(" altitude (m): %f", event->altitude);
218 LOGI(" speed (m/s): %f", event->speed);
219 LOGI(" bearing (deg): %f", event->bearing);
220 LOGI(" accuracy: %f", event->accuracy);
221 LOGI(" flags: %" PRIx16, event->flags);
222 }
223
handleGnssDataEvent(const chreGnssDataEvent * event)224 void handleGnssDataEvent(const chreGnssDataEvent *event) {
225 LOGI("Received data: %" PRIu8 " measurements", event->measurement_count);
226
227 const struct chreGnssMeasurement *measurement = event->measurements;
228 for (uint8_t i = 0; i < event->measurement_count; i++) {
229 LOGI("%" PRIu8 ": const %" PRIu8 ", cn0 %f",
230 i, measurement->constellation, measurement->c_n0_dbhz);
231 measurement++;
232 }
233 }
234
nanoappStart()235 bool nanoappStart() {
236 LOGI("App started as instance %" PRIu32, chreGetInstanceId());
237
238 const char *gnssCapabilitiesStr;
239 uint32_t gnssCapabilities = chreGnssGetCapabilities();
240 switch (gnssCapabilities) {
241 case CHRE_GNSS_CAPABILITIES_LOCATION
242 | CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
243 gnssCapabilitiesStr = "LOCATION | MEASUREMENTS";
244 gLocationSupported = true;
245 gMeasurementSupported = true;
246 break;
247 case CHRE_GNSS_CAPABILITIES_LOCATION:
248 gnssCapabilitiesStr = "LOCATION";
249 gLocationSupported = true;
250 break;
251 case CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
252 gnssCapabilitiesStr = "MEASUREMENTS";
253 gMeasurementSupported = true;
254 break;
255 case CHRE_GNSS_CAPABILITIES_NONE:
256 gnssCapabilitiesStr = "NONE";
257 break;
258 default:
259 gnssCapabilitiesStr = "INVALID";
260 }
261
262 LOGI("Detected GNSS support as: %s (%" PRIu32 ")",
263 gnssCapabilitiesStr, gnssCapabilities);
264
265 if (gLocationSupported && kEnableLocationTest) {
266 makeLocationRequest();
267 }
268
269 if (gMeasurementSupported && kEnableMeasurementTest) {
270 makeMeasurementRequest();
271 }
272
273 return true;
274 }
275
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)276 void nanoappHandleEvent(uint32_t senderInstanceId,
277 uint16_t eventType,
278 const void *eventData) {
279 switch (eventType) {
280 case CHRE_EVENT_GNSS_ASYNC_RESULT:
281 handleGnssAsyncResult(static_cast<const chreAsyncResult *>(eventData));
282 break;
283 case CHRE_EVENT_GNSS_LOCATION:
284 handleGnssLocationEvent(
285 static_cast<const chreGnssLocationEvent *>(eventData));
286 break;
287 case CHRE_EVENT_GNSS_DATA:
288 handleGnssDataEvent(static_cast<const chreGnssDataEvent *>(eventData));
289 break;
290 case CHRE_EVENT_TIMER:
291 handleTimerEvent(eventData);
292 break;
293 default:
294 LOGW("Unhandled event type %" PRIu16, eventType);
295 }
296 }
297
nanoappEnd()298 void nanoappEnd() {
299 LOGI("Stopped");
300 }
301
302 #ifdef CHRE_NANOAPP_INTERNAL
303 } // anonymous namespace
304 } // namespace chre
305
306 #include "chre/util/nanoapp/app_id.h"
307 #include "chre/platform/static_nanoapp_init.h"
308
309 CHRE_STATIC_NANOAPP_INIT(GnssWorld, chre::kGnssWorldAppId, 0);
310 #endif // CHRE_NANOAPP_INTERNAL
311