Commit | Line | Data |
---|---|---|
7753f181 DD |
1 | |
2 | #include <stdint.h> | |
6ff2d683 | 3 | #include <stddef.h> |
7753f181 DD |
4 | #include <fcntl.h> |
5 | #include <sys/socket.h> | |
6 | #include <netlink/genl/genl.h> | |
7 | #include <netlink/genl/family.h> | |
8 | #include <netlink/genl/ctrl.h> | |
9 | #include <linux/rtnetlink.h> | |
10 | #include <netpacket/packet.h> | |
11 | #include <linux/filter.h> | |
12 | #include <linux/errqueue.h> | |
13 | ||
14 | #include <linux/pkt_sched.h> | |
15 | #include <netlink/object-api.h> | |
16 | #include <netlink/netlink.h> | |
17 | #include <netlink/socket.h> | |
18 | #include <netlink/handlers.h> | |
19 | ||
20 | #include "sync.h" | |
21 | ||
22 | #define LOG_TAG "WifiHAL" | |
23 | ||
24 | #include <utils/Log.h> | |
25 | ||
26 | #include "wifi_hal.h" | |
27 | #include "common.h" | |
28 | #include "cpp_bindings.h" | |
29 | ||
30 | typedef enum { | |
31 | ||
32 | GSCAN_ATTRIBUTE_NUM_BUCKETS = 10, | |
33 | GSCAN_ATTRIBUTE_BASE_PERIOD, | |
34 | GSCAN_ATTRIBUTE_BUCKETS_BAND, | |
35 | GSCAN_ATTRIBUTE_BUCKET_ID, | |
36 | GSCAN_ATTRIBUTE_BUCKET_PERIOD, | |
37 | GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, | |
38 | GSCAN_ATTRIBUTE_BUCKET_CHANNELS, | |
39 | GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, | |
40 | GSCAN_ATTRIBUTE_REPORT_THRESHOLD, | |
41 | GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE, | |
42 | GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS, | |
43 | GSCAN_ATTRIBUTE_BAND = GSCAN_ATTRIBUTE_BUCKETS_BAND, | |
44 | ||
45 | GSCAN_ATTRIBUTE_ENABLE_FEATURE = 20, | |
46 | GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE, /* indicates no more results */ | |
47 | GSCAN_ATTRIBUTE_REPORT_EVENTS, | |
48 | ||
49 | /* remaining reserved for additional attributes */ | |
50 | GSCAN_ATTRIBUTE_NUM_OF_RESULTS = 30, | |
51 | GSCAN_ATTRIBUTE_SCAN_RESULTS, /* flat array of wifi_scan_result */ | |
52 | GSCAN_ATTRIBUTE_NUM_CHANNELS, | |
53 | GSCAN_ATTRIBUTE_CHANNEL_LIST, | |
54 | GSCAN_ATTRIBUTE_SCAN_ID, | |
55 | GSCAN_ATTRIBUTE_SCAN_FLAGS, | |
acc9b163 | 56 | GSCAN_ATTRIBUTE_SCAN_BUCKET_BIT, |
7753f181 DD |
57 | |
58 | /* remaining reserved for additional attributes */ | |
59 | ||
60 | GSCAN_ATTRIBUTE_SSID = 40, | |
61 | GSCAN_ATTRIBUTE_BSSID, | |
62 | GSCAN_ATTRIBUTE_CHANNEL, | |
63 | GSCAN_ATTRIBUTE_RSSI, | |
64 | GSCAN_ATTRIBUTE_TIMESTAMP, | |
65 | GSCAN_ATTRIBUTE_RTT, | |
66 | GSCAN_ATTRIBUTE_RTTSD, | |
67 | ||
68 | /* remaining reserved for additional attributes */ | |
69 | ||
70 | GSCAN_ATTRIBUTE_HOTLIST_BSSIDS = 50, | |
71 | GSCAN_ATTRIBUTE_RSSI_LOW, | |
72 | GSCAN_ATTRIBUTE_RSSI_HIGH, | |
73 | GSCAN_ATTRIBUTE_HOTLIST_ELEM, | |
74 | GSCAN_ATTRIBUTE_HOTLIST_FLUSH, | |
75 | GSCAN_ATTRIBUTE_CHANNEL_NUMBER, | |
76 | ||
77 | /* remaining reserved for additional attributes */ | |
78 | GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE = 60, | |
79 | GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, | |
80 | GSCAN_ATTRIBUTE_MIN_BREACHING, | |
81 | GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS, | |
82 | ||
0e19df50 JPS |
83 | GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT = 70, |
84 | GSCAN_ATTRIBUTE_BUCKET_EXPONENT, | |
85 | GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, | |
86 | ||
10d7569e PG |
87 | GSCAN_ATTRIBUTE_NUM_BSSID, |
88 | GSCAN_ATTRIBUTE_BLACKLIST_BSSID, | |
89 | ||
7753f181 DD |
90 | GSCAN_ATTRIBUTE_MAX |
91 | ||
92 | } GSCAN_ATTRIBUTE; | |
93 | ||
6ff2d683 JPS |
94 | typedef enum { |
95 | EPNO_ATTRIBUTE_SSID_LIST, | |
96 | EPNO_ATTRIBUTE_SSID_NUM, | |
97 | EPNO_ATTRIBUTE_SSID, | |
98 | EPNO_ATTRIBUTE_SSID_LEN, | |
99 | EPNO_ATTRIBUTE_RSSI, | |
100 | EPNO_ATTRIBUTE_FLAGS, | |
101 | EPNO_ATTRIBUTE_AUTH, | |
102 | EPNO_ATTRIBUTE_MAX | |
103 | } EPNO_ATTRIBUTE; | |
104 | ||
105 | typedef enum { | |
106 | EPNO_ATTRIBUTE_HS_PARAM_LIST, | |
107 | EPNO_ATTRIBUTE_HS_NUM, | |
108 | EPNO_ATTRIBUTE_HS_ID, | |
109 | EPNO_ATTRIBUTE_HS_REALM, | |
110 | EPNO_ATTRIBUTE_HS_CONSORTIUM_IDS, | |
111 | EPNO_ATTRIBUTE_HS_PLMN, | |
112 | EPNO_ATTRIBUTE_HS_MAX | |
113 | } EPNO_HS_ATTRIBUTE; | |
114 | ||
7753f181 DD |
115 | |
116 | class GetCapabilitiesCommand : public WifiCommand | |
117 | { | |
118 | wifi_gscan_capabilities *mCapabilities; | |
119 | public: | |
120 | GetCapabilitiesCommand(wifi_interface_handle iface, wifi_gscan_capabilities *capabitlites) | |
121 | : WifiCommand(iface, 0), mCapabilities(capabitlites) | |
122 | { | |
123 | memset(mCapabilities, 0, sizeof(*mCapabilities)); | |
124 | } | |
125 | ||
126 | virtual int create() { | |
127 | ALOGD("Creating message to get scan capablities; iface = %d", mIfaceInfo->id); | |
128 | ||
129 | int ret = mMsg.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_CAPABILITIES); | |
130 | if (ret < 0) { | |
acc9b163 | 131 | ALOGD("NL message creation failed"); |
7753f181 DD |
132 | return ret; |
133 | } | |
134 | ||
135 | return ret; | |
136 | } | |
137 | ||
138 | protected: | |
139 | virtual int handleResponse(WifiEvent& reply) { | |
140 | ||
141 | ALOGD("In GetCapabilities::handleResponse"); | |
142 | ||
143 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { | |
144 | ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); | |
145 | return NL_SKIP; | |
146 | } | |
147 | ||
148 | int id = reply.get_vendor_id(); | |
149 | int subcmd = reply.get_vendor_subcmd(); | |
150 | ||
151 | void *data = reply.get_vendor_data(); | |
152 | int len = reply.get_vendor_data_len(); | |
153 | ||
154 | ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, | |
155 | sizeof(*mCapabilities)); | |
156 | ||
157 | memcpy(mCapabilities, data, min(len, (int) sizeof(*mCapabilities))); | |
158 | ||
159 | return NL_OK; | |
160 | } | |
161 | }; | |
162 | ||
163 | ||
164 | wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, | |
165 | wifi_gscan_capabilities *capabilities) | |
166 | { | |
167 | GetCapabilitiesCommand command(handle, capabilities); | |
168 | return (wifi_error) command.requestResponse(); | |
169 | } | |
170 | ||
171 | class GetChannelListCommand : public WifiCommand | |
172 | { | |
173 | wifi_channel *channels; | |
174 | int max_channels; | |
175 | int *num_channels; | |
176 | int band; | |
177 | public: | |
178 | GetChannelListCommand(wifi_interface_handle iface, wifi_channel *channel_buf, int *ch_num, | |
179 | int num_max_ch, int band) | |
180 | : WifiCommand(iface, 0), channels(channel_buf), max_channels(num_max_ch), num_channels(ch_num), | |
181 | band(band) | |
182 | { | |
183 | memset(channels, 0, sizeof(wifi_channel) * max_channels); | |
184 | } | |
185 | virtual int create() { | |
186 | ALOGD("Creating message to get channel list; iface = %d", mIfaceInfo->id); | |
187 | ||
188 | int ret = mMsg.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_VALID_CHANNELS); | |
189 | if (ret < 0) { | |
190 | return ret; | |
191 | } | |
192 | ||
193 | nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); | |
194 | ret = mMsg.put_u32(GSCAN_ATTRIBUTE_BAND, band); | |
195 | if (ret < 0) { | |
196 | return ret; | |
197 | } | |
198 | ||
199 | mMsg.attr_end(data); | |
200 | ||
201 | return ret; | |
202 | } | |
203 | ||
204 | protected: | |
205 | virtual int handleResponse(WifiEvent& reply) { | |
206 | ||
207 | ALOGD("In GetChannelList::handleResponse"); | |
208 | ||
209 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { | |
210 | ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); | |
211 | return NL_SKIP; | |
212 | } | |
213 | ||
214 | int id = reply.get_vendor_id(); | |
215 | int subcmd = reply.get_vendor_subcmd(); | |
216 | int num_channels_to_copy = 0; | |
217 | ||
218 | nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
219 | int len = reply.get_vendor_data_len(); | |
220 | ||
221 | ALOGD("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); | |
222 | if (vendor_data == NULL || len == 0) { | |
223 | ALOGE("no vendor data in GetChannelList response; ignoring it"); | |
224 | return NL_SKIP; | |
225 | } | |
226 | ||
227 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
228 | if (it.get_type() == GSCAN_ATTRIBUTE_NUM_CHANNELS) { | |
229 | num_channels_to_copy = it.get_u32(); | |
230 | ALOGD("Got channel list with %d channels", num_channels_to_copy); | |
231 | if(num_channels_to_copy > max_channels) | |
232 | num_channels_to_copy = max_channels; | |
233 | *num_channels = num_channels_to_copy; | |
234 | } else if (it.get_type() == GSCAN_ATTRIBUTE_CHANNEL_LIST && num_channels_to_copy) { | |
235 | memcpy(channels, it.get_data(), sizeof(int) * num_channels_to_copy); | |
236 | } else { | |
237 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
238 | it.get_type(), it.get_len()); | |
239 | } | |
240 | } | |
241 | ||
242 | return NL_OK; | |
243 | } | |
244 | }; | |
245 | ||
246 | wifi_error wifi_get_valid_channels(wifi_interface_handle handle, | |
247 | int band, int max_channels, wifi_channel *channels, int *num_channels) | |
248 | { | |
249 | GetChannelListCommand command(handle, channels, num_channels, | |
250 | max_channels, band); | |
251 | return (wifi_error) command.requestResponse(); | |
252 | } | |
253 | ///////////////////////////////////////////////////////////////////////////// | |
254 | ||
255 | /* helper functions */ | |
256 | ||
257 | static int parseScanResults(wifi_scan_result *results, int num, nlattr *attr) | |
258 | { | |
259 | memset(results, 0, sizeof(wifi_scan_result) * num); | |
260 | ||
261 | int i = 0; | |
262 | for (nl_iterator it(attr); it.has_next() && i < num; it.next(), i++) { | |
263 | ||
264 | int index = it.get_type(); | |
265 | ALOGD("retrieved scan result %d", index); | |
266 | nlattr *sc_data = (nlattr *) it.get_data(); | |
267 | wifi_scan_result *result = results + i; | |
268 | ||
269 | for (nl_iterator it2(sc_data); it2.has_next(); it2.next()) { | |
270 | int type = it2.get_type(); | |
271 | if (type == GSCAN_ATTRIBUTE_SSID) { | |
272 | strncpy(result->ssid, (char *) it2.get_data(), it2.get_len()); | |
273 | result->ssid[it2.get_len()] = 0; | |
274 | } else if (type == GSCAN_ATTRIBUTE_BSSID) { | |
275 | memcpy(result->bssid, (byte *) it2.get_data(), sizeof(mac_addr)); | |
276 | } else if (type == GSCAN_ATTRIBUTE_TIMESTAMP) { | |
277 | result->ts = it2.get_u64(); | |
278 | } else if (type == GSCAN_ATTRIBUTE_CHANNEL) { | |
279 | result->ts = it2.get_u16(); | |
280 | } else if (type == GSCAN_ATTRIBUTE_RSSI) { | |
281 | result->rssi = it2.get_u8(); | |
282 | } else if (type == GSCAN_ATTRIBUTE_RTT) { | |
283 | result->rtt = it2.get_u64(); | |
284 | } else if (type == GSCAN_ATTRIBUTE_RTTSD) { | |
285 | result->rtt_sd = it2.get_u64(); | |
286 | } | |
287 | } | |
288 | ||
289 | } | |
290 | ||
291 | if (i >= num) { | |
292 | ALOGE("Got too many results; skipping some"); | |
293 | } | |
294 | ||
295 | return i; | |
296 | } | |
297 | ||
298 | int createFeatureRequest(WifiRequest& request, int subcmd) { | |
299 | ||
300 | int result = request.create(GOOGLE_OUI, subcmd); | |
301 | if (result < 0) { | |
302 | return result; | |
303 | } | |
304 | ||
305 | return WIFI_SUCCESS; | |
306 | } | |
307 | ||
308 | class ScanCommand : public WifiCommand | |
309 | { | |
310 | wifi_scan_cmd_params *mParams; | |
311 | wifi_scan_result_handler mHandler; | |
312 | static unsigned mGlobalFullScanBuckets; | |
313 | bool mLocalFullScanBuckets; | |
314 | public: | |
315 | ScanCommand(wifi_interface_handle iface, int id, wifi_scan_cmd_params *params, | |
316 | wifi_scan_result_handler handler) | |
317 | : WifiCommand(iface, id), mParams(params), mHandler(handler), | |
318 | mLocalFullScanBuckets(0) | |
319 | { } | |
320 | ||
321 | int createSetupRequest(WifiRequest& request) { | |
322 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN); | |
323 | if (result < 0) { | |
324 | return result; | |
325 | } | |
326 | ||
327 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
328 | result = request.put_u32(GSCAN_ATTRIBUTE_BASE_PERIOD, mParams->base_period); | |
329 | if (result < 0) { | |
330 | return result; | |
331 | } | |
332 | ||
acc9b163 | 333 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, mParams->max_ap_per_scan); |
7753f181 DD |
334 | if (result < 0) { |
335 | return result; | |
336 | } | |
337 | ||
338 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD, mParams->report_threshold_percent); | |
339 | if (result < 0) { | |
340 | return result; | |
341 | } | |
342 | ||
343 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS, mParams->report_threshold_num_scans); | |
344 | if (result < 0) { | |
345 | return result; | |
346 | } | |
347 | ||
348 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BUCKETS, mParams->num_buckets); | |
349 | if (result < 0) { | |
350 | return result; | |
351 | } | |
352 | ||
353 | for (int i = 0; i < mParams->num_buckets; i++) { | |
354 | nlattr * bucket = request.attr_start(i); // next bucket | |
355 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_ID, mParams->buckets[i].bucket); | |
356 | if (result < 0) { | |
357 | return result; | |
358 | } | |
359 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_PERIOD, mParams->buckets[i].period); | |
360 | if (result < 0) { | |
361 | return result; | |
362 | } | |
363 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKETS_BAND, | |
364 | mParams->buckets[i].band); | |
365 | if (result < 0) { | |
366 | return result; | |
367 | } | |
368 | ||
369 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_EVENTS, | |
370 | mParams->buckets[i].report_events); | |
371 | if (result < 0) { | |
372 | return result; | |
373 | } | |
374 | ||
375 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, | |
376 | mParams->buckets[i].num_channels); | |
377 | if (result < 0) { | |
378 | return result; | |
379 | } | |
380 | ||
0e19df50 | 381 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_EXPONENT, |
acc9b163 | 382 | mParams->buckets[i].base); |
0e19df50 JPS |
383 | if (result < 0) { |
384 | return result; | |
385 | } | |
386 | ||
387 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, | |
388 | mParams->buckets[i].max_period); | |
389 | if (result < 0) { | |
390 | return result; | |
391 | } | |
392 | ||
393 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT, | |
394 | mParams->buckets[i].step_count); | |
395 | if (result < 0) { | |
396 | return result; | |
397 | } | |
398 | ||
7753f181 DD |
399 | if (mParams->buckets[i].num_channels) { |
400 | nlattr *channels = request.attr_start(GSCAN_ATTRIBUTE_BUCKET_CHANNELS); | |
401 | for (int j = 0; j < mParams->buckets[i].num_channels; j++) { | |
402 | result = request.put_u32(j, mParams->buckets[i].channels[j].channel); | |
403 | if (result < 0) { | |
404 | return result; | |
405 | } | |
406 | } | |
407 | request.attr_end(channels); | |
408 | } | |
409 | ||
410 | request.attr_end(bucket); | |
411 | } | |
412 | ||
413 | request.attr_end(data); | |
414 | return WIFI_SUCCESS; | |
415 | } | |
416 | ||
417 | int createStartRequest(WifiRequest& request) { | |
418 | return createFeatureRequest(request, SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN); | |
419 | } | |
420 | ||
421 | int createStopRequest(WifiRequest& request) { | |
422 | return createFeatureRequest(request, SLSI_NL80211_VENDOR_SUBCMD_DEL_GSCAN); | |
423 | } | |
424 | ||
425 | int start() { | |
426 | ALOGD(" sending scan req to driver"); | |
427 | WifiRequest request(familyId(), ifaceId()); | |
428 | int result = createSetupRequest(request); | |
429 | if (result != WIFI_SUCCESS) { | |
430 | ALOGE("failed to create setup request; result = %d", result); | |
431 | return result; | |
432 | } | |
433 | ALOGD("Starting scan"); | |
434 | ||
435 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
436 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
437 | ||
438 | int nBuckets = 0; | |
439 | for (int i = 0; i < mParams->num_buckets; i++) { | |
440 | if (mParams->buckets[i].report_events == 2) { | |
441 | nBuckets++; | |
442 | } | |
443 | } | |
444 | ||
445 | if (nBuckets != 0) { | |
446 | ALOGI("Full scan requested with nBuckets = %d", nBuckets); | |
447 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); | |
448 | } | |
449 | result = requestResponse(request); | |
450 | if (result != WIFI_SUCCESS) { | |
451 | ALOGE("failed to start scan; result = %d", result); | |
452 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
453 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
454 | return result; | |
455 | } | |
456 | ||
d919889a | 457 | |
7753f181 DD |
458 | return result; |
459 | } | |
460 | ||
461 | virtual int cancel() { | |
462 | ALOGD("Stopping scan"); | |
463 | ||
464 | WifiRequest request(familyId(), ifaceId()); | |
465 | int result = createStopRequest(request); | |
466 | if (result != WIFI_SUCCESS) { | |
467 | ALOGE("failed to create stop request; result = %d", result); | |
468 | } else { | |
469 | result = requestResponse(request); | |
470 | if (result != WIFI_SUCCESS) { | |
471 | ALOGE("failed to stop scan; result = %d", result); | |
472 | } | |
473 | } | |
474 | ||
475 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
476 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
477 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); | |
478 | ||
479 | return WIFI_SUCCESS; | |
480 | } | |
481 | ||
482 | virtual int handleResponse(WifiEvent& reply) { | |
483 | /* Nothing to do on response! */ | |
484 | return NL_SKIP; | |
485 | } | |
486 | ||
487 | virtual int handleEvent(WifiEvent& event) { | |
488 | ALOGD("Got a scan results event"); | |
489 | ||
490 | event.log(); | |
491 | ||
492 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
493 | unsigned int len = event.get_vendor_data_len(); | |
494 | int event_id = event.get_vendor_subcmd(); | |
495 | ALOGD("handleEvent, event_id = %d", event_id); | |
496 | ||
497 | if(event_id == GSCAN_EVENT_COMPLETE_SCAN) { | |
498 | if (vendor_data == NULL || len != 4) { | |
499 | ALOGD("Scan complete type not mentioned!"); | |
500 | return NL_SKIP; | |
501 | } | |
502 | wifi_scan_event evt_type; | |
503 | ||
504 | evt_type = (wifi_scan_event) event.get_u32(NL80211_ATTR_VENDOR_DATA); | |
505 | ALOGD("Scan complete: Received event type %d", evt_type); | |
506 | if(*mHandler.on_scan_event) | |
507 | (*mHandler.on_scan_event)(evt_type, evt_type); | |
508 | } else if(event_id == GSCAN_EVENT_FULL_SCAN_RESULTS) { | |
acc9b163 JPS |
509 | uint32_t bucket_scanned; |
510 | wifi_scan_result *scan_result = NULL; | |
511 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
512 | if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_BUCKET_BIT) { | |
513 | bucket_scanned = it.get_u32(); | |
514 | } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS) { | |
515 | if (it.get_len() >= (int)sizeof(*scan_result)) | |
516 | scan_result = (wifi_scan_result *)it.get_data(); | |
517 | } | |
518 | } | |
519 | if (scan_result) { | |
520 | if(*mHandler.on_full_scan_result) | |
521 | (*mHandler.on_full_scan_result)(id(), scan_result, bucket_scanned); | |
522 | ||
523 | ALOGD("%-32s\t", scan_result->ssid); | |
524 | ALOGD("%02x:%02x:%02x:%02x:%02x:%02x ", scan_result->bssid[0], scan_result->bssid[1], | |
525 | scan_result->bssid[2], scan_result->bssid[3], scan_result->bssid[4], scan_result->bssid[5]); | |
526 | ALOGD("%d\t", scan_result->rssi); | |
527 | ALOGD("%d\t", scan_result->channel); | |
528 | ALOGD("%lld\t", scan_result->ts); | |
529 | ALOGD("%lld\t", scan_result->rtt); | |
530 | ALOGD("%lld\n", scan_result->rtt_sd); | |
7753f181 | 531 | } |
7753f181 DD |
532 | } |
533 | return NL_SKIP; | |
534 | } | |
535 | }; | |
536 | ||
537 | unsigned ScanCommand::mGlobalFullScanBuckets = 0; | |
538 | ||
539 | wifi_error wifi_start_gscan( | |
540 | wifi_request_id id, | |
541 | wifi_interface_handle iface, | |
542 | wifi_scan_cmd_params params, | |
543 | wifi_scan_result_handler handler) | |
544 | { | |
545 | wifi_handle handle = getWifiHandle(iface); | |
546 | ||
547 | ALOGD("Starting GScan, halHandle = %p", handle); | |
548 | ||
549 | ScanCommand *cmd = new ScanCommand(iface, id, ¶ms, handler); | |
550 | wifi_register_cmd(handle, id, cmd); | |
551 | return (wifi_error)cmd->start(); | |
552 | } | |
553 | ||
554 | wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) | |
555 | { | |
556 | ALOGD("Stopping GScan"); | |
557 | wifi_handle handle = getWifiHandle(iface); | |
558 | ||
559 | if(id == -1) { | |
560 | wifi_scan_result_handler handler; | |
561 | wifi_scan_cmd_params dummy_params; | |
562 | wifi_handle handle = getWifiHandle(iface); | |
563 | memset(&handler, 0, sizeof(handler)); | |
564 | ||
565 | ScanCommand *cmd = new ScanCommand(iface, id, &dummy_params, handler); | |
566 | cmd->cancel(); | |
567 | cmd->releaseRef(); | |
568 | return WIFI_SUCCESS; | |
569 | } | |
570 | ||
571 | ||
572 | WifiCommand *cmd = wifi_unregister_cmd(handle, id); | |
573 | if (cmd) { | |
574 | cmd->cancel(); | |
575 | cmd->releaseRef(); | |
576 | return WIFI_SUCCESS; | |
577 | } | |
578 | ||
579 | return WIFI_ERROR_INVALID_ARGS; | |
580 | } | |
581 | ||
582 | class GetScanResultsCommand : public WifiCommand { | |
583 | wifi_cached_scan_results *mScans; | |
584 | int mMax; | |
585 | int *mNum; | |
586 | int mRetrieved; | |
587 | byte mFlush; | |
588 | int mCompleted; | |
589 | static const int MAX_RESULTS = 320; | |
590 | wifi_scan_result mScanResults[MAX_RESULTS]; | |
591 | int mNextScanResult; | |
592 | public: | |
593 | GetScanResultsCommand(wifi_interface_handle iface, byte flush, | |
594 | wifi_cached_scan_results *results, int max, int *num) | |
595 | : WifiCommand(iface, -1), mScans(results), mMax(max), mNum(num), | |
596 | mRetrieved(0), mFlush(flush), mCompleted(0) | |
597 | { } | |
598 | ||
599 | int createRequest(WifiRequest& request, int num, byte flush) { | |
600 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_SCAN_RESULTS); | |
601 | if (result < 0) { | |
602 | return result; | |
603 | } | |
604 | ||
605 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
606 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num); | |
607 | if (result < 0) { | |
608 | return result; | |
609 | } | |
610 | ||
611 | request.attr_end(data); | |
612 | return WIFI_SUCCESS; | |
613 | } | |
614 | ||
615 | int execute() { | |
616 | WifiRequest request(familyId(), ifaceId()); | |
617 | ALOGD("retrieving %d scan results", mMax); | |
618 | ||
619 | for (int i = 0; i < 10 && mRetrieved < mMax; i++) { | |
620 | int result = createRequest(request, (mMax - mRetrieved), mFlush); | |
621 | if (result < 0) { | |
622 | ALOGE("failed to create request"); | |
623 | return result; | |
624 | } | |
625 | ||
626 | int prev_retrieved = mRetrieved; | |
627 | ||
628 | result = requestResponse(request); | |
629 | ||
630 | if (result != WIFI_SUCCESS) { | |
631 | ALOGE("failed to retrieve scan results; result = %d", result); | |
632 | return result; | |
633 | } | |
634 | ||
635 | if (mRetrieved == prev_retrieved || mCompleted) { | |
636 | /* no more items left to retrieve */ | |
637 | break; | |
638 | } | |
639 | ||
640 | request.destroy(); | |
641 | } | |
642 | ||
643 | ALOGE("GetScanResults read %d results", mRetrieved); | |
644 | *mNum = mRetrieved; | |
645 | return WIFI_SUCCESS; | |
646 | } | |
647 | ||
648 | virtual int handleResponse(WifiEvent& reply) { | |
649 | ALOGD("In GetScanResultsCommand::handleResponse"); | |
650 | ||
651 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { | |
652 | ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); | |
653 | return NL_SKIP; | |
654 | } | |
655 | ||
656 | int id = reply.get_vendor_id(); | |
657 | int subcmd = reply.get_vendor_subcmd(); | |
658 | ||
659 | ALOGD("Id = %0x, subcmd = %d", id, subcmd); | |
660 | ||
661 | nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
662 | int len = reply.get_vendor_data_len(); | |
663 | ||
664 | if (vendor_data == NULL || len == 0) { | |
665 | ALOGE("no vendor data in GetScanResults response; ignoring it"); | |
666 | return NL_SKIP; | |
667 | } | |
668 | ||
669 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
670 | if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE) { | |
671 | mCompleted = it.get_u8(); | |
672 | ALOGD("retrieved mCompleted flag : %d", mCompleted); | |
673 | } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS || it.get_type() == 0) { | |
674 | int scan_id = 0, flags = 0, num = 0; | |
675 | for (nl_iterator it2(it.get()); it2.has_next(); it2.next()) { | |
676 | if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_ID) { | |
677 | scan_id = it2.get_u32(); | |
678 | ALOGD("retrieved scan_id : 0x%0x", scan_id); | |
679 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_FLAGS) { | |
680 | flags = it2.get_u8(); | |
681 | ALOGD("retrieved scan_flags : 0x%0x", flags); | |
682 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_NUM_OF_RESULTS) { | |
683 | num = it2.get_u32(); | |
684 | ALOGD("retrieved num_results: %d", num); | |
685 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS) { | |
686 | if (mRetrieved >= mMax) { | |
687 | ALOGW("Stored %d scans, ignoring excess results", mRetrieved); | |
688 | break; | |
689 | } | |
690 | num = it2.get_len() / sizeof(wifi_scan_result); | |
691 | num = min(MAX_RESULTS - mNextScanResult, num); | |
692 | num = min((int)MAX_AP_CACHE_PER_SCAN, num); | |
693 | memcpy(mScanResults + mNextScanResult, it2.get_data(), | |
694 | sizeof(wifi_scan_result) * num); | |
695 | ALOGD("Retrieved %d scan results", num); | |
696 | wifi_scan_result *results = (wifi_scan_result *)it2.get_data(); | |
697 | for (int i = 0; i < num; i++) { | |
698 | wifi_scan_result *result = results + i; | |
699 | ALOGD("%02d %-32s %02x:%02x:%02x:%02x:%02x:%02x %04d", i, | |
700 | result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], | |
701 | result->bssid[3], result->bssid[4], result->bssid[5], | |
702 | result->rssi); | |
703 | } | |
704 | mScans[mRetrieved].scan_id = scan_id; | |
705 | mScans[mRetrieved].flags = flags; | |
706 | mScans[mRetrieved].num_results = num; | |
707 | ALOGD("Setting result of scan_id : 0x%0x", mScans[mRetrieved].scan_id); | |
708 | memcpy(mScans[mRetrieved].results, | |
709 | &(mScanResults[mNextScanResult]), num * sizeof(wifi_scan_result)); | |
710 | mNextScanResult += num; | |
711 | mRetrieved++; | |
712 | } else { | |
713 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
714 | it.get_type(), it.get_len()); | |
715 | } | |
716 | } | |
717 | } else { | |
718 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
719 | it.get_type(), it.get_len()); | |
720 | } | |
721 | } | |
722 | ||
723 | return NL_OK; | |
724 | } | |
725 | }; | |
726 | ||
727 | wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, | |
728 | int max, wifi_cached_scan_results *results, int *num) { | |
729 | ALOGD("Getting cached scan results, iface handle = %p, num = %d", iface, *num); | |
730 | ||
731 | GetScanResultsCommand *cmd = new GetScanResultsCommand(iface, flush, results, max, num); | |
732 | return (wifi_error)cmd->execute(); | |
733 | } | |
734 | ||
735 | ///////////////////////////////////////////////////////////////////////////// | |
736 | ||
737 | class BssidHotlistCommand : public WifiCommand | |
738 | { | |
739 | private: | |
740 | wifi_bssid_hotlist_params mParams; | |
741 | wifi_hotlist_ap_found_handler mHandler; | |
742 | static const int MAX_RESULTS = 64; | |
743 | wifi_scan_result mResults[MAX_RESULTS]; | |
744 | public: | |
745 | BssidHotlistCommand(wifi_interface_handle handle, int id, | |
746 | wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) | |
747 | : WifiCommand(handle, id), mParams(params), mHandler(handler) | |
748 | { } | |
749 | ||
750 | int createSetupRequest(WifiRequest& request) { | |
751 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_BSSID_HOTLIST); | |
752 | if (result < 0) { | |
753 | return result; | |
754 | } | |
755 | ||
756 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
757 | ||
758 | result = request.put_u32(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); | |
759 | if (result < 0) { | |
760 | return result; | |
761 | } | |
762 | ||
763 | struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); | |
764 | for (int i = 0; i < mParams.num_bssid; i++) { | |
765 | nlattr *attr2 = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_ELEM); | |
766 | if (attr2 == NULL) { | |
767 | return WIFI_ERROR_OUT_OF_MEMORY; | |
768 | } | |
769 | result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); | |
770 | if (result < 0) { | |
771 | return result; | |
772 | } | |
773 | result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); | |
774 | if (result < 0) { | |
775 | return result; | |
776 | } | |
777 | result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); | |
778 | if (result < 0) { | |
779 | return result; | |
780 | } | |
781 | request.attr_end(attr2); | |
782 | } | |
783 | ||
784 | request.attr_end(attr); | |
785 | request.attr_end(data); | |
786 | return result; | |
787 | } | |
788 | ||
789 | int createTeardownRequest(WifiRequest& request) { | |
790 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_RESET_BSSID_HOTLIST); | |
791 | if (result < 0) { | |
792 | return result; | |
793 | } | |
794 | ||
795 | return result; | |
796 | } | |
797 | ||
798 | int start() { | |
799 | ALOGD("Executing hotlist setup request, num = %d", mParams.num_bssid); | |
800 | WifiRequest request(familyId(), ifaceId()); | |
801 | int result = createSetupRequest(request); | |
802 | if (result < 0) { | |
803 | return result; | |
804 | } | |
805 | ||
806 | result = requestResponse(request); | |
807 | if (result < 0) { | |
808 | ALOGD("Failed to execute hotlist setup request, result = %d", result); | |
809 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); | |
810 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); | |
811 | return result; | |
812 | } | |
813 | ||
814 | ALOGD("Successfully set %d APs in the hotlist", mParams.num_bssid); | |
815 | ||
816 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); | |
817 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); | |
818 | ||
819 | return result; | |
820 | } | |
821 | ||
822 | virtual int cancel() { | |
823 | /* unregister event handler */ | |
824 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); | |
825 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); | |
826 | /* create set hotlist message with empty hotlist */ | |
827 | WifiRequest request(familyId(), ifaceId()); | |
828 | int result = createTeardownRequest(request); | |
829 | if (result < 0) { | |
830 | return result; | |
831 | } | |
832 | ||
833 | result = requestResponse(request); | |
834 | if (result < 0) { | |
835 | return result; | |
836 | } | |
837 | ||
838 | ALOGD("Successfully reset APs in current hotlist"); | |
839 | return result; | |
840 | } | |
841 | ||
842 | virtual int handleResponse(WifiEvent& reply) { | |
843 | /* Nothing to do on response! */ | |
844 | return NL_SKIP; | |
845 | } | |
846 | ||
847 | virtual int handleEvent(WifiEvent& event) { | |
848 | ALOGD("Hotlist AP event"); | |
849 | int event_id = event.get_vendor_subcmd(); | |
850 | event.log(); | |
851 | ||
852 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
853 | int len = event.get_vendor_data_len(); | |
854 | ||
855 | if (vendor_data == NULL || len == 0) { | |
856 | ALOGD("No scan results found"); | |
857 | return NL_SKIP; | |
858 | } | |
859 | ||
860 | memset(mResults, 0, sizeof(wifi_scan_result) * MAX_RESULTS); | |
861 | ||
862 | int num = len / sizeof(wifi_scan_result); | |
863 | num = min(MAX_RESULTS, num); | |
864 | memcpy(mResults, event.get_vendor_data(), num * sizeof(wifi_scan_result)); | |
865 | ||
866 | if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_FOUND) { | |
867 | ALOGD("FOUND %d hotlist APs", num); | |
868 | if (*mHandler.on_hotlist_ap_found) | |
869 | (*mHandler.on_hotlist_ap_found)(id(), num, mResults); | |
870 | } else if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_LOST) { | |
871 | ALOGD("LOST %d hotlist APs", num); | |
872 | if (*mHandler.on_hotlist_ap_lost) | |
873 | (*mHandler.on_hotlist_ap_lost)(id(), num, mResults); | |
874 | } | |
875 | return NL_SKIP; | |
876 | } | |
877 | }; | |
878 | ||
879 | wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, | |
880 | wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) | |
881 | { | |
882 | wifi_handle handle = getWifiHandle(iface); | |
883 | ||
884 | BssidHotlistCommand *cmd = new BssidHotlistCommand(iface, id, params, handler); | |
885 | wifi_register_cmd(handle, id, cmd); | |
886 | return (wifi_error)cmd->start(); | |
887 | } | |
888 | ||
889 | wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) | |
890 | { | |
891 | wifi_handle handle = getWifiHandle(iface); | |
892 | ||
893 | WifiCommand *cmd = wifi_unregister_cmd(handle, id); | |
894 | if (cmd) { | |
895 | cmd->cancel(); | |
896 | cmd->releaseRef(); | |
897 | return WIFI_SUCCESS; | |
898 | } | |
899 | ||
900 | return WIFI_ERROR_INVALID_ARGS; | |
901 | } | |
902 | ||
903 | ||
904 | ///////////////////////////////////////////////////////////////////////////// | |
905 | ||
906 | class SignificantWifiChangeCommand : public WifiCommand | |
907 | { | |
908 | typedef struct { | |
909 | mac_addr bssid; // BSSID | |
910 | wifi_channel channel; // channel frequency in MHz | |
911 | int num_rssi; // number of rssi samples | |
912 | wifi_rssi rssi[8]; // RSSI history in db | |
913 | } wifi_significant_change_result_internal; | |
914 | ||
915 | private: | |
916 | wifi_significant_change_params mParams; | |
917 | wifi_significant_change_handler mHandler; | |
918 | static const int MAX_RESULTS = 64; | |
919 | wifi_significant_change_result_internal mResultsBuffer[MAX_RESULTS]; | |
920 | wifi_significant_change_result *mResults[MAX_RESULTS]; | |
921 | public: | |
922 | SignificantWifiChangeCommand(wifi_interface_handle handle, int id, | |
923 | wifi_significant_change_params params, wifi_significant_change_handler handler) | |
924 | : WifiCommand(handle, id), mParams(params), mHandler(handler) | |
925 | { } | |
926 | ||
927 | int createSetupRequest(WifiRequest& request) { | |
928 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_SIGNIFICANT_CHANGE); | |
929 | if (result < 0) { | |
930 | return result; | |
931 | } | |
932 | ||
933 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
934 | ||
935 | result = request.put_u16(GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE, mParams.rssi_sample_size); | |
936 | if (result < 0) { | |
937 | return result; | |
938 | } | |
939 | result = request.put_u16(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); | |
940 | if (result < 0) { | |
941 | return result; | |
942 | } | |
943 | result = request.put_u16(GSCAN_ATTRIBUTE_MIN_BREACHING, mParams.min_breaching); | |
944 | if (result < 0) { | |
945 | return result; | |
946 | } | |
947 | ||
948 | struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS); | |
949 | ||
950 | for (int i = 0; i < mParams.num_bssid; i++) { | |
951 | ||
952 | nlattr *attr2 = request.attr_start(i); | |
953 | if (attr2 == NULL) { | |
954 | return WIFI_ERROR_OUT_OF_MEMORY; | |
955 | } | |
956 | result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); | |
957 | if (result < 0) { | |
958 | return result; | |
959 | } | |
960 | result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); | |
961 | if (result < 0) { | |
962 | return result; | |
963 | } | |
964 | result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); | |
965 | if (result < 0) { | |
966 | return result; | |
967 | } | |
968 | request.attr_end(attr2); | |
969 | } | |
970 | ||
971 | request.attr_end(attr); | |
972 | request.attr_end(data); | |
973 | ||
974 | return result; | |
975 | } | |
976 | ||
977 | int createTeardownRequest(WifiRequest& request) { | |
978 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_RESET_SIGNIFICANT_CHANGE); | |
979 | if (result < 0) { | |
980 | return result; | |
981 | } | |
982 | ||
983 | return result; | |
984 | } | |
985 | ||
986 | int start() { | |
987 | ALOGD("Set significant wifi change"); | |
988 | WifiRequest request(familyId(), ifaceId()); | |
989 | ||
990 | int result = createSetupRequest(request); | |
991 | if (result < 0) { | |
992 | return result; | |
993 | } | |
994 | ||
995 | result = requestResponse(request); | |
996 | if (result < 0) { | |
997 | ALOGD("failed to set significant wifi change %d", result); | |
998 | return result; | |
999 | } | |
1000 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); | |
1001 | ||
1002 | return result; | |
1003 | } | |
1004 | ||
1005 | virtual int cancel() { | |
1006 | /* unregister event handler */ | |
1007 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); | |
1008 | ||
1009 | /* create set significant change monitor message with empty hotlist */ | |
1010 | WifiRequest request(familyId(), ifaceId()); | |
1011 | ||
1012 | int result = createTeardownRequest(request); | |
1013 | if (result < 0) { | |
1014 | return result; | |
1015 | } | |
1016 | ||
1017 | result = requestResponse(request); | |
1018 | if (result < 0) { | |
1019 | return result; | |
1020 | } | |
1021 | ||
1022 | ALOGD("successfully reset significant wifi change"); | |
1023 | return result; | |
1024 | } | |
1025 | ||
1026 | virtual int handleResponse(WifiEvent& reply) { | |
1027 | /* Nothing to do on response! */ | |
1028 | return NL_SKIP; | |
1029 | } | |
1030 | ||
1031 | virtual int handleEvent(WifiEvent& event) { | |
1032 | ALOGD("Got a significant wifi change event"); | |
1033 | ||
1034 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
1035 | int len = event.get_vendor_data_len(); | |
1036 | ||
1037 | if (vendor_data == NULL || len == 0) { | |
1038 | ALOGD("No scan results found"); | |
1039 | return NL_SKIP; | |
1040 | } | |
1041 | ||
1042 | typedef struct { | |
1043 | uint16_t channel; | |
1044 | mac_addr bssid; | |
1045 | int16_t rssi_history[8]; | |
1046 | } ChangeInfo; | |
1047 | ||
1048 | int num = min(len / sizeof(ChangeInfo), MAX_RESULTS); | |
1049 | ChangeInfo *ci = (ChangeInfo *)event.get_vendor_data(); | |
1050 | ||
1051 | for (int i = 0; i < num; i++) { | |
1052 | memcpy(mResultsBuffer[i].bssid, ci[i].bssid, sizeof(mac_addr)); | |
1053 | mResultsBuffer[i].channel = ci[i].channel; | |
acc9b163 JPS |
1054 | /* Driver sends N samples and the rest 8-N are filled 0x7FFF |
1055 | * N = no of rssi samples to average sent in significant change request. */ | |
7753f181 DD |
1056 | int num_rssi = 0; |
1057 | for (int j = 0; j < 8; j++) { | |
1058 | if (ci[i].rssi_history[j] == 0x7FFF) { | |
1059 | num_rssi = j; | |
1060 | break; | |
1061 | } | |
1062 | mResultsBuffer[i].rssi[j] = (int) ci[i].rssi_history[j]; | |
1063 | } | |
1064 | mResultsBuffer[i].num_rssi = num_rssi; | |
1065 | mResults[i] = reinterpret_cast<wifi_significant_change_result *>(&(mResultsBuffer[i])); | |
1066 | } | |
1067 | ||
1068 | ALOGD("Retrieved %d scan results", num); | |
1069 | ||
1070 | if (num != 0) { | |
1071 | (*mHandler.on_significant_change)(id(), num, mResults); | |
1072 | } else { | |
1073 | ALOGW("No significant change reported"); | |
1074 | } | |
1075 | ||
1076 | return NL_SKIP; | |
1077 | } | |
1078 | }; | |
1079 | ||
1080 | wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, | |
1081 | wifi_significant_change_params params, wifi_significant_change_handler handler) | |
1082 | { | |
1083 | wifi_handle handle = getWifiHandle(iface); | |
1084 | ||
1085 | SignificantWifiChangeCommand *cmd = new SignificantWifiChangeCommand( | |
1086 | iface, id, params, handler); | |
1087 | wifi_register_cmd(handle, id, cmd); | |
1088 | return (wifi_error)cmd->start(); | |
1089 | } | |
1090 | ||
1091 | wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) | |
1092 | { | |
1093 | wifi_handle handle = getWifiHandle(iface); | |
1094 | ||
1095 | WifiCommand *cmd = wifi_unregister_cmd(handle, id); | |
1096 | if (cmd) { | |
1097 | cmd->cancel(); | |
1098 | cmd->releaseRef(); | |
1099 | return WIFI_SUCCESS; | |
1100 | } | |
1101 | ||
1102 | return WIFI_ERROR_INVALID_ARGS; | |
1103 | } | |
c15187c1 | 1104 | |
6ff2d683 JPS |
1105 | class ePNOCommand : public WifiCommand |
1106 | { | |
1107 | private: | |
c15187c1 | 1108 | wifi_epno_params *epno_params; |
6ff2d683 JPS |
1109 | wifi_epno_handler mHandler; |
1110 | wifi_scan_result mResults; | |
1111 | public: | |
1112 | ePNOCommand(wifi_interface_handle handle, int id, | |
c15187c1 | 1113 | wifi_epno_params *params, wifi_epno_handler handler) |
6ff2d683 JPS |
1114 | : WifiCommand(handle, id), mHandler(handler) |
1115 | { | |
c15187c1 | 1116 | epno_params = params; |
6ff2d683 JPS |
1117 | } |
1118 | ||
1119 | int createSetupRequest(WifiRequest& request) { | |
c15187c1 | 1120 | int rssi_threshold; |
6ff2d683 JPS |
1121 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_EPNO_LIST); |
1122 | if (result < 0) { | |
1123 | return result; | |
1124 | } | |
1125 | ||
1126 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
c15187c1 JPS |
1127 | if (epno_params == NULL) { |
1128 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_NUM, 0); | |
1129 | if (result < 0) { | |
1130 | return result; | |
1131 | } | |
1132 | request.attr_end(data); | |
1133 | return result; | |
1134 | } | |
6ff2d683 | 1135 | |
c15187c1 JPS |
1136 | rssi_threshold = epno_params->min5GHz_rssi < epno_params->min24GHz_rssi ? epno_params->min5GHz_rssi : epno_params->min24GHz_rssi; |
1137 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_NUM, epno_params->num_networks); | |
6ff2d683 JPS |
1138 | if (result < 0) { |
1139 | return result; | |
1140 | } | |
1141 | ||
1142 | struct nlattr * attr = request.attr_start(EPNO_ATTRIBUTE_SSID_LIST); | |
c15187c1 | 1143 | for (int i = 0; i < epno_params->num_networks; i++) { |
6ff2d683 JPS |
1144 | nlattr *attr2 = request.attr_start(i); |
1145 | if (attr2 == NULL) { | |
1146 | return WIFI_ERROR_OUT_OF_MEMORY; | |
1147 | } | |
c15187c1 JPS |
1148 | result = request.put(EPNO_ATTRIBUTE_SSID, epno_params->networks[i].ssid, 32); |
1149 | ALOGI("ePNO [SSID:%s rssi_thresh:%d flags:%d auth:%d]", epno_params->networks[i].ssid, | |
1150 | (signed char)rssi_threshold, epno_params->networks[i].flags, | |
1151 | epno_params->networks[i].auth_bit_field); | |
6ff2d683 JPS |
1152 | if (result < 0) { |
1153 | return result; | |
1154 | } | |
c15187c1 | 1155 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_LEN, strlen(epno_params->networks[i].ssid)); |
6ff2d683 JPS |
1156 | if (result < 0) { |
1157 | return result; | |
1158 | } | |
1159 | ||
c15187c1 | 1160 | result = request.put_u8(EPNO_ATTRIBUTE_RSSI, rssi_threshold); |
6ff2d683 JPS |
1161 | if (result < 0) { |
1162 | return result; | |
1163 | } | |
c15187c1 | 1164 | result = request.put_u8(EPNO_ATTRIBUTE_FLAGS, epno_params->networks[i].flags); |
6ff2d683 JPS |
1165 | if (result < 0) { |
1166 | return result; | |
1167 | } | |
c15187c1 | 1168 | result = request.put_u8(EPNO_ATTRIBUTE_AUTH, epno_params->networks[i].auth_bit_field); |
6ff2d683 JPS |
1169 | if (result < 0) { |
1170 | return result; | |
1171 | } | |
1172 | request.attr_end(attr2); | |
1173 | } | |
1174 | ||
1175 | request.attr_end(attr); | |
1176 | request.attr_end(data); | |
1177 | return result; | |
1178 | } | |
1179 | ||
1180 | int start() { | |
c15187c1 | 1181 | ALOGI("ePNO num_network=%d", epno_params ? epno_params->num_networks : 0); |
6ff2d683 JPS |
1182 | WifiRequest request(familyId(), ifaceId()); |
1183 | int result = createSetupRequest(request); | |
1184 | if (result < 0) { | |
1185 | return result; | |
1186 | } | |
1187 | ||
1188 | result = requestResponse(request); | |
1189 | if (result < 0) { | |
1190 | ALOGI("Failed: ePNO setup request, result = %d", result); | |
1191 | unregisterVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); | |
1192 | return result; | |
1193 | } | |
1194 | ||
c15187c1 JPS |
1195 | if (epno_params) { |
1196 | ALOGI("Successfully set %d SSIDs for ePNO", epno_params->num_networks); | |
1197 | registerVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); | |
1198 | } else { | |
1199 | ALOGI("Successfully reset ePNO"); | |
1200 | } | |
6ff2d683 JPS |
1201 | return result; |
1202 | } | |
1203 | ||
1204 | virtual int cancel() { | |
1205 | /* unregister event handler */ | |
1206 | unregisterVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); | |
1207 | return 0; | |
1208 | } | |
1209 | ||
1210 | virtual int handleResponse(WifiEvent& reply) { | |
1211 | /* Nothing to do on response! */ | |
1212 | return NL_SKIP; | |
1213 | } | |
1214 | ||
1215 | virtual int handleEvent(WifiEvent& event) { | |
1216 | ALOGI("ePNO event"); | |
1217 | int event_id = event.get_vendor_subcmd(); | |
1218 | // event.log(); | |
1219 | ||
1220 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
1221 | int len = event.get_vendor_data_len(); | |
1222 | ||
1223 | if (vendor_data == NULL || len == 0) { | |
1224 | ALOGI("No scan results found"); | |
1225 | return NL_SKIP; | |
1226 | } | |
1227 | ||
1228 | mResults = *(wifi_scan_result *) event.get_vendor_data(); | |
1229 | if (*mHandler.on_network_found) | |
1230 | (*mHandler.on_network_found)(id(), 1, &mResults); | |
1231 | return NL_SKIP; | |
1232 | } | |
1233 | }; | |
1234 | ||
1235 | wifi_error wifi_set_epno_list(wifi_request_id id, | |
1236 | wifi_interface_handle iface, | |
c15187c1 | 1237 | const wifi_epno_params *epno_params, |
6ff2d683 JPS |
1238 | wifi_epno_handler handler) |
1239 | { | |
c15187c1 JPS |
1240 | wifi_handle handle = getWifiHandle(iface); |
1241 | ||
1242 | ePNOCommand *cmd = new ePNOCommand(iface, id, (wifi_epno_params *)epno_params, handler); | |
1243 | wifi_register_cmd(handle, id, cmd); | |
1244 | wifi_error result = (wifi_error)cmd->start(); | |
1245 | if (result != WIFI_SUCCESS) { | |
1246 | wifi_unregister_cmd(handle, id); | |
1247 | } | |
1248 | return result; | |
1249 | } | |
1250 | ||
1251 | wifi_error wifi_reset_epno_list(wifi_request_id id, wifi_interface_handle iface) | |
1252 | { | |
1253 | wifi_handle handle = getWifiHandle(iface); | |
1254 | wifi_epno_handler handler; | |
1255 | ||
1256 | handler.on_network_found = NULL; | |
1257 | ePNOCommand *cmd = new ePNOCommand(iface, id, NULL, handler); | |
1258 | wifi_register_cmd(handle, id, cmd); | |
1259 | wifi_error result = (wifi_error)cmd->start(); | |
1260 | if (result != WIFI_SUCCESS) { | |
1261 | wifi_unregister_cmd(handle, id); | |
1262 | } | |
1263 | return result; | |
6ff2d683 JPS |
1264 | } |
1265 | ||
1266 | class HsListCommand : public WifiCommand | |
1267 | { | |
1268 | int num_hs; | |
1269 | wifi_passpoint_network *mNetworks; | |
1270 | wifi_passpoint_event_handler mHandler; | |
1271 | public: | |
1272 | HsListCommand(wifi_request_id id, wifi_interface_handle iface, | |
1273 | int num, wifi_passpoint_network *hs_list, wifi_passpoint_event_handler handler) | |
1274 | : WifiCommand(iface, id), num_hs(num), mNetworks(hs_list), | |
1275 | mHandler(handler) | |
1276 | { | |
1277 | } | |
1278 | ||
1279 | HsListCommand(wifi_request_id id, wifi_interface_handle iface, | |
1280 | int num) | |
1281 | : WifiCommand(iface, id), num_hs(num), mNetworks(NULL) | |
1282 | { | |
1283 | } | |
1284 | ||
1285 | int createRequest(WifiRequest& request, int val) { | |
1286 | int result; | |
1287 | ||
1288 | if (val) { | |
1289 | result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_HS_LIST); | |
1290 | result = request.put_u32(EPNO_ATTRIBUTE_HS_NUM, num_hs); | |
1291 | if (result < 0) { | |
1292 | return result; | |
1293 | } | |
1294 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
1295 | ||
1296 | struct nlattr * attr = request.attr_start(EPNO_ATTRIBUTE_HS_PARAM_LIST); | |
1297 | for (int i = 0; i < num_hs; i++) { | |
1298 | nlattr *attr2 = request.attr_start(i); | |
1299 | if (attr2 == NULL) { | |
1300 | return WIFI_ERROR_OUT_OF_MEMORY; | |
1301 | } | |
1302 | result = request.put_u32(EPNO_ATTRIBUTE_HS_ID, mNetworks[i].id); | |
1303 | if (result < 0) { | |
1304 | return result; | |
1305 | } | |
1306 | result = request.put(EPNO_ATTRIBUTE_HS_REALM, mNetworks[i].realm, 256); | |
1307 | if (result < 0) { | |
1308 | return result; | |
1309 | } | |
1310 | result = request.put(EPNO_ATTRIBUTE_HS_CONSORTIUM_IDS, mNetworks[i].roamingConsortiumIds, 128); | |
1311 | if (result < 0) { | |
1312 | return result; | |
1313 | } | |
1314 | result = request.put(EPNO_ATTRIBUTE_HS_PLMN, mNetworks[i].plmn, 3); | |
1315 | if (result < 0) { | |
1316 | return result; | |
1317 | } | |
1318 | request.attr_end(attr2); | |
1319 | } | |
1320 | request.attr_end(attr); | |
1321 | request.attr_end(data); | |
1322 | }else { | |
1323 | result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_RESET_HS_LIST); | |
1324 | if (result < 0) { | |
1325 | return result; | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | return WIFI_SUCCESS; | |
1330 | } | |
1331 | ||
1332 | int start() { | |
1333 | ||
1334 | WifiRequest request(familyId(), ifaceId()); | |
1335 | int result = createRequest(request, num_hs); | |
1336 | if (result != WIFI_SUCCESS) { | |
1337 | ALOGE("failed to create request; result = %d", result); | |
1338 | return result; | |
1339 | } | |
1340 | ||
1341 | registerVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
1342 | ||
1343 | result = requestResponse(request); | |
1344 | if (result != WIFI_SUCCESS) { | |
1345 | ALOGE("failed to set ANQPO networks; result = %d", result); | |
1346 | unregisterVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
1347 | return result; | |
1348 | } | |
1349 | ||
1350 | return result; | |
1351 | } | |
1352 | ||
1353 | virtual int cancel() { | |
1354 | ||
1355 | WifiRequest request(familyId(), ifaceId()); | |
1356 | int result = createRequest(request, 0); | |
1357 | if (result != WIFI_SUCCESS) { | |
1358 | ALOGE("failed to create request; result = %d", result); | |
1359 | } else { | |
1360 | result = requestResponse(request); | |
1361 | if (result != WIFI_SUCCESS) { | |
1362 | ALOGE("failed to reset ANQPO networks;result = %d", result); | |
1363 | } | |
1364 | } | |
1365 | ||
1366 | unregisterVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
1367 | return WIFI_SUCCESS; | |
1368 | } | |
1369 | ||
1370 | virtual int handleResponse(WifiEvent& reply) { | |
1371 | ALOGD("Request complete!"); | |
1372 | /* Nothing to do on response! */ | |
1373 | return NL_SKIP; | |
1374 | } | |
1375 | ||
1376 | virtual int handleEvent(WifiEvent& event) { | |
1377 | ||
1378 | ALOGI("hotspot matched event"); | |
1379 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
1380 | unsigned int len = event.get_vendor_data_len(); | |
1381 | if (vendor_data == NULL || len < sizeof(wifi_scan_result)) { | |
1382 | ALOGE("ERROR: No scan results found"); | |
1383 | return NL_SKIP; | |
1384 | } | |
1385 | ||
1386 | wifi_scan_result *result = (wifi_scan_result *)event.get_vendor_data(); | |
1387 | byte *anqp = (byte *)result + offsetof(wifi_scan_result, ie_data) + result->ie_length; | |
1388 | int networkId = *(int *)anqp; | |
1389 | anqp += sizeof(int); | |
1390 | int anqp_len = *(u16 *)anqp; | |
1391 | anqp += sizeof(u16); | |
1392 | ||
1393 | ALOGI("%-32s\t", result->ssid); | |
1394 | ||
1395 | ALOGI("%02x:%02x:%02x:%02x:%02x:%02x ", result->bssid[0], result->bssid[1], | |
1396 | result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5]); | |
1397 | ||
1398 | ALOGI("%d\t", result->rssi); | |
1399 | ALOGI("%d\t", result->channel); | |
1400 | ALOGI("%lld\t", result->ts); | |
1401 | ALOGI("%lld\t", result->rtt); | |
1402 | ALOGI("%lld\n", result->rtt_sd); | |
1403 | ||
1404 | if(*mHandler.on_passpoint_network_found) | |
1405 | (*mHandler.on_passpoint_network_found)(id(), networkId, result, anqp_len, anqp); | |
1406 | ||
1407 | return NL_SKIP; | |
1408 | } | |
1409 | }; | |
1410 | ||
1411 | wifi_error wifi_set_passpoint_list(wifi_request_id id, wifi_interface_handle iface, int num, | |
1412 | wifi_passpoint_network *networks, wifi_passpoint_event_handler handler) | |
1413 | { | |
1414 | wifi_handle handle = getWifiHandle(iface); | |
1415 | HsListCommand *cmd = new HsListCommand(id, iface, num, networks, handler); | |
1416 | ||
1417 | wifi_register_cmd(handle, id, cmd); | |
1418 | wifi_error result = (wifi_error)cmd->start(); | |
1419 | if (result != WIFI_SUCCESS) { | |
1420 | wifi_unregister_cmd(handle, id); | |
1421 | } | |
1422 | return result; | |
1423 | } | |
1424 | ||
1425 | wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface) | |
1426 | { | |
1427 | wifi_handle handle = getWifiHandle(iface); | |
1428 | wifi_error result; | |
1429 | HsListCommand *cmd = (HsListCommand *)(wifi_get_cmd(handle, id)); | |
1430 | ||
1431 | if (cmd == NULL) { | |
1432 | cmd = new HsListCommand(id, iface, 0); | |
1433 | wifi_register_cmd(handle, id, cmd); | |
1434 | } | |
1435 | result = (wifi_error)cmd->cancel(); | |
1436 | wifi_unregister_cmd(handle, id); | |
1437 | return result; | |
1438 | } | |
c15187c1 | 1439 | |
10d7569e PG |
1440 | class BssidBlacklistCommand : public WifiCommand |
1441 | { | |
1442 | private: | |
1443 | wifi_bssid_params *mParams; | |
1444 | public: | |
1445 | BssidBlacklistCommand(wifi_interface_handle handle, int id, | |
1446 | wifi_bssid_params *params) | |
1447 | : WifiCommand(handle, id), mParams(params) | |
1448 | { } | |
1449 | int createRequest(WifiRequest& request) { | |
1450 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_BSSID_BLACKLIST); | |
1451 | if (result < 0) { | |
1452 | return result; | |
1453 | } | |
1454 | ||
1455 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
1456 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BSSID, mParams->num_bssid); | |
1457 | if (result < 0) { | |
1458 | return result; | |
1459 | } | |
1460 | ||
1461 | for (int i = 0; i < mParams->num_bssid; i++) { | |
1462 | result = request.put_addr(GSCAN_ATTRIBUTE_BLACKLIST_BSSID, mParams->bssids[i]); | |
1463 | if (result < 0) { | |
1464 | return result; | |
1465 | } | |
1466 | } | |
1467 | request.attr_end(data); | |
1468 | return result; | |
1469 | } | |
1470 | ||
1471 | int start() { | |
1472 | ALOGD("Executing bssid blacklist request, num = %d", mParams->num_bssid); | |
1473 | WifiRequest request(familyId(), ifaceId()); | |
1474 | int result = createRequest(request); | |
1475 | if (result < 0) { | |
1476 | return result; | |
1477 | } | |
1478 | ||
1479 | result = requestResponse(request); | |
1480 | if (result < 0) { | |
1481 | ALOGE("Failed to execute bssid blacklist request, result = %d", result); | |
1482 | return result; | |
1483 | } | |
1484 | ||
1485 | ALOGI("Successfully added %d blacklist bssids", mParams->num_bssid); | |
1486 | return result; | |
1487 | } | |
1488 | ||
1489 | ||
1490 | virtual int handleResponse(WifiEvent& reply) { | |
1491 | /* Nothing to do on response! */ | |
1492 | return NL_SKIP; | |
1493 | } | |
1494 | }; | |
1495 | ||
1496 | wifi_error wifi_set_bssid_blacklist(wifi_request_id id, wifi_interface_handle iface, | |
1497 | wifi_bssid_params params) | |
1498 | { | |
1499 | wifi_handle handle = getWifiHandle(iface); | |
1500 | ||
1501 | BssidBlacklistCommand *cmd = new BssidBlacklistCommand(iface, id, ¶ms); | |
1502 | wifi_error result = (wifi_error)cmd->start(); | |
1503 | //release the reference of command as well | |
1504 | cmd->releaseRef(); | |
1505 | return result; | |
1506 | } |