xref: /aosp_15_r20/system/chre/apps/wifi_world/wifi_world.cc (revision 84e339476a462649f82315436d70fd732297a399)
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   } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_RANGING) {
212     uint64_t timeSinceRequest = chreGetTime() - gLastRangingTimeNs;
213     if (result->success) {
214       LOGI("Successfully requested WiFi ranging (response time %" PRIu64 " ms)",
215            timeSinceRequest / kOneMillisecondInNanoseconds);
216     } else {
217       gPendingRanging = false;
218       LOGE("Error requesting a WiFi ranging with %" PRIu8, result->errorCode);
219     }
220 
221     if (result->cookie != &kRangingCookie) {
222       LOGE("Ranging cookie mismatch");
223     }
224 
225   } else {
226     LOGE("Received invalid async result");
227   }
228 }
229 
prepareRanging(const chreWifiScanEvent * event)230 void prepareRanging(const chreWifiScanEvent *event) {
231   if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_RTT_RANGING) {
232     // Collect the first CHRE_WIFI_RANGING_LIST_MAX_LEN AP's that support the
233     // capability.
234     for (uint8_t i = 0; i < event->resultCount; i++) {
235       if (gTargetCount < CHRE_WIFI_RANGING_LIST_MAX_LEN &&
236           (event->results[i].flags &
237            CHRE_WIFI_SCAN_RESULT_FLAGS_IS_FTM_RESPONDER)) {
238         chreWifiRangingTargetFromScanResult(&event->results[i],
239                                             &gTargetList[gTargetCount++]);
240       }
241     }
242 
243     // Make ranging request only when all scan events are received.
244     if (!gPendingOnDemandScan) {
245       if (gTargetCount == 0 && event->resultCount == 0) {
246         LOGI("No AP to make ranging request to");
247       } else if (gTargetCount == 0) {
248         LOGI("No AP with RTT capability found");
249         // Adding one AP to exercise ranging API.
250         chreWifiRangingTargetFromScanResult(&event->results[0],
251                                             &gTargetList[gTargetCount++]);
252       }
253 
254       if (gTargetCount > 0) {
255         struct chreWifiRangingParams params = {
256             .targetListLen = gTargetCount,
257             .targetList = &gTargetList[0],
258         };
259 
260         gLastRangingTimeNs = chreGetTime();
261         if (!chreWifiRequestRangingAsync(&params, &kRangingCookie)) {
262           LOGE("Failed to request WiFi ranging");
263         } else {
264           gPendingRanging = true;
265         }
266         gTargetCount = 0;
267       }
268     }
269   }
270 }
271 
272 /**
273  * Handles a WiFi scan event.
274  *
275  * @param event a pointer to the details of the WiFi scan event.
276  */
handleWifiScanEvent(const chreWifiScanEvent * event)277 void handleWifiScanEvent(const chreWifiScanEvent *event) {
278   LOGI("Received Wifi scan event of type %" PRIu8 " index %" PRIu8
279        " with %" PRIu8 "/%" PRIu8 " results completed at %" PRIu64 "ns",
280        event->scanType, event->eventIndex, event->resultCount,
281        event->resultTotal, event->referenceTime);
282 
283   if (gPendingOnDemandScan) {
284     uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
285     LOGI("Time since scan request = %" PRIu64 " ms",
286          timeSinceRequest / kOneMillisecondInNanoseconds);
287 
288     if (event->scanType != gWifiScanParams.scanType) {
289       LOGE("Invalid scan event type (expected %" PRIu8 ", received %" PRIu8 ")",
290            gWifiScanParams.scanType, event->scanType);
291     }
292 
293     gScanResultAcc += event->resultCount;
294     if (gScanResultAcc >= event->resultTotal) {
295       gPendingOnDemandScan = false;
296       gScanResultAcc = 0;
297     }
298 
299     if (gEnableRanging) {
300       prepareRanging(event);
301     }
302   }
303 
304   for (uint8_t i = 0; i < event->resultCount; i++) {
305     const chreWifiScanResult &result = event->results[i];
306 #ifdef WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
307     chre::logChreWifiResult(result);
308 #else
309     chre::logChreWifiResult(result, true /* logSsidOnly */);
310 #endif
311   }
312 }
313 
handleWifiRangingEvent(const chreWifiRangingEvent * event)314 void handleWifiRangingEvent(const chreWifiRangingEvent *event) {
315   LOGI("Received Wifi ranging event with %" PRIu8 " results",
316        event->resultCount);
317 
318   if (!gPendingRanging) {
319     LOGE("WiFi ranging event not expected");
320   } else {
321     gPendingRanging = false;
322     for (uint8_t i = 0; i < event->resultCount; i++) {
323       logChreRangingResult(event->results[i]);
324     }
325   }
326 }
327 
328 /**
329  * Handles a timer event.
330  *
331  * @param eventData The cookie passed to the timer request.
332  */
handleTimerEvent(const void * eventData)333 void handleTimerEvent(const void *eventData) {
334   const uint32_t *timerHandle = static_cast<const uint32_t *>(eventData);
335   if (*timerHandle == gWifiScanTimerHandle) {
336     gWifiScanParams.scanType = gWifiScanTypes[gScanTypeIndex];
337     gWifiScanParams.maxScanAgeMs = 5000;  // 5 seconds
338     gWifiScanParams.frequencyListLen = 0;
339     gWifiScanParams.ssidListLen = 0;
340     gScanTypeIndex = (gScanTypeIndex + 1) % ARRAY_SIZE(gWifiScanTypes);
341 
342     if (chreWifiRequestScanAsync(&gWifiScanParams, &kOnDemandScanCookie)) {
343       LOGI("Requested a WiFi scan successfully");
344       gLastRequestTimeNs = chreGetTime();
345     } else {
346       LOGE("Failed to request a WiFi scan");
347     }
348   } else {
349     LOGE("Received invalid timer handle");
350   }
351 
352   requestDelayedWifiScan();
353 }
354 
355 }  // namespace
356 
nanoappStart()357 bool nanoappStart() {
358   LOGI("App started as instance %" PRIu32, chreGetInstanceId());
359 
360   gWifiCapabilities = chreWifiGetCapabilities();
361   LOGI("Detected WiFi support as: 0x%" PRIx32, gWifiCapabilities);
362 
363   if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_SCAN_MONITORING) {
364     if (chreWifiConfigureScanMonitorAsync(true, &kScanMonitoringCookie)) {
365       LOGI("Scan monitor enable request successful");
366     } else {
367       LOGE("Error sending scan monitoring request");
368     }
369   }
370 
371   requestDelayedWifiScan();
372   return true;
373 }
374 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)375 void nanoappHandleEvent(uint32_t senderInstanceId, uint16_t eventType,
376                         const void *eventData) {
377   UNUSED_VAR(senderInstanceId);
378 
379   switch (eventType) {
380     case CHRE_EVENT_WIFI_ASYNC_RESULT:
381       handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
382       break;
383     case CHRE_EVENT_WIFI_SCAN_RESULT:
384       handleWifiScanEvent(static_cast<const chreWifiScanEvent *>(eventData));
385       break;
386     case CHRE_EVENT_WIFI_RANGING_RESULT:
387       handleWifiRangingEvent(
388           static_cast<const chreWifiRangingEvent *>(eventData));
389       break;
390     case CHRE_EVENT_TIMER:
391       handleTimerEvent(eventData);
392       break;
393     default:
394       LOGW("Unhandled event type %" PRIu16, eventType);
395   }
396 }
397 
nanoappEnd()398 void nanoappEnd() {
399   LOGI("Wifi world app stopped");
400 }
401 
402 #ifdef CHRE_NANOAPP_INTERNAL
403 }  // anonymous namespace
404 }  // namespace chre
405 
406 #include "chre/platform/static_nanoapp_init.h"
407 #include "chre/util/nanoapp/app_id.h"
408 #include "chre/util/system/napp_permissions.h"
409 
410 CHRE_STATIC_NANOAPP_INIT(WifiWorld, chre::kWifiWorldAppId, 0,
411                          chre::NanoappPermissions::CHRE_PERMS_WIFI);
412 #endif  // CHRE_NANOAPP_INTERNAL
413