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(&params, &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