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