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