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