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 #include <cmath>
20
21 #include "chre/util/macros.h"
22 #include "chre/util/nanoapp/log.h"
23 #include "chre/util/nanoapp/wifi.h"
24 #include "chre/util/time.h"
25
26 using chre::kOneMillisecondInNanoseconds;
27 using chre::Nanoseconds;
28 using chre::Seconds;
29
30 #define LOG_TAG "[WifiWorld]"
31
32 //#define WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
33
34 #ifdef CHRE_NANOAPP_INTERNAL
35 namespace chre {
36 namespace {
37 #endif // CHRE_NANOAPP_INTERNAL
38
39 namespace {
40
41 //! A fake/unused cookie to pass into the configure scan monitoring async
42 //! request.
43 constexpr uint32_t kScanMonitoringCookie = 0x1337;
44
45 //! A fake/unused cookie to pass into on-demand scan async request.
46 constexpr uint32_t kOnDemandScanCookie = 0xcafe;
47
48 //! A fake/unused cookie to pass into ranging async request.
49 constexpr uint32_t kRangingCookie = 0xbeef;
50
51 //! The interval for on-demand wifi scans.
52 constexpr Nanoseconds kWifiScanInterval = Nanoseconds(Seconds(10));
53
54 //! A handle for the cyclic timer to request periodic on-demand wifi-scans.
55 uint32_t gWifiScanTimerHandle;
56
57 //! A global instance of wifi capabilities to use when reqeuesting wifi
58 //! functionality. This is populated at startup.
59 uint32_t gWifiCapabilities;
60
61 //! The last time in nanoseconds a wifi scan request was sucessfully made.
62 uint64_t gLastRequestTimeNs = 0;
63
64 //! True if CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN mode is requested.
65 bool gPendingOnDemandScan = false;
66
67 //! Accumulating count of the scan request results so far.
68 uint32_t gScanResultAcc = 0;
69
70 //! The currently requested on-demand wifi scan parameters.
71 chreWifiScanParams gWifiScanParams = {};
72
73 //! The sequence of on-demand wifi scan types to request for.
74 constexpr chreWifiScanType gWifiScanTypes[] = {
75 CHRE_WIFI_SCAN_TYPE_ACTIVE, CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS,
76 CHRE_WIFI_SCAN_TYPE_PASSIVE};
77
78 //! The index of the next wifi scan type to request for.
79 uint8_t gScanTypeIndex = 0;
80
81 //! Whether to enable WiFi RTT ranging requests.
82 bool gEnableRanging = true;
83
84 //! The number of targets to make ranging request for.
85 uint8_t gTargetCount = 0;
86
87 //! The list of ranging targets.
88 chreWifiRangingTarget gTargetList[CHRE_WIFI_RANGING_LIST_MAX_LEN];
89
90 //! TIme last ranging request was made.
91 uint64_t gLastRangingTimeNs = 0;
92
93 //! Whether the app is awaiting any ranging event.
94 bool gPendingRanging = false;
95
96 /**
97 * Logs a CHRE WiFi ranging result.
98 *
99 * @param result the ranging result to log.
100 */
logChreRangingResult(const chreWifiRangingResult & result)101 void logChreRangingResult(const chreWifiRangingResult &result) {
102 const char *bssidStr = "<non-printable>";
103 char bssidBuffer[chre::kBssidStrLen];
104 if (chre::parseBssidToStr(result.macAddress, bssidBuffer,
105 sizeof(bssidBuffer))) {
106 bssidStr = bssidBuffer;
107 }
108 LOGI("BSSID %s", bssidStr);
109 LOGI(" age: %" PRIu64 " ms",
110 (chreGetTime() - result.timestamp) / kOneMillisecondInNanoseconds);
111
112 if (result.status != CHRE_WIFI_RANGING_STATUS_SUCCESS) {
113 LOGE(" ranging failed");
114 } else {
115 LOGI(" rssi: %" PRId8 " dBm", result.rssi);
116 LOGI(" distance: %" PRIu32 " mm", result.distance);
117 LOGI(" distanceStdDev: %" PRIu32 " mm", result.distanceStdDev);
118
119 if (result.flags & CHRE_WIFI_RTT_RESULT_HAS_LCI) {
120 const chreWifiRangingResult::chreWifiLci lci = result.lci;
121 LOGI(" latitude: 0x%" PRIx64 ", %f degs", lci.latitude,
122 static_cast<float>(lci.latitude) / static_cast<float>(1 << 25));
123 LOGI(" longitude: 0x%" PRIx64 ", %f degs", lci.longitude,
124 static_cast<float>(lci.longitude) / static_cast<float>(1 << 25));
125
126 float altitude =
127 static_cast<float>(lci.altitude) / static_cast<float>(1 << 8);
128 if (lci.altitudeType == CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN) {
129 LOGI(" altitude: unknown");
130 } else if (lci.altitudeType == CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS) {
131 LOGI(" altitude: 0x%" PRIx32 ", %f m", lci.altitude, altitude);
132 } else if (lci.altitudeType == CHRE_WIFI_LCI_ALTITUDE_TYPE_FLOORS) {
133 LOGI(" altitude: 0x%" PRIx32 ", %f floors", lci.altitude, altitude);
134 } else {
135 LOGE(" altitude: undefined");
136 }
137
138 if (lci.latitudeUncertainty == CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN) {
139 LOGI(" latitude uncertainty: unknown");
140 } else {
141 LOGI(" latitude uncertainty: %f degs",
142 powf(2, 8 - lci.latitudeUncertainty));
143 }
144 if (lci.longitudeUncertainty == CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN) {
145 LOGI(" longitude uncertainty: unknown");
146 } else {
147 LOGI(" longitude uncertainty: %f degs",
148 powf(2, 8 - lci.longitudeUncertainty));
149 }
150 if (lci.altitudeUncertainty == CHRE_WIFI_LCI_UNCERTAINTY_UNKNOWN ||
151 lci.altitudeType != CHRE_WIFI_LCI_ALTITUDE_TYPE_METERS) {
152 LOGI(" altitude uncertainty: unknown");
153 } else {
154 LOGI(" altitude uncertainty: %f m",
155 powf(2, 21 - lci.altitudeUncertainty));
156 }
157 }
158 }
159 }
160
161 /**
162 * Requests a delayed WiFi scan using a one-shot timer. The interval is
163 * specified as a constant at the top of this file.
164 */
requestDelayedWifiScan()165 void requestDelayedWifiScan() {
166 if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) {
167 // Schedule a timer to send an active WiFi scan.
168 gWifiScanTimerHandle =
169 chreTimerSet(kWifiScanInterval.toRawNanoseconds(),
170 &gWifiScanTimerHandle /* data */, true /* oneShot */);
171 if (gWifiScanTimerHandle == CHRE_TIMER_INVALID) {
172 LOGE("Failed to set timer for delayed WiFi scan");
173 } else {
174 LOGI("Set a timer to request a WiFi scan");
175 }
176 }
177 }
178
179 /**
180 * Handles the result of an asynchronous request for a WiFi resource.
181 *
182 * @param result a pointer to the event structure containing the result of the
183 * request.
184 */
handleWifiAsyncResult(const chreAsyncResult * result)185 void handleWifiAsyncResult(const chreAsyncResult *result) {
186 if (result->requestType == CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR) {
187 if (result->success) {
188 LOGI("Successfully requested WiFi scan monitoring");
189 } else {
190 LOGE("Error requesting WiFi scan monitoring with %" PRIu8,
191 result->errorCode);
192 }
193
194 if (result->cookie != &kScanMonitoringCookie) {
195 LOGE("Scan monitoring request cookie mismatch");
196 }
197 } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN) {
198 uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
199 if (result->success) {
200 LOGI(
201 "Successfully requested an on-demand WiFi scan (response time "
202 "%" PRIu64 " ms)",
203 timeSinceRequest / kOneMillisecondInNanoseconds);
204 gPendingOnDemandScan = true;
205 } else {
206 LOGE("Error requesting an on-demand WiFi scan with %" PRIu8,
207 result->errorCode);
208 }
209
210 if (result->cookie != &kOnDemandScanCookie) {
211 LOGE("On-demand scan cookie mismatch");
212 }
213
214 requestDelayedWifiScan();
215 } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_RANGING) {
216 uint64_t timeSinceRequest = chreGetTime() - gLastRangingTimeNs;
217 if (result->success) {
218 LOGI("Successfully requested WiFi ranging (response time %" PRIu64 " ms)",
219 timeSinceRequest / kOneMillisecondInNanoseconds);
220 } else {
221 gPendingRanging = false;
222 LOGE("Error requesting a WiFi ranging with %" PRIu8, result->errorCode);
223 }
224
225 if (result->cookie != &kRangingCookie) {
226 LOGE("Ranging cookie mismatch");
227 }
228
229 } else {
230 LOGE("Received invalid async result");
231 }
232 }
233
prepareRanging(const chreWifiScanEvent * event)234 void prepareRanging(const chreWifiScanEvent *event) {
235 if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_RTT_RANGING) {
236 // Collect the first CHRE_WIFI_RANGING_LIST_MAX_LEN AP's that support the
237 // capability.
238 for (uint8_t i = 0; i < event->resultCount; i++) {
239 if (gTargetCount < CHRE_WIFI_RANGING_LIST_MAX_LEN &&
240 (event->results[i].flags &
241 CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER)) {
242 chreWifiRangingTargetFromScanResult(&event->results[i],
243 &gTargetList[gTargetCount++]);
244 }
245 }
246
247 // Make ranging request only when all scan events are received.
248 if (!gPendingOnDemandScan) {
249 if (gTargetCount == 0 && event->resultCount == 0) {
250 LOGI("No AP to make ranging request to");
251 } else if (gTargetCount == 0) {
252 LOGI("No AP with RTT capability found");
253 // Adding one AP to exercise ranging API.
254 chreWifiRangingTargetFromScanResult(&event->results[0],
255 &gTargetList[gTargetCount++]);
256 }
257
258 if (gTargetCount > 0) {
259 struct chreWifiRangingParams params = {
260 .targetListLen = gTargetCount,
261 .targetList = &gTargetList[0],
262 };
263
264 gLastRangingTimeNs = chreGetTime();
265 if (!chreWifiRequestRangingAsync(¶ms, &kRangingCookie)) {
266 LOGE("Failed to request WiFi ranging");
267 } else {
268 gPendingRanging = true;
269 }
270 gTargetCount = 0;
271 }
272 }
273 }
274 }
275
276 /**
277 * Handles a WiFi scan event.
278 *
279 * @param event a pointer to the details of the WiFi scan event.
280 */
handleWifiScanEvent(const chreWifiScanEvent * event)281 void handleWifiScanEvent(const chreWifiScanEvent *event) {
282 LOGI("Received Wifi scan event of type %" PRIu8 " with %" PRIu8
283 " results at %" PRIu64 "ns",
284 event->scanType, event->resultCount, event->referenceTime);
285
286 if (gPendingOnDemandScan) {
287 uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
288 LOGI("Time since scan request = %" PRIu64 " ms",
289 timeSinceRequest / kOneMillisecondInNanoseconds);
290
291 if (event->scanType != gWifiScanParams.scanType) {
292 LOGE("Invalid scan event type (expected %" PRIu8 ", received %" PRIu8 ")",
293 gWifiScanParams.scanType, event->scanType);
294 }
295
296 gScanResultAcc += event->resultCount;
297 if (gScanResultAcc >= event->resultTotal) {
298 gPendingOnDemandScan = false;
299 gScanResultAcc = 0;
300 }
301
302 if (gEnableRanging) {
303 prepareRanging(event);
304 }
305 }
306
307 for (uint8_t i = 0; i < event->resultCount; i++) {
308 const chreWifiScanResult &result = event->results[i];
309 #ifdef WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
310 chre::logChreWifiResult(result);
311 #else
312 chre::logChreWifiResult(result, true /* logSsidOnly */);
313 #endif
314 }
315 }
316
handleWifiRangingEvent(const chreWifiRangingEvent * event)317 void handleWifiRangingEvent(const chreWifiRangingEvent *event) {
318 LOGI("Received Wifi ranging event with %" PRIu8 " results",
319 event->resultCount);
320
321 if (!gPendingRanging) {
322 LOGE("WiFi ranging event not expected");
323 } else {
324 gPendingRanging = false;
325 for (uint8_t i = 0; i < event->resultCount; i++) {
326 logChreRangingResult(event->results[i]);
327 }
328 }
329 }
330
331 /**
332 * Handles a timer event.
333 *
334 * @param eventData The cookie passed to the timer request.
335 */
handleTimerEvent(const void * eventData)336 void handleTimerEvent(const void *eventData) {
337 const uint32_t *timerHandle = static_cast<const uint32_t *>(eventData);
338 if (*timerHandle == gWifiScanTimerHandle) {
339 gWifiScanParams.scanType = gWifiScanTypes[gScanTypeIndex];
340 gWifiScanParams.maxScanAgeMs = 5000; // 5 seconds
341 gWifiScanParams.frequencyListLen = 0;
342 gWifiScanParams.ssidListLen = 0;
343 gScanTypeIndex = (gScanTypeIndex + 1) % ARRAY_SIZE(gWifiScanTypes);
344
345 if (chreWifiRequestScanAsync(&gWifiScanParams, &kOnDemandScanCookie)) {
346 LOGI("Requested a WiFi scan successfully");
347 gLastRequestTimeNs = chreGetTime();
348 } else {
349 LOGE("Failed to request a WiFi scan");
350 }
351 } else {
352 LOGE("Received invalid timer handle");
353 }
354 }
355
356 } // namespace
357
nanoappStart()358 bool nanoappStart() {
359 LOGI("App started as instance %" PRIu32, chreGetInstanceId());
360
361 gWifiCapabilities = chreWifiGetCapabilities();
362 LOGI("Detected WiFi support as: 0x%" PRIx32, gWifiCapabilities);
363
364 if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_SCAN_MONITORING) {
365 if (chreWifiConfigureScanMonitorAsync(true, &kScanMonitoringCookie)) {
366 LOGI("Scan monitor enable request successful");
367 } else {
368 LOGE("Error sending scan monitoring request");
369 }
370 }
371
372 requestDelayedWifiScan();
373 return true;
374 }
375
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)376 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
377 const void *eventData) {
378 switch (eventType) {
379 case CHRE_EVENT_WIFI_ASYNC_RESULT:
380 handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
381 break;
382 case CHRE_EVENT_WIFI_SCAN_RESULT:
383 handleWifiScanEvent(static_cast<const chreWifiScanEvent *>(eventData));
384 break;
385 case CHRE_EVENT_WIFI_RANGING_RESULT:
386 handleWifiRangingEvent(
387 static_cast<const chreWifiRangingEvent *>(eventData));
388 break;
389 case CHRE_EVENT_TIMER:
390 handleTimerEvent(eventData);
391 break;
392 default:
393 LOGW("Unhandled event type %" PRIu16, eventType);
394 }
395 }
396
nanoappEnd()397 void nanoappEnd() {
398 LOGI("Wifi world app stopped");
399 }
400
401 #ifdef CHRE_NANOAPP_INTERNAL
402 } // anonymous namespace
403 } // namespace chre
404
405 #include "chre/platform/static_nanoapp_init.h"
406 #include "chre/util/nanoapp/app_id.h"
407 #include "chre/util/system/napp_permissions.h"
408
409 CHRE_STATIC_NANOAPP_INIT(WifiWorld, chre::kWifiWorldAppId, 0,
410 chre::NanoappPermissions::CHRE_PERMS_WIFI);
411 #endif // CHRE_NANOAPP_INTERNAL
412