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