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