• 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 <chre.h>
18 #include <cinttypes>
19 
20 #include "chre/util/macros.h"
21 #include "chre/util/nanoapp/log.h"
22 #include "chre/util/time.h"
23 #include "chre/util/nanoapp/wifi.h"
24 
25 using chre::kOneMillisecondInNanoseconds;
26 using chre::Nanoseconds;
27 using chre::Seconds;
28 
29 #define LOG_TAG "[WifiWorld]"
30 
31 //#define WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
32 
33 #ifdef CHRE_NANOAPP_INTERNAL
34 namespace chre {
35 namespace {
36 #endif  // CHRE_NANOAPP_INTERNAL
37 
38 namespace {
39 
40 //! A dummy cookie to pass into the configure scan monitoring async request.
41 constexpr uint32_t kScanMonitoringCookie = 0x1337;
42 
43 //! A dummy cookie to pass into request scan async.
44 constexpr uint32_t kOnDemandScanCookie = 0xcafe;
45 
46 //! The interval for on-demand wifi scans.
47 constexpr Nanoseconds kWifiScanInterval = Nanoseconds(Seconds(10));
48 
49 //! A handle for the cyclic timer to request periodic on-demand wifi-scans.
50 uint32_t gWifiScanTimerHandle;
51 
52 //! A global instance of wifi capabilities to use when reqeuesting wifi
53 //! functionality. This is populated at startup.
54 uint32_t gWifiCapabilities;
55 
56 //! The last time in nanoseconds a wifi scan request was sucessfully made.
57 uint64_t gLastRequestTimeNs = 0;
58 
59 //! True if CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN mode is requested.
60 bool gPendingOnDemandScan = false;
61 
62 //! Accumulating count of the scan request results so far.
63 uint32_t gScanResultAcc = 0;
64 
65 //! The currently requested on-demand wifi scan parameters.
66 chreWifiScanParams gWifiScanParams = {};
67 
68 //! The sequence of on-demand wifi scan types to request for.
69 constexpr chreWifiScanType gWifiScanTypes[] = {
70   CHRE_WIFI_SCAN_TYPE_ACTIVE,
71   CHRE_WIFI_SCAN_TYPE_ACTIVE_PLUS_PASSIVE_DFS,
72   CHRE_WIFI_SCAN_TYPE_PASSIVE
73 };
74 
75 //! The index of the next wifi scan type to request for.
76 uint8_t gScanTypeIndex = 0;
77 
78 /**
79  * Logs a CHRE wifi scan result.
80  *
81  * @param result the scan result to log.
82  */
logChreWifiResult(const chreWifiScanResult & result)83 void logChreWifiResult(const chreWifiScanResult& result) {
84   const char *ssidStr = "<non-printable>";
85   char ssidBuffer[chre::kMaxSsidStrLen];
86   if (result.ssidLen == 0) {
87     ssidStr = "<empty>";
88   } else if (chre::parseSsidToStr(ssidBuffer, sizeof(ssidBuffer),
89                                   result.ssid, result.ssidLen)) {
90     ssidStr = ssidBuffer;
91   }
92 
93   LOGI("Found network with SSID: %s", ssidStr);
94 #ifdef WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
95   const char *bssidStr = "<non-printable>";
96   char bssidBuffer[chre::kBssidStrLen];
97   if (chre::parseBssidToStr(result.bssid, bssidBuffer, sizeof(bssidBuffer))) {
98     bssidStr = bssidBuffer;
99   }
100 
101   LOGI("  age (ms): %" PRIu32, result.ageMs);
102   LOGI("  capability info: %" PRIx16, result.capabilityInfo);
103   LOGI("  bssid: %s", bssidStr);
104   LOGI("  flags: %" PRIx8, result.flags);
105   LOGI("  rssi: %" PRId8 "dBm", result.rssi);
106   LOGI("  band: %s (%" PRIu8 ")",
107        chre::parseChreWifiBand(result.band), result.band);
108   LOGI("  primary channel: %" PRIu32, result.primaryChannel);
109   LOGI("  center frequency primary: %" PRIu32, result.centerFreqPrimary);
110   LOGI("  center frequency secondary: %" PRIu32, result.centerFreqSecondary);
111   LOGI("  channel width: %" PRIu8, result.channelWidth);
112   LOGI("  security mode: %" PRIx8, result.securityMode);
113 #endif  // WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
114 }
115 
116 /**
117  * Requests a delayed wifi scan using a one-shot timer. The interval is
118  * specified as a constant at the top of this file.
119  */
requestDelayedWifiScan()120 void requestDelayedWifiScan() {
121   if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) {
122     // Schedule a timer to send an active wifi scan.
123     gWifiScanTimerHandle = chreTimerSet(kWifiScanInterval.toRawNanoseconds(),
124                                         &gWifiScanTimerHandle /* data */,
125                                         true /* oneShot */);
126     if (gWifiScanTimerHandle == CHRE_TIMER_INVALID) {
127       LOGE("Failed to set timer for delayed wifi scan");
128     } else {
129       LOGI("Set a timer to request a WiFi scan");
130     }
131   }
132 }
133 
134 /**
135  * Handles the result of an asynchronous request for a wifi resource.
136  *
137  * @param result a pointer to the event structure containing the result of the
138  * request.
139  */
handleWifiAsyncResult(const chreAsyncResult * result)140 void handleWifiAsyncResult(const chreAsyncResult *result) {
141   if (result->requestType == CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR) {
142     if (result->success) {
143       LOGI("Successfully requested wifi scan monitoring");
144     } else {
145       LOGE("Error requesting wifi scan monitoring with %" PRIu8,
146            result->errorCode);
147     }
148 
149     if (result->cookie != &kScanMonitoringCookie) {
150       LOGE("Scan monitoring request cookie mismatch");
151     }
152   } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN) {
153     uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
154     if (result->success) {
155       LOGI("Successfully requested an on-demand wifi scan (response time %"
156            PRIu64 " ms)", timeSinceRequest / kOneMillisecondInNanoseconds);
157       gPendingOnDemandScan = true;
158     } else {
159       LOGE("Error requesting an on-demand wifi scan with %" PRIu8,
160            result->errorCode);
161     }
162 
163     if (result->cookie != &kOnDemandScanCookie) {
164       LOGE("On-demand scan cookie mismatch");
165     }
166 
167     requestDelayedWifiScan();
168   } else {
169     LOGE("Received invalid async result");
170   }
171 }
172 
173 /**
174  * Handles a wifi scan event.
175  *
176  * @param event a pointer to the details of the wifi scan event.
177  */
handleWifiScanEvent(const chreWifiScanEvent * event)178 void handleWifiScanEvent(const chreWifiScanEvent *event) {
179   LOGI("Received Wifi scan event of type %" PRIu8 " with %" PRIu8
180        " results at %" PRIu64 "ns", event->scanType, event->resultCount,
181        event->referenceTime);
182 
183   if (gPendingOnDemandScan) {
184     uint64_t timeSinceRequest = chreGetTime() - gLastRequestTimeNs;
185     LOGI("Time since scan request = %" PRIu64 " ms",
186          timeSinceRequest / kOneMillisecondInNanoseconds);
187 
188     if (event->scanType != gWifiScanParams.scanType) {
189       LOGE("Invalid scan event type (expected %" PRIu8 ", received %" PRIu8 ")",
190            gWifiScanParams.scanType, event->scanType);
191     }
192 
193     gScanResultAcc += event->resultCount;
194     if (gScanResultAcc >= event->resultTotal) {
195       gPendingOnDemandScan = false;
196       gScanResultAcc = 0;
197     }
198   }
199 
200   for (uint8_t i = 0; i < event->resultCount; i++) {
201     const chreWifiScanResult& result = event->results[i];
202     logChreWifiResult(result);
203   }
204 }
205 
206 /**
207  * Handles a timer event.
208  *
209  * @param eventData The cookie passed to the timer request.
210  */
handleTimerEvent(const void * eventData)211 void handleTimerEvent(const void *eventData) {
212   const uint32_t *timerHandle = static_cast<const uint32_t *>(eventData);
213   if (*timerHandle == gWifiScanTimerHandle) {
214     gWifiScanParams.scanType         = gWifiScanTypes[gScanTypeIndex];
215     gWifiScanParams.maxScanAgeMs     = 5000;  // 5 seconds
216     gWifiScanParams.frequencyListLen = 0;
217     gWifiScanParams.ssidListLen      = 0;
218     gScanTypeIndex = (gScanTypeIndex + 1) % ARRAY_SIZE(gWifiScanTypes);
219 
220     if (chreWifiRequestScanAsync(&gWifiScanParams, &kOnDemandScanCookie)) {
221       LOGI("Requested a wifi scan successfully");
222       gLastRequestTimeNs = chreGetTime();
223     } else {
224       LOGE("Failed to request a wifi scan");
225     }
226   } else {
227     LOGE("Received invalid timer handle");
228   }
229 }
230 
231 }  // namespace
232 
nanoappStart()233 bool nanoappStart() {
234   LOGI("App started as instance %" PRIu32, chreGetInstanceId());
235 
236   const char *gWifiCapabilitiesStr;
237   gWifiCapabilities = chreWifiGetCapabilities();
238   switch (gWifiCapabilities) {
239     case CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN
240         | CHRE_WIFI_CAPABILITIES_SCAN_MONITORING:
241       gWifiCapabilitiesStr = "ON_DEMAND_SCAN | SCAN_MONITORING";
242       break;
243     case CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN:
244       gWifiCapabilitiesStr = "ON_DEMAND_SCAN";
245       break;
246     case CHRE_WIFI_CAPABILITIES_SCAN_MONITORING:
247       gWifiCapabilitiesStr = "SCAN_MONITORING";
248       break;
249     case CHRE_WIFI_CAPABILITIES_NONE:
250       gWifiCapabilitiesStr = "NONE";
251       break;
252     default:
253       gWifiCapabilitiesStr = "INVALID";
254   }
255 
256   LOGI("Detected WiFi support as: %s (%" PRIu32 ")",
257        gWifiCapabilitiesStr, gWifiCapabilities);
258 
259   if (gWifiCapabilities & CHRE_WIFI_CAPABILITIES_SCAN_MONITORING) {
260     if (chreWifiConfigureScanMonitorAsync(true, &kScanMonitoringCookie)) {
261       LOGI("Scan monitor enable request successful");
262     } else {
263       LOGE("Error sending scan monitoring request");
264     }
265   }
266 
267   requestDelayedWifiScan();
268   return true;
269 }
270 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)271 void nanoappHandleEvent(uint32_t senderInstanceId,
272                         uint16_t eventType,
273                         const void *eventData) {
274   switch (eventType) {
275     case CHRE_EVENT_WIFI_ASYNC_RESULT:
276       handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
277       break;
278     case CHRE_EVENT_WIFI_SCAN_RESULT:
279       handleWifiScanEvent(static_cast<const chreWifiScanEvent *>(eventData));
280       break;
281     case CHRE_EVENT_TIMER:
282       handleTimerEvent(eventData);
283       break;
284     default:
285       LOGW("Unhandled event type %" PRIu16, eventType);
286   }
287 }
288 
nanoappEnd()289 void nanoappEnd() {
290   LOGI("Wifi world app stopped");
291 }
292 
293 #ifdef CHRE_NANOAPP_INTERNAL
294 }  // anonymous namespace
295 }  // namespace chre
296 
297 #include "chre/util/nanoapp/app_id.h"
298 #include "chre/platform/static_nanoapp_init.h"
299 
300 CHRE_STATIC_NANOAPP_INIT(WifiWorld, chre::kWifiWorldAppId, 0);
301 #endif  // CHRE_NANOAPP_INTERNAL
302