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/nanoapp/log.h"
21 #include "chre/util/time.h"
22 #include "chre/util/nanoapp/wifi.h"
23 
24 #define LOG_TAG "[WifiWorld]"
25 
26 //#define WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
27 
28 #ifdef CHRE_NANOAPP_INTERNAL
29 namespace chre {
30 namespace {
31 #endif  // CHRE_NANOAPP_INTERNAL
32 
33 //! A dummy cookie to pass into the configure scan monitoring async request.
34 const uint32_t kScanMonitoringCookie = 0x1337;
35 
36 //! A dummy cookie to pass into request scan async.
37 const uint32_t kOnDemandScanCookie = 0xcafe;
38 
39 //! The interval for on-demand wifi scans.
40 const Nanoseconds kWifiScanInterval = Nanoseconds(Seconds(10));
41 
42 //! A handle for the cyclic timer to request periodic on-demand wifi-scans.
43 uint32_t gWifiScanTimerHandle;
44 
45 namespace {
46 
47 /**
48  * Logs a CHRE wifi scan result.
49  *
50  * @param result the scan result to log.
51  */
logChreWifiResult(const chreWifiScanResult & result)52 void logChreWifiResult(const chreWifiScanResult& result) {
53   const char *ssidStr = "<non-printable>";
54   char ssidBuffer[kMaxSsidStrLen];
55   if (result.ssidLen == 0) {
56     ssidStr = "<empty>";
57   } else if (parseSsidToStr(ssidBuffer, kMaxSsidStrLen,
58                             result.ssid, result.ssidLen)) {
59     ssidStr = ssidBuffer;
60   }
61 
62   const char *bssidStr = "<non-printable>";
63   char bssidBuffer[kBssidStrLen];
64   if (parseBssidToStr(result.bssid, bssidBuffer, kBssidStrLen)) {
65     bssidStr = bssidBuffer;
66   }
67 
68   LOGI("Found network with SSID: %s", ssidStr);
69 #ifdef WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
70   LOGI("  age (ms): %" PRIu32, result.ageMs);
71   LOGI("  capability info: %" PRIx16, result.capabilityInfo);
72   LOGI("  bssid: %s", bssidStr);
73   LOGI("  flags: %" PRIx8, result.flags);
74   LOGI("  rssi: %" PRId8 "dBm", result.rssi);
75   LOGI("  band: %s (%" PRIu8 ")", parseChreWifiBand(result.band), result.band);
76   LOGI("  primary channel: %" PRIu32, result.primaryChannel);
77   LOGI("  center frequency primary: %" PRIu32, result.centerFreqPrimary);
78   LOGI("  center frequency secondary: %" PRIu32, result.centerFreqSecondary);
79   LOGI("  channel width: %" PRIu8, result.channelWidth);
80   LOGI("  security mode: %" PRIx8, result.securityMode);
81 #endif  // WIFI_WORLD_VERBOSE_WIFI_RESULT_LOGS
82 }
83 
84 /**
85  * Handles the result of an asynchronous request for a wifi resource.
86  *
87  * @param result a pointer to the event structure containing the result of the
88  * request.
89  */
handleWifiAsyncResult(const chreAsyncResult * result)90 void handleWifiAsyncResult(const chreAsyncResult *result) {
91   if (result->requestType == CHRE_WIFI_REQUEST_TYPE_CONFIGURE_SCAN_MONITOR) {
92     if (result->success) {
93       LOGI("Successfully requested wifi scan monitoring");
94     } else {
95       LOGI("Error requesting wifi scan monitoring with %" PRIu8,
96            result->errorCode);
97     }
98 
99     if (result->cookie != &kScanMonitoringCookie) {
100       LOGE("Scan monitoring request cookie mismatch");
101     }
102   } else if (result->requestType == CHRE_WIFI_REQUEST_TYPE_REQUEST_SCAN) {
103     if (result->success) {
104       LOGI("Successfully requested an on-demand wifi scan");
105     } else {
106       LOGE("Error requesting an on-demand wifi scan with %" PRIu8,
107            result->errorCode);
108     }
109 
110     if (result->cookie != &kOnDemandScanCookie) {
111       LOGE("On-demand scan cookie mismatch");
112     }
113   }
114 }
115 
116 /**
117  * Handles a wifi scan event.
118  *
119  * @param event a pointer to the details of the wifi scan event.
120  */
handleWifiScanEvent(const chreWifiScanEvent * event)121 void handleWifiScanEvent(const chreWifiScanEvent *event) {
122   for (uint8_t i = 0; i < event->resultCount; i++) {
123     const chreWifiScanResult& result = event->results[i];
124     logChreWifiResult(result);
125   }
126 }
127 
128 /**
129  * Handles a timer event.
130  *
131  * @param eventData The cookie passed to the timer request.
132  */
handleTimerEvent(const void * eventData)133 void handleTimerEvent(const void *eventData) {
134   const uint32_t *timerHandle = static_cast<const uint32_t *>(eventData);
135   if (*timerHandle == gWifiScanTimerHandle) {
136     if (chreWifiRequestScanAsyncDefault(&kOnDemandScanCookie)) {
137       LOGI("Requested a wifi scan successfully");
138     } else {
139       LOGE("Failed to request a wifi scan");
140     }
141   } else {
142     LOGE("Received invalid timer handle");
143   }
144 }
145 
146 }  // namespace
147 
nanoappStart()148 bool nanoappStart() {
149   LOGI("App started as instance %" PRIu32, chreGetInstanceId());
150 
151   const char *wifiCapabilitiesStr;
152   uint32_t wifiCapabilities = chreWifiGetCapabilities();
153   switch (wifiCapabilities) {
154     case CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN
155         | CHRE_WIFI_CAPABILITIES_SCAN_MONITORING:
156       wifiCapabilitiesStr = "ON_DEMAND_SCAN | SCAN_MONITORING";
157       break;
158     case CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN:
159       wifiCapabilitiesStr = "ON_DEMAND_SCAN";
160       break;
161     case CHRE_WIFI_CAPABILITIES_SCAN_MONITORING:
162       wifiCapabilitiesStr = "SCAN_MONITORING";
163       break;
164     case CHRE_WIFI_CAPABILITIES_NONE:
165       wifiCapabilitiesStr = "NONE";
166       break;
167     default:
168       wifiCapabilitiesStr = "INVALID";
169   }
170 
171   LOGI("Detected WiFi support as: %s (%" PRIu32 ")",
172        wifiCapabilitiesStr, wifiCapabilities);
173 
174   if (wifiCapabilities & CHRE_WIFI_CAPABILITIES_SCAN_MONITORING) {
175     if (chreWifiConfigureScanMonitorAsync(true, &kScanMonitoringCookie)) {
176       LOGI("Scan monitor enable request successful");
177     } else {
178       LOGE("Error sending scan monitoring request");
179     }
180   }
181 
182   if (wifiCapabilities & CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN) {
183     // Schedule a timer to send an active wifi scan.
184     gWifiScanTimerHandle = chreTimerSet(kWifiScanInterval.toRawNanoseconds(),
185                                         &gWifiScanTimerHandle /* data */,
186                                         false /* oneShot */);
187     if (gWifiScanTimerHandle == CHRE_TIMER_INVALID) {
188       LOGE("Failed to set periodic scan timer");
189     } else {
190       LOGI("Set a timer to request periodic WiFi scans");
191     }
192   }
193 
194   return true;
195 }
196 
nanoappHandleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)197 void nanoappHandleEvent(uint32_t senderInstanceId,
198                         uint16_t eventType,
199                         const void *eventData) {
200   switch (eventType) {
201     case CHRE_EVENT_WIFI_ASYNC_RESULT:
202       handleWifiAsyncResult(static_cast<const chreAsyncResult *>(eventData));
203       break;
204     case CHRE_EVENT_WIFI_SCAN_RESULT:
205       handleWifiScanEvent(static_cast<const chreWifiScanEvent *>(eventData));
206       break;
207     case CHRE_EVENT_TIMER:
208       handleTimerEvent(eventData);
209       break;
210     default:
211       LOGW("Unhandled event type %" PRIu16, eventType);
212   }
213 }
214 
nanoappEnd()215 void nanoappEnd() {
216   LOGI("Wifi world app stopped");
217 }
218 
219 #ifdef CHRE_NANOAPP_INTERNAL
220 }  // anonymous namespace
221 }  // namespace chre
222 
223 #include "chre/util/nanoapp/app_id.h"
224 #include "chre/platform/static_nanoapp_init.h"
225 
226 CHRE_STATIC_NANOAPP_INIT(WifiWorld, chre::kWifiWorldAppId, 0);
227 #endif  // CHRE_NANOAPP_INTERNAL
228