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
27namespace chre {
28namespace {
29#endif  // CHRE_NANOAPP_INTERNAL
30
31//! A dummy cookie to pass into the location session async request.
32const uint32_t kLocationSessionCookie = 0x1337;
33
34//! The minimum time to the next fix for a location.
35constexpr Milliseconds kLocationMinTimeToNextFix(0);
36
37//! The interval in seconds between location updates.
38const uint32_t kLocationIntervals[] = {
39  30,
40  15,
41  30,
42  15,
43  0,
44  10,
45};
46
47//! Whether Gnss Location capability is supported by the platform
48bool gLocationSupported = false;
49
50uint32_t gTimerHandle;
51uint32_t gTimerCount = 0;
52
53//! Whether an async result has been received.
54bool gAsyncResultReceived = false;
55
56void makeLocationRequest() {
57  uint32_t interval = kLocationIntervals[gTimerCount++];
58  LOGI("Modifying location update interval to %" PRIu32 " sec", interval);
59
60  if (interval > 0) {
61    if (chreGnssLocationSessionStartAsync(
62          interval * 1000,
63          kLocationMinTimeToNextFix.getMilliseconds(),
64          &kLocationSessionCookie)) {
65      LOGI("Location session start request sent");
66    } else {
67      LOGE("Error sending location session start request");
68    }
69  } else {
70    if (chreGnssLocationSessionStopAsync(
71          &kLocationSessionCookie)) {
72      LOGI("Location session stop request sent");
73    } else {
74      LOGE("Error sending location session stop request");
75    }
76  }
77
78  // set a timer to verify reception of async result.
79  gTimerHandle = chreTimerSet(
80      CHRE_GNSS_ASYNC_RESULT_TIMEOUT_NS, /* 5 sec in CHRE 1.1 */
81      nullptr /* data */, true /* oneShot */);
82}
83
84void handleTimerEvent(const void *eventData) {
85  LOGI("Timer event received, count %d", gTimerCount);
86  if (!gAsyncResultReceived) {
87    LOGE("Async result not received!");
88  }
89  gAsyncResultReceived = false;
90
91  if (gLocationSupported && gTimerCount < ARRAY_SIZE(kLocationIntervals)) {
92    makeLocationRequest();
93  }
94}
95
96void handleGnssAsyncResult(const chreAsyncResult *result) {
97  if (result->requestType == CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_START) {
98    if (result->success) {
99      LOGI("Successfully requested a GNSS location session");
100      gAsyncResultReceived = true;
101    } else {
102      LOGE("Error requesting GNSS scan monitoring with %" PRIu8,
103           result->errorCode);
104    }
105
106    if (result->cookie != &kLocationSessionCookie) {
107      LOGE("Location session start request cookie mismatch");
108    }
109  } else if (result->requestType
110             == CHRE_GNSS_REQUEST_TYPE_LOCATION_SESSION_STOP) {
111    if (result->success) {
112      LOGI("Successfully stopped a GNSS location session");
113      gAsyncResultReceived = true;
114    } else {
115      LOGE("Error stoppinging GNSS scan monitoring with %" PRIu8,
116           result->errorCode);
117    }
118
119    if (result->cookie != &kLocationSessionCookie) {
120      LOGE("Location session stop request cookie mismatch");
121    }
122  } else {
123    LOGE("Received invalid async result %" PRIu8, result->requestType);
124  }
125}
126
127void handleGnssLocationEvent(const chreGnssLocationEvent *event) {
128  LOGI("Received location: %" PRId32 ", %" PRId32, event->latitude_deg_e7,
129       event->longitude_deg_e7);
130  LOGI("  timestamp (ms): %" PRIu64, event->timestamp);
131  LOGI("  altitude (m): %f", event->altitude);
132  LOGI("  speed (m/s): %f", event->speed);
133  LOGI("  bearing (deg): %f", event->bearing);
134  LOGI("  accuracy: %f", event->accuracy);
135  LOGI("  flags: %" PRIx16, event->flags);
136}
137
138bool nanoappStart() {
139  LOGI("App started as instance %" PRIu32, chreGetInstanceId());
140
141  const char *gnssCapabilitiesStr;
142  uint32_t gnssCapabilities = chreGnssGetCapabilities();
143  switch (gnssCapabilities) {
144    case CHRE_GNSS_CAPABILITIES_LOCATION
145        | CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
146      gnssCapabilitiesStr = "LOCATION | MEASUREMENTS";
147      gLocationSupported = true;
148      break;
149    case CHRE_GNSS_CAPABILITIES_LOCATION:
150      gnssCapabilitiesStr = "LOCATION";
151      gLocationSupported = true;
152      break;
153    case CHRE_GNSS_CAPABILITIES_MEASUREMENTS:
154      gnssCapabilitiesStr = "MEASUREMENTS";
155      break;
156    case CHRE_GNSS_CAPABILITIES_NONE:
157      gnssCapabilitiesStr = "NONE";
158      break;
159    default:
160      gnssCapabilitiesStr = "INVALID";
161  }
162
163  LOGI("Detected GNSS support as: %s (%" PRIu32 ")",
164       gnssCapabilitiesStr, gnssCapabilities);
165
166  if (gLocationSupported) {
167    makeLocationRequest();
168  }
169
170  return true;
171}
172
173void nanoappHandleEvent(uint32_t senderInstanceId,
174                        uint16_t eventType,
175                        const void *eventData) {
176  switch (eventType) {
177    case CHRE_EVENT_GNSS_ASYNC_RESULT:
178      handleGnssAsyncResult(static_cast<const chreAsyncResult *>(eventData));
179      break;
180    case CHRE_EVENT_GNSS_LOCATION:
181      handleGnssLocationEvent(
182          static_cast<const chreGnssLocationEvent *>(eventData));
183      break;
184    case CHRE_EVENT_TIMER:
185      handleTimerEvent(eventData);
186      break;
187    default:
188      LOGW("Unhandled event type %" PRIu16, eventType);
189  }
190}
191
192void nanoappEnd() {
193  LOGI("Stopped");
194}
195
196#ifdef CHRE_NANOAPP_INTERNAL
197}  // anonymous namespace
198}  // namespace chre
199
200#include "chre/util/nanoapp/app_id.h"
201#include "chre/platform/static_nanoapp_init.h"
202
203CHRE_STATIC_NANOAPP_INIT(GnssWorld, chre::kGnssWorldAppId, 0);
204#endif  // CHRE_NANOAPP_INTERNAL
205