• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params, &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