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(¶ms, &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