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