Commit | Line | Data |
---|---|---|
7753f181 | 1 | #include <stdint.h> |
6ff2d683 | 2 | #include <stddef.h> |
7753f181 DD |
3 | #include <fcntl.h> |
4 | #include <sys/socket.h> | |
5 | #include <netlink/genl/genl.h> | |
6 | #include <netlink/genl/family.h> | |
7 | #include <netlink/genl/ctrl.h> | |
8 | #include <linux/rtnetlink.h> | |
9 | #include <netpacket/packet.h> | |
10 | #include <linux/filter.h> | |
11 | #include <linux/errqueue.h> | |
12 | ||
13 | #include <linux/pkt_sched.h> | |
14 | #include <netlink/object-api.h> | |
15 | #include <netlink/netlink.h> | |
16 | #include <netlink/socket.h> | |
17 | #include <netlink/handlers.h> | |
18 | ||
19 | #include "sync.h" | |
20 | ||
7753f181 DD |
21 | #include <utils/Log.h> |
22 | ||
23 | #include "wifi_hal.h" | |
24 | #include "common.h" | |
25 | #include "cpp_bindings.h" | |
26 | ||
6ff2d683 | 27 | typedef enum { |
1fc70afb MG |
28 | EPNO_ATTRIBUTE_MINIMUM_5G_RSSI, |
29 | EPNO_ATTRIBUTE_MINIMUM_2G_RSSI, | |
30 | EPNO_ATTRIBUTE_INITIAL_SCORE_MAX, | |
31 | EPNO_ATTRIBUTE_CUR_CONN_BONUS, | |
32 | EPNO_ATTRIBUTE_SAME_NETWORK_BONUS, | |
33 | EPNO_ATTRIBUTE_SECURE_BONUS, | |
34 | EPNO_ATTRIBUTE_5G_BONUS, | |
6ff2d683 | 35 | EPNO_ATTRIBUTE_SSID_NUM, |
1fc70afb | 36 | EPNO_ATTRIBUTE_SSID_LIST, |
6ff2d683 JPS |
37 | EPNO_ATTRIBUTE_SSID, |
38 | EPNO_ATTRIBUTE_SSID_LEN, | |
6ff2d683 JPS |
39 | EPNO_ATTRIBUTE_FLAGS, |
40 | EPNO_ATTRIBUTE_AUTH, | |
41 | EPNO_ATTRIBUTE_MAX | |
42 | } EPNO_ATTRIBUTE; | |
43 | ||
44 | typedef enum { | |
45 | EPNO_ATTRIBUTE_HS_PARAM_LIST, | |
46 | EPNO_ATTRIBUTE_HS_NUM, | |
47 | EPNO_ATTRIBUTE_HS_ID, | |
48 | EPNO_ATTRIBUTE_HS_REALM, | |
49 | EPNO_ATTRIBUTE_HS_CONSORTIUM_IDS, | |
50 | EPNO_ATTRIBUTE_HS_PLMN, | |
51 | EPNO_ATTRIBUTE_HS_MAX | |
52 | } EPNO_HS_ATTRIBUTE; | |
53 | ||
7753f181 DD |
54 | |
55 | class GetCapabilitiesCommand : public WifiCommand | |
56 | { | |
57 | wifi_gscan_capabilities *mCapabilities; | |
58 | public: | |
59 | GetCapabilitiesCommand(wifi_interface_handle iface, wifi_gscan_capabilities *capabitlites) | |
60 | : WifiCommand(iface, 0), mCapabilities(capabitlites) | |
61 | { | |
62 | memset(mCapabilities, 0, sizeof(*mCapabilities)); | |
63 | } | |
64 | ||
65 | virtual int create() { | |
7753f181 DD |
66 | int ret = mMsg.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_CAPABILITIES); |
67 | if (ret < 0) { | |
9a2ccc1c | 68 | ALOGE("NL message creation failed"); |
7753f181 DD |
69 | return ret; |
70 | } | |
71 | ||
72 | return ret; | |
73 | } | |
74 | ||
75 | protected: | |
76 | virtual int handleResponse(WifiEvent& reply) { | |
77 | ||
7753f181 | 78 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { |
d20effd2 | 79 | ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); |
7753f181 DD |
80 | return NL_SKIP; |
81 | } | |
82 | ||
7753f181 DD |
83 | void *data = reply.get_vendor_data(); |
84 | int len = reply.get_vendor_data_len(); | |
85 | ||
7753f181 DD |
86 | memcpy(mCapabilities, data, min(len, (int) sizeof(*mCapabilities))); |
87 | ||
88 | return NL_OK; | |
89 | } | |
90 | }; | |
91 | ||
92 | ||
93 | wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, | |
94 | wifi_gscan_capabilities *capabilities) | |
95 | { | |
96 | GetCapabilitiesCommand command(handle, capabilities); | |
97 | return (wifi_error) command.requestResponse(); | |
98 | } | |
99 | ||
100 | class GetChannelListCommand : public WifiCommand | |
101 | { | |
102 | wifi_channel *channels; | |
103 | int max_channels; | |
104 | int *num_channels; | |
105 | int band; | |
106 | public: | |
107 | GetChannelListCommand(wifi_interface_handle iface, wifi_channel *channel_buf, int *ch_num, | |
108 | int num_max_ch, int band) | |
109 | : WifiCommand(iface, 0), channels(channel_buf), max_channels(num_max_ch), num_channels(ch_num), | |
110 | band(band) | |
111 | { | |
112 | memset(channels, 0, sizeof(wifi_channel) * max_channels); | |
113 | } | |
114 | virtual int create() { | |
7753f181 DD |
115 | int ret = mMsg.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_VALID_CHANNELS); |
116 | if (ret < 0) { | |
117 | return ret; | |
118 | } | |
119 | ||
120 | nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); | |
121 | ret = mMsg.put_u32(GSCAN_ATTRIBUTE_BAND, band); | |
122 | if (ret < 0) { | |
123 | return ret; | |
124 | } | |
125 | ||
126 | mMsg.attr_end(data); | |
127 | ||
128 | return ret; | |
129 | } | |
130 | ||
131 | protected: | |
132 | virtual int handleResponse(WifiEvent& reply) { | |
133 | ||
7753f181 | 134 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { |
d20effd2 | 135 | ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); |
7753f181 DD |
136 | return NL_SKIP; |
137 | } | |
138 | ||
7753f181 DD |
139 | int num_channels_to_copy = 0; |
140 | ||
141 | nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
142 | int len = reply.get_vendor_data_len(); | |
143 | ||
7753f181 DD |
144 | if (vendor_data == NULL || len == 0) { |
145 | ALOGE("no vendor data in GetChannelList response; ignoring it"); | |
146 | return NL_SKIP; | |
147 | } | |
148 | ||
149 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
150 | if (it.get_type() == GSCAN_ATTRIBUTE_NUM_CHANNELS) { | |
151 | num_channels_to_copy = it.get_u32(); | |
c0b490a2 | 152 | /*ALOGD("Got channel list with %d channels", num_channels_to_copy);*/ |
7753f181 DD |
153 | if(num_channels_to_copy > max_channels) |
154 | num_channels_to_copy = max_channels; | |
155 | *num_channels = num_channels_to_copy; | |
156 | } else if (it.get_type() == GSCAN_ATTRIBUTE_CHANNEL_LIST && num_channels_to_copy) { | |
157 | memcpy(channels, it.get_data(), sizeof(int) * num_channels_to_copy); | |
158 | } else { | |
159 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
160 | it.get_type(), it.get_len()); | |
161 | } | |
162 | } | |
163 | ||
164 | return NL_OK; | |
165 | } | |
166 | }; | |
167 | ||
168 | wifi_error wifi_get_valid_channels(wifi_interface_handle handle, | |
169 | int band, int max_channels, wifi_channel *channels, int *num_channels) | |
170 | { | |
171 | GetChannelListCommand command(handle, channels, num_channels, | |
172 | max_channels, band); | |
173 | return (wifi_error) command.requestResponse(); | |
174 | } | |
175 | ///////////////////////////////////////////////////////////////////////////// | |
176 | ||
177 | /* helper functions */ | |
178 | ||
8094868d | 179 | /* |
7753f181 DD |
180 | static int parseScanResults(wifi_scan_result *results, int num, nlattr *attr) |
181 | { | |
182 | memset(results, 0, sizeof(wifi_scan_result) * num); | |
183 | ||
184 | int i = 0; | |
185 | for (nl_iterator it(attr); it.has_next() && i < num; it.next(), i++) { | |
186 | ||
7753f181 DD |
187 | nlattr *sc_data = (nlattr *) it.get_data(); |
188 | wifi_scan_result *result = results + i; | |
189 | ||
190 | for (nl_iterator it2(sc_data); it2.has_next(); it2.next()) { | |
191 | int type = it2.get_type(); | |
192 | if (type == GSCAN_ATTRIBUTE_SSID) { | |
193 | strncpy(result->ssid, (char *) it2.get_data(), it2.get_len()); | |
194 | result->ssid[it2.get_len()] = 0; | |
195 | } else if (type == GSCAN_ATTRIBUTE_BSSID) { | |
196 | memcpy(result->bssid, (byte *) it2.get_data(), sizeof(mac_addr)); | |
197 | } else if (type == GSCAN_ATTRIBUTE_TIMESTAMP) { | |
198 | result->ts = it2.get_u64(); | |
199 | } else if (type == GSCAN_ATTRIBUTE_CHANNEL) { | |
200 | result->ts = it2.get_u16(); | |
201 | } else if (type == GSCAN_ATTRIBUTE_RSSI) { | |
202 | result->rssi = it2.get_u8(); | |
203 | } else if (type == GSCAN_ATTRIBUTE_RTT) { | |
204 | result->rtt = it2.get_u64(); | |
205 | } else if (type == GSCAN_ATTRIBUTE_RTTSD) { | |
206 | result->rtt_sd = it2.get_u64(); | |
207 | } | |
208 | } | |
209 | ||
210 | } | |
211 | ||
212 | if (i >= num) { | |
213 | ALOGE("Got too many results; skipping some"); | |
214 | } | |
215 | ||
216 | return i; | |
217 | } | |
8094868d | 218 | */ |
7753f181 DD |
219 | |
220 | int createFeatureRequest(WifiRequest& request, int subcmd) { | |
221 | ||
222 | int result = request.create(GOOGLE_OUI, subcmd); | |
223 | if (result < 0) { | |
224 | return result; | |
225 | } | |
226 | ||
227 | return WIFI_SUCCESS; | |
228 | } | |
229 | ||
230 | class ScanCommand : public WifiCommand | |
231 | { | |
232 | wifi_scan_cmd_params *mParams; | |
233 | wifi_scan_result_handler mHandler; | |
234 | static unsigned mGlobalFullScanBuckets; | |
7753f181 DD |
235 | public: |
236 | ScanCommand(wifi_interface_handle iface, int id, wifi_scan_cmd_params *params, | |
237 | wifi_scan_result_handler handler) | |
8094868d | 238 | : WifiCommand(iface, id), mParams(params), mHandler(handler) |
7753f181 DD |
239 | { } |
240 | ||
241 | int createSetupRequest(WifiRequest& request) { | |
242 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN); | |
243 | if (result < 0) { | |
244 | return result; | |
245 | } | |
246 | ||
247 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
248 | result = request.put_u32(GSCAN_ATTRIBUTE_BASE_PERIOD, mParams->base_period); | |
249 | if (result < 0) { | |
250 | return result; | |
251 | } | |
252 | ||
acc9b163 | 253 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, mParams->max_ap_per_scan); |
7753f181 DD |
254 | if (result < 0) { |
255 | return result; | |
256 | } | |
257 | ||
258 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD, mParams->report_threshold_percent); | |
259 | if (result < 0) { | |
260 | return result; | |
261 | } | |
262 | ||
263 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD_NUM_SCANS, mParams->report_threshold_num_scans); | |
264 | if (result < 0) { | |
265 | return result; | |
266 | } | |
267 | ||
268 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BUCKETS, mParams->num_buckets); | |
269 | if (result < 0) { | |
270 | return result; | |
271 | } | |
272 | ||
273 | for (int i = 0; i < mParams->num_buckets; i++) { | |
274 | nlattr * bucket = request.attr_start(i); // next bucket | |
275 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_ID, mParams->buckets[i].bucket); | |
276 | if (result < 0) { | |
277 | return result; | |
278 | } | |
279 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_PERIOD, mParams->buckets[i].period); | |
280 | if (result < 0) { | |
281 | return result; | |
282 | } | |
283 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKETS_BAND, | |
284 | mParams->buckets[i].band); | |
285 | if (result < 0) { | |
286 | return result; | |
287 | } | |
288 | ||
f3a9f24c MG |
289 | if (mParams->buckets[i].report_events == 0) { |
290 | mParams->buckets[i].report_events = REPORT_EVENTS_EACH_SCAN; | |
291 | } | |
7753f181 DD |
292 | result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_EVENTS, |
293 | mParams->buckets[i].report_events); | |
294 | if (result < 0) { | |
295 | return result; | |
296 | } | |
297 | ||
298 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, | |
299 | mParams->buckets[i].num_channels); | |
300 | if (result < 0) { | |
301 | return result; | |
302 | } | |
303 | ||
0e19df50 | 304 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_EXPONENT, |
acc9b163 | 305 | mParams->buckets[i].base); |
0e19df50 JPS |
306 | if (result < 0) { |
307 | return result; | |
308 | } | |
309 | ||
310 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, | |
311 | mParams->buckets[i].max_period); | |
312 | if (result < 0) { | |
313 | return result; | |
314 | } | |
315 | ||
316 | result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT, | |
317 | mParams->buckets[i].step_count); | |
318 | if (result < 0) { | |
319 | return result; | |
320 | } | |
321 | ||
7753f181 DD |
322 | if (mParams->buckets[i].num_channels) { |
323 | nlattr *channels = request.attr_start(GSCAN_ATTRIBUTE_BUCKET_CHANNELS); | |
324 | for (int j = 0; j < mParams->buckets[i].num_channels; j++) { | |
325 | result = request.put_u32(j, mParams->buckets[i].channels[j].channel); | |
326 | if (result < 0) { | |
327 | return result; | |
328 | } | |
329 | } | |
330 | request.attr_end(channels); | |
331 | } | |
332 | ||
333 | request.attr_end(bucket); | |
334 | } | |
335 | ||
336 | request.attr_end(data); | |
337 | return WIFI_SUCCESS; | |
338 | } | |
339 | ||
340 | int createStartRequest(WifiRequest& request) { | |
341 | return createFeatureRequest(request, SLSI_NL80211_VENDOR_SUBCMD_ADD_GSCAN); | |
342 | } | |
343 | ||
344 | int createStopRequest(WifiRequest& request) { | |
345 | return createFeatureRequest(request, SLSI_NL80211_VENDOR_SUBCMD_DEL_GSCAN); | |
346 | } | |
347 | ||
348 | int start() { | |
c0b490a2 | 349 | ALOGD("starting Gscan"); |
7753f181 DD |
350 | WifiRequest request(familyId(), ifaceId()); |
351 | int result = createSetupRequest(request); | |
352 | if (result != WIFI_SUCCESS) { | |
353 | ALOGE("failed to create setup request; result = %d", result); | |
354 | return result; | |
355 | } | |
7753f181 DD |
356 | |
357 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
358 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
359 | ||
360 | int nBuckets = 0; | |
361 | for (int i = 0; i < mParams->num_buckets; i++) { | |
3ba88d77 | 362 | if (mParams->buckets[i].report_events & REPORT_EVENTS_FULL_RESULTS) { |
7753f181 DD |
363 | nBuckets++; |
364 | } | |
365 | } | |
366 | ||
367 | if (nBuckets != 0) { | |
368 | ALOGI("Full scan requested with nBuckets = %d", nBuckets); | |
369 | registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); | |
370 | } | |
371 | result = requestResponse(request); | |
372 | if (result != WIFI_SUCCESS) { | |
373 | ALOGE("failed to start scan; result = %d", result); | |
374 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
375 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
376 | return result; | |
377 | } | |
378 | ||
0fe9bfaf | 379 | |
7753f181 DD |
380 | return result; |
381 | } | |
382 | ||
383 | virtual int cancel() { | |
c0b490a2 | 384 | ALOGD("Stopping Gscan"); |
7753f181 DD |
385 | |
386 | WifiRequest request(familyId(), ifaceId()); | |
387 | int result = createStopRequest(request); | |
388 | if (result != WIFI_SUCCESS) { | |
389 | ALOGE("failed to create stop request; result = %d", result); | |
390 | } else { | |
391 | result = requestResponse(request); | |
392 | if (result != WIFI_SUCCESS) { | |
393 | ALOGE("failed to stop scan; result = %d", result); | |
394 | } | |
395 | } | |
396 | ||
397 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); | |
398 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); | |
399 | unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); | |
400 | ||
401 | return WIFI_SUCCESS; | |
402 | } | |
403 | ||
404 | virtual int handleResponse(WifiEvent& reply) { | |
405 | /* Nothing to do on response! */ | |
406 | return NL_SKIP; | |
407 | } | |
408 | ||
409 | virtual int handleEvent(WifiEvent& event) { | |
d20effd2 | 410 | //event.log(); |
7753f181 DD |
411 | |
412 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
413 | unsigned int len = event.get_vendor_data_len(); | |
414 | int event_id = event.get_vendor_subcmd(); | |
7753f181 DD |
415 | |
416 | if(event_id == GSCAN_EVENT_COMPLETE_SCAN) { | |
417 | if (vendor_data == NULL || len != 4) { | |
9a2ccc1c | 418 | ALOGE("Scan complete type not mentioned!"); |
7753f181 DD |
419 | return NL_SKIP; |
420 | } | |
421 | wifi_scan_event evt_type; | |
422 | ||
423 | evt_type = (wifi_scan_event) event.get_u32(NL80211_ATTR_VENDOR_DATA); | |
7753f181 | 424 | if(*mHandler.on_scan_event) |
f3a9f24c | 425 | (*mHandler.on_scan_event)(id(), evt_type); |
7753f181 | 426 | } else if(event_id == GSCAN_EVENT_FULL_SCAN_RESULTS) { |
0fe9bfaf | 427 | uint32_t bucket_scanned = 0; |
acc9b163 JPS |
428 | wifi_scan_result *scan_result = NULL; |
429 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
430 | if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_BUCKET_BIT) { | |
431 | bucket_scanned = it.get_u32(); | |
432 | } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS) { | |
433 | if (it.get_len() >= (int)sizeof(*scan_result)) | |
434 | scan_result = (wifi_scan_result *)it.get_data(); | |
435 | } | |
436 | } | |
437 | if (scan_result) { | |
438 | if(*mHandler.on_full_scan_result) | |
439 | (*mHandler.on_full_scan_result)(id(), scan_result, bucket_scanned); | |
d20effd2 | 440 | /* |
acc9b163 JPS |
441 | ALOGD("%-32s\t", scan_result->ssid); |
442 | ALOGD("%02x:%02x:%02x:%02x:%02x:%02x ", scan_result->bssid[0], scan_result->bssid[1], | |
443 | scan_result->bssid[2], scan_result->bssid[3], scan_result->bssid[4], scan_result->bssid[5]); | |
444 | ALOGD("%d\t", scan_result->rssi); | |
445 | ALOGD("%d\t", scan_result->channel); | |
446 | ALOGD("%lld\t", scan_result->ts); | |
447 | ALOGD("%lld\t", scan_result->rtt); | |
448 | ALOGD("%lld\n", scan_result->rtt_sd); | |
d20effd2 | 449 | */ |
7753f181 | 450 | } |
7753f181 DD |
451 | } |
452 | return NL_SKIP; | |
453 | } | |
454 | }; | |
455 | ||
456 | unsigned ScanCommand::mGlobalFullScanBuckets = 0; | |
457 | ||
458 | wifi_error wifi_start_gscan( | |
459 | wifi_request_id id, | |
460 | wifi_interface_handle iface, | |
461 | wifi_scan_cmd_params params, | |
462 | wifi_scan_result_handler handler) | |
463 | { | |
464 | wifi_handle handle = getWifiHandle(iface); | |
465 | ||
7753f181 DD |
466 | ScanCommand *cmd = new ScanCommand(iface, id, ¶ms, handler); |
467 | wifi_register_cmd(handle, id, cmd); | |
468 | return (wifi_error)cmd->start(); | |
469 | } | |
470 | ||
471 | wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) | |
472 | { | |
7753f181 DD |
473 | wifi_handle handle = getWifiHandle(iface); |
474 | ||
475 | if(id == -1) { | |
476 | wifi_scan_result_handler handler; | |
477 | wifi_scan_cmd_params dummy_params; | |
7753f181 DD |
478 | memset(&handler, 0, sizeof(handler)); |
479 | ||
480 | ScanCommand *cmd = new ScanCommand(iface, id, &dummy_params, handler); | |
481 | cmd->cancel(); | |
482 | cmd->releaseRef(); | |
483 | return WIFI_SUCCESS; | |
484 | } | |
485 | ||
486 | ||
487 | WifiCommand *cmd = wifi_unregister_cmd(handle, id); | |
488 | if (cmd) { | |
489 | cmd->cancel(); | |
490 | cmd->releaseRef(); | |
491 | return WIFI_SUCCESS; | |
492 | } | |
493 | ||
494 | return WIFI_ERROR_INVALID_ARGS; | |
495 | } | |
496 | ||
497 | class GetScanResultsCommand : public WifiCommand { | |
498 | wifi_cached_scan_results *mScans; | |
499 | int mMax; | |
500 | int *mNum; | |
501 | int mRetrieved; | |
502 | byte mFlush; | |
503 | int mCompleted; | |
504 | static const int MAX_RESULTS = 320; | |
505 | wifi_scan_result mScanResults[MAX_RESULTS]; | |
506 | int mNextScanResult; | |
507 | public: | |
508 | GetScanResultsCommand(wifi_interface_handle iface, byte flush, | |
509 | wifi_cached_scan_results *results, int max, int *num) | |
510 | : WifiCommand(iface, -1), mScans(results), mMax(max), mNum(num), | |
511 | mRetrieved(0), mFlush(flush), mCompleted(0) | |
0fe9bfaf PG |
512 | { |
513 | memset(mScanResults,0,sizeof(mScanResults)); | |
514 | mNextScanResult = 0; | |
515 | } | |
7753f181 DD |
516 | |
517 | int createRequest(WifiRequest& request, int num, byte flush) { | |
518 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_GET_SCAN_RESULTS); | |
519 | if (result < 0) { | |
520 | return result; | |
521 | } | |
522 | ||
523 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
524 | result = request.put_u32(GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num); | |
525 | if (result < 0) { | |
526 | return result; | |
527 | } | |
528 | ||
529 | request.attr_end(data); | |
530 | return WIFI_SUCCESS; | |
531 | } | |
532 | ||
533 | int execute() { | |
534 | WifiRequest request(familyId(), ifaceId()); | |
7753f181 DD |
535 | |
536 | for (int i = 0; i < 10 && mRetrieved < mMax; i++) { | |
537 | int result = createRequest(request, (mMax - mRetrieved), mFlush); | |
538 | if (result < 0) { | |
539 | ALOGE("failed to create request"); | |
540 | return result; | |
541 | } | |
542 | ||
543 | int prev_retrieved = mRetrieved; | |
544 | ||
545 | result = requestResponse(request); | |
546 | ||
547 | if (result != WIFI_SUCCESS) { | |
548 | ALOGE("failed to retrieve scan results; result = %d", result); | |
549 | return result; | |
550 | } | |
551 | ||
552 | if (mRetrieved == prev_retrieved || mCompleted) { | |
553 | /* no more items left to retrieve */ | |
554 | break; | |
555 | } | |
556 | ||
557 | request.destroy(); | |
558 | } | |
559 | ||
560 | ALOGE("GetScanResults read %d results", mRetrieved); | |
561 | *mNum = mRetrieved; | |
562 | return WIFI_SUCCESS; | |
563 | } | |
564 | ||
565 | virtual int handleResponse(WifiEvent& reply) { | |
7753f181 DD |
566 | |
567 | if (reply.get_cmd() != NL80211_CMD_VENDOR) { | |
d20effd2 | 568 | ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); |
7753f181 DD |
569 | return NL_SKIP; |
570 | } | |
571 | ||
7753f181 DD |
572 | nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); |
573 | int len = reply.get_vendor_data_len(); | |
574 | ||
575 | if (vendor_data == NULL || len == 0) { | |
576 | ALOGE("no vendor data in GetScanResults response; ignoring it"); | |
577 | return NL_SKIP; | |
578 | } | |
579 | ||
580 | for (nl_iterator it(vendor_data); it.has_next(); it.next()) { | |
581 | if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE) { | |
582 | mCompleted = it.get_u8(); | |
d20effd2 | 583 | //ALOGD("retrieved mCompleted flag : %d", mCompleted); |
7753f181 DD |
584 | } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS || it.get_type() == 0) { |
585 | int scan_id = 0, flags = 0, num = 0; | |
586 | for (nl_iterator it2(it.get()); it2.has_next(); it2.next()) { | |
587 | if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_ID) { | |
588 | scan_id = it2.get_u32(); | |
d20effd2 | 589 | //ALOGD("retrieved scan_id : 0x%0x", scan_id); |
7753f181 DD |
590 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_FLAGS) { |
591 | flags = it2.get_u8(); | |
d20effd2 | 592 | //ALOGD("retrieved scan_flags : 0x%0x", flags); |
7753f181 DD |
593 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_NUM_OF_RESULTS) { |
594 | num = it2.get_u32(); | |
d20effd2 | 595 | //ALOGD("retrieved num_results: %d", num); |
7753f181 DD |
596 | } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS) { |
597 | if (mRetrieved >= mMax) { | |
598 | ALOGW("Stored %d scans, ignoring excess results", mRetrieved); | |
599 | break; | |
600 | } | |
601 | num = it2.get_len() / sizeof(wifi_scan_result); | |
602 | num = min(MAX_RESULTS - mNextScanResult, num); | |
603 | num = min((int)MAX_AP_CACHE_PER_SCAN, num); | |
604 | memcpy(mScanResults + mNextScanResult, it2.get_data(), | |
605 | sizeof(wifi_scan_result) * num); | |
d20effd2 | 606 | /* |
8094868d | 607 | wifi_scan_result *results = (wifi_scan_result *)it2.get_data(); |
7753f181 DD |
608 | for (int i = 0; i < num; i++) { |
609 | wifi_scan_result *result = results + i; | |
610 | ALOGD("%02d %-32s %02x:%02x:%02x:%02x:%02x:%02x %04d", i, | |
611 | result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], | |
612 | result->bssid[3], result->bssid[4], result->bssid[5], | |
613 | result->rssi); | |
d20effd2 | 614 | }*/ |
7753f181 DD |
615 | mScans[mRetrieved].scan_id = scan_id; |
616 | mScans[mRetrieved].flags = flags; | |
617 | mScans[mRetrieved].num_results = num; | |
d20effd2 | 618 | //ALOGD("Setting result of scan_id : 0x%0x", mScans[mRetrieved].scan_id); |
7753f181 DD |
619 | memcpy(mScans[mRetrieved].results, |
620 | &(mScanResults[mNextScanResult]), num * sizeof(wifi_scan_result)); | |
621 | mNextScanResult += num; | |
622 | mRetrieved++; | |
623 | } else { | |
624 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
625 | it.get_type(), it.get_len()); | |
626 | } | |
627 | } | |
628 | } else { | |
629 | ALOGW("Ignoring invalid attribute type = %d, size = %d", | |
630 | it.get_type(), it.get_len()); | |
631 | } | |
632 | } | |
633 | ||
634 | return NL_OK; | |
635 | } | |
636 | }; | |
637 | ||
638 | wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, | |
639 | int max, wifi_cached_scan_results *results, int *num) { | |
7753f181 DD |
640 | GetScanResultsCommand *cmd = new GetScanResultsCommand(iface, flush, results, max, num); |
641 | return (wifi_error)cmd->execute(); | |
642 | } | |
643 | ||
644 | ///////////////////////////////////////////////////////////////////////////// | |
6ff2d683 JPS |
645 | class ePNOCommand : public WifiCommand |
646 | { | |
647 | private: | |
c15187c1 | 648 | wifi_epno_params *epno_params; |
6ff2d683 JPS |
649 | wifi_epno_handler mHandler; |
650 | wifi_scan_result mResults; | |
651 | public: | |
652 | ePNOCommand(wifi_interface_handle handle, int id, | |
c15187c1 | 653 | wifi_epno_params *params, wifi_epno_handler handler) |
6ff2d683 JPS |
654 | : WifiCommand(handle, id), mHandler(handler) |
655 | { | |
c15187c1 | 656 | epno_params = params; |
0fe9bfaf | 657 | memset(&mResults,0,sizeof(wifi_scan_result)); |
6ff2d683 JPS |
658 | } |
659 | ||
660 | int createSetupRequest(WifiRequest& request) { | |
661 | int result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_EPNO_LIST); | |
662 | if (result < 0) { | |
663 | return result; | |
664 | } | |
665 | ||
666 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
c15187c1 JPS |
667 | if (epno_params == NULL) { |
668 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_NUM, 0); | |
669 | if (result < 0) { | |
670 | return result; | |
671 | } | |
672 | request.attr_end(data); | |
673 | return result; | |
674 | } | |
1fc70afb MG |
675 | result = request.put_u16(EPNO_ATTRIBUTE_MINIMUM_5G_RSSI, epno_params->min5GHz_rssi); |
676 | if (result < 0) { | |
677 | return result; | |
678 | } | |
679 | result = request.put_u16(EPNO_ATTRIBUTE_MINIMUM_2G_RSSI, epno_params->min24GHz_rssi); | |
680 | if (result < 0) { | |
681 | return result; | |
682 | } | |
683 | result = request.put_u16(EPNO_ATTRIBUTE_INITIAL_SCORE_MAX, epno_params->initial_score_max); | |
684 | if (result < 0) { | |
685 | return result; | |
686 | } | |
687 | result = request.put_u8(EPNO_ATTRIBUTE_CUR_CONN_BONUS, epno_params->current_connection_bonus); | |
688 | if (result < 0) { | |
689 | return result; | |
690 | } | |
691 | result = request.put_u8(EPNO_ATTRIBUTE_SAME_NETWORK_BONUS, epno_params->same_network_bonus); | |
692 | if (result < 0) { | |
693 | return result; | |
694 | } | |
695 | result = request.put_u8(EPNO_ATTRIBUTE_SECURE_BONUS, epno_params->secure_bonus); | |
696 | if (result < 0) { | |
697 | return result; | |
698 | } | |
699 | result = request.put_u8(EPNO_ATTRIBUTE_5G_BONUS, epno_params->band5GHz_bonus); | |
700 | if (result < 0) { | |
701 | return result; | |
702 | } | |
c15187c1 | 703 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_NUM, epno_params->num_networks); |
6ff2d683 JPS |
704 | if (result < 0) { |
705 | return result; | |
706 | } | |
707 | ||
1fc70afb | 708 | ALOGI("ePNO [min5GHz_rssi:%d min24GHz_rssi:%d initial_score_max:%d current_connection_bonus:%d same_network_bonus:%d secure_bonus:%d band5GHz_bonus:%d num_networks:%d]", |
c0b490a2 JPS |
709 | epno_params->min5GHz_rssi, |
710 | epno_params->min24GHz_rssi, | |
711 | epno_params->initial_score_max, | |
712 | epno_params->current_connection_bonus, | |
713 | epno_params->same_network_bonus, | |
714 | epno_params->secure_bonus, | |
715 | epno_params->band5GHz_bonus, | |
716 | epno_params->num_networks); | |
1fc70afb | 717 | |
6ff2d683 | 718 | struct nlattr * attr = request.attr_start(EPNO_ATTRIBUTE_SSID_LIST); |
c15187c1 | 719 | for (int i = 0; i < epno_params->num_networks; i++) { |
6ff2d683 JPS |
720 | nlattr *attr2 = request.attr_start(i); |
721 | if (attr2 == NULL) { | |
722 | return WIFI_ERROR_OUT_OF_MEMORY; | |
723 | } | |
1fc70afb | 724 | result = request.put_u16(EPNO_ATTRIBUTE_FLAGS, epno_params->networks[i].flags); |
6ff2d683 JPS |
725 | if (result < 0) { |
726 | return result; | |
727 | } | |
1fc70afb | 728 | result = request.put_u8(EPNO_ATTRIBUTE_AUTH, epno_params->networks[i].auth_bit_field); |
6ff2d683 JPS |
729 | if (result < 0) { |
730 | return result; | |
731 | } | |
1fc70afb | 732 | result = request.put_u8(EPNO_ATTRIBUTE_SSID_LEN, strlen(epno_params->networks[i].ssid)); |
6ff2d683 JPS |
733 | if (result < 0) { |
734 | return result; | |
735 | } | |
1fc70afb | 736 | result = request.put(EPNO_ATTRIBUTE_SSID, epno_params->networks[i].ssid, strlen(epno_params->networks[i].ssid)); |
6ff2d683 JPS |
737 | if (result < 0) { |
738 | return result; | |
739 | } | |
740 | request.attr_end(attr2); | |
741 | } | |
742 | ||
743 | request.attr_end(attr); | |
744 | request.attr_end(data); | |
745 | return result; | |
746 | } | |
747 | ||
748 | int start() { | |
c15187c1 | 749 | ALOGI("ePNO num_network=%d", epno_params ? epno_params->num_networks : 0); |
6ff2d683 JPS |
750 | WifiRequest request(familyId(), ifaceId()); |
751 | int result = createSetupRequest(request); | |
752 | if (result < 0) { | |
753 | return result; | |
754 | } | |
755 | ||
756 | result = requestResponse(request); | |
757 | if (result < 0) { | |
758 | ALOGI("Failed: ePNO setup request, result = %d", result); | |
759 | unregisterVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); | |
760 | return result; | |
761 | } | |
762 | ||
c15187c1 | 763 | if (epno_params) { |
c15187c1 | 764 | registerVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); |
c15187c1 | 765 | } |
6ff2d683 JPS |
766 | return result; |
767 | } | |
768 | ||
769 | virtual int cancel() { | |
770 | /* unregister event handler */ | |
771 | unregisterVendorHandler(GOOGLE_OUI, WIFI_EPNO_EVENT); | |
772 | return 0; | |
773 | } | |
774 | ||
775 | virtual int handleResponse(WifiEvent& reply) { | |
776 | /* Nothing to do on response! */ | |
777 | return NL_SKIP; | |
778 | } | |
779 | ||
780 | virtual int handleEvent(WifiEvent& event) { | |
6ff2d683 JPS |
781 | // event.log(); |
782 | ||
783 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); | |
784 | int len = event.get_vendor_data_len(); | |
785 | ||
786 | if (vendor_data == NULL || len == 0) { | |
787 | ALOGI("No scan results found"); | |
788 | return NL_SKIP; | |
789 | } | |
790 | ||
0fe9bfaf | 791 | |
6ff2d683 JPS |
792 | mResults = *(wifi_scan_result *) event.get_vendor_data(); |
793 | if (*mHandler.on_network_found) | |
794 | (*mHandler.on_network_found)(id(), 1, &mResults); | |
795 | return NL_SKIP; | |
796 | } | |
797 | }; | |
798 | ||
799 | wifi_error wifi_set_epno_list(wifi_request_id id, | |
800 | wifi_interface_handle iface, | |
c15187c1 | 801 | const wifi_epno_params *epno_params, |
6ff2d683 JPS |
802 | wifi_epno_handler handler) |
803 | { | |
c15187c1 | 804 | wifi_handle handle = getWifiHandle(iface); |
c15187c1 JPS |
805 | ePNOCommand *cmd = new ePNOCommand(iface, id, (wifi_epno_params *)epno_params, handler); |
806 | wifi_register_cmd(handle, id, cmd); | |
807 | wifi_error result = (wifi_error)cmd->start(); | |
808 | if (result != WIFI_SUCCESS) { | |
809 | wifi_unregister_cmd(handle, id); | |
810 | } | |
811 | return result; | |
812 | } | |
813 | ||
814 | wifi_error wifi_reset_epno_list(wifi_request_id id, wifi_interface_handle iface) | |
815 | { | |
816 | wifi_handle handle = getWifiHandle(iface); | |
817 | wifi_epno_handler handler; | |
818 | ||
819 | handler.on_network_found = NULL; | |
820 | ePNOCommand *cmd = new ePNOCommand(iface, id, NULL, handler); | |
821 | wifi_register_cmd(handle, id, cmd); | |
822 | wifi_error result = (wifi_error)cmd->start(); | |
823 | if (result != WIFI_SUCCESS) { | |
824 | wifi_unregister_cmd(handle, id); | |
825 | } | |
826 | return result; | |
6ff2d683 JPS |
827 | } |
828 | ||
829 | class HsListCommand : public WifiCommand | |
830 | { | |
831 | int num_hs; | |
832 | wifi_passpoint_network *mNetworks; | |
833 | wifi_passpoint_event_handler mHandler; | |
834 | public: | |
835 | HsListCommand(wifi_request_id id, wifi_interface_handle iface, | |
836 | int num, wifi_passpoint_network *hs_list, wifi_passpoint_event_handler handler) | |
837 | : WifiCommand(iface, id), num_hs(num), mNetworks(hs_list), | |
838 | mHandler(handler) | |
839 | { | |
840 | } | |
841 | ||
842 | HsListCommand(wifi_request_id id, wifi_interface_handle iface, | |
843 | int num) | |
844 | : WifiCommand(iface, id), num_hs(num), mNetworks(NULL) | |
845 | { | |
0fe9bfaf | 846 | mHandler.on_passpoint_network_found = NULL; |
6ff2d683 JPS |
847 | } |
848 | ||
849 | int createRequest(WifiRequest& request, int val) { | |
850 | int result; | |
851 | ||
852 | if (val) { | |
853 | result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_SET_HS_LIST); | |
854 | result = request.put_u32(EPNO_ATTRIBUTE_HS_NUM, num_hs); | |
855 | if (result < 0) { | |
856 | return result; | |
857 | } | |
858 | nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); | |
859 | ||
860 | struct nlattr * attr = request.attr_start(EPNO_ATTRIBUTE_HS_PARAM_LIST); | |
861 | for (int i = 0; i < num_hs; i++) { | |
862 | nlattr *attr2 = request.attr_start(i); | |
863 | if (attr2 == NULL) { | |
864 | return WIFI_ERROR_OUT_OF_MEMORY; | |
865 | } | |
866 | result = request.put_u32(EPNO_ATTRIBUTE_HS_ID, mNetworks[i].id); | |
867 | if (result < 0) { | |
868 | return result; | |
869 | } | |
870 | result = request.put(EPNO_ATTRIBUTE_HS_REALM, mNetworks[i].realm, 256); | |
871 | if (result < 0) { | |
872 | return result; | |
873 | } | |
874 | result = request.put(EPNO_ATTRIBUTE_HS_CONSORTIUM_IDS, mNetworks[i].roamingConsortiumIds, 128); | |
875 | if (result < 0) { | |
876 | return result; | |
877 | } | |
878 | result = request.put(EPNO_ATTRIBUTE_HS_PLMN, mNetworks[i].plmn, 3); | |
879 | if (result < 0) { | |
880 | return result; | |
881 | } | |
882 | request.attr_end(attr2); | |
883 | } | |
884 | request.attr_end(attr); | |
885 | request.attr_end(data); | |
886 | }else { | |
887 | result = request.create(GOOGLE_OUI, SLSI_NL80211_VENDOR_SUBCMD_RESET_HS_LIST); | |
888 | if (result < 0) { | |
889 | return result; | |
890 | } | |
891 | } | |
892 | ||
893 | return WIFI_SUCCESS; | |
894 | } | |
895 | ||
896 | int start() { | |
897 | ||
898 | WifiRequest request(familyId(), ifaceId()); | |
899 | int result = createRequest(request, num_hs); | |
900 | if (result != WIFI_SUCCESS) { | |
901 | ALOGE("failed to create request; result = %d", result); | |
902 | return result; | |
903 | } | |
904 | ||
905 | registerVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
906 | ||
907 | result = requestResponse(request); | |
908 | if (result != WIFI_SUCCESS) { | |
909 | ALOGE("failed to set ANQPO networks; result = %d", result); | |
910 | unregisterVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
911 | return result; | |
912 | } | |
913 | ||
914 | return result; | |
915 | } | |
916 | ||
917 | virtual int cancel() { | |
918 | ||
919 | WifiRequest request(familyId(), ifaceId()); | |
920 | int result = createRequest(request, 0); | |
921 | if (result != WIFI_SUCCESS) { | |
922 | ALOGE("failed to create request; result = %d", result); | |
923 | } else { | |
924 | result = requestResponse(request); | |
925 | if (result != WIFI_SUCCESS) { | |
926 | ALOGE("failed to reset ANQPO networks;result = %d", result); | |
927 | } | |
928 | } | |
929 | ||
930 | unregisterVendorHandler(GOOGLE_OUI, WIFI_HOTSPOT_MATCH); | |
931 | return WIFI_SUCCESS; | |
932 | } | |
933 | ||
934 | virtual int handleResponse(WifiEvent& reply) { | |
6ff2d683 JPS |
935 | /* Nothing to do on response! */ |
936 | return NL_SKIP; | |
937 | } | |
938 | ||
939 | virtual int handleEvent(WifiEvent& event) { | |
6ff2d683 JPS |
940 | nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); |
941 | unsigned int len = event.get_vendor_data_len(); | |
942 | if (vendor_data == NULL || len < sizeof(wifi_scan_result)) { | |
943 | ALOGE("ERROR: No scan results found"); | |
944 | return NL_SKIP; | |
945 | } | |
946 | ||
947 | wifi_scan_result *result = (wifi_scan_result *)event.get_vendor_data(); | |
948 | byte *anqp = (byte *)result + offsetof(wifi_scan_result, ie_data) + result->ie_length; | |
949 | int networkId = *(int *)anqp; | |
950 | anqp += sizeof(int); | |
951 | int anqp_len = *(u16 *)anqp; | |
952 | anqp += sizeof(u16); | |
953 | ||
6ff2d683 JPS |
954 | if(*mHandler.on_passpoint_network_found) |
955 | (*mHandler.on_passpoint_network_found)(id(), networkId, result, anqp_len, anqp); | |
956 | ||
957 | return NL_SKIP; | |
958 | } | |
959 | }; | |
960 | ||
961 | wifi_error wifi_set_passpoint_list(wifi_request_id id, wifi_interface_handle iface, int num, | |
962 | wifi_passpoint_network *networks, wifi_passpoint_event_handler handler) | |
963 | { | |
964 | wifi_handle handle = getWifiHandle(iface); | |
965 | HsListCommand *cmd = new HsListCommand(id, iface, num, networks, handler); | |
966 | ||
967 | wifi_register_cmd(handle, id, cmd); | |
968 | wifi_error result = (wifi_error)cmd->start(); | |
969 | if (result != WIFI_SUCCESS) { | |
970 | wifi_unregister_cmd(handle, id); | |
971 | } | |
972 | return result; | |
973 | } | |
974 | ||
975 | wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface) | |
976 | { | |
977 | wifi_handle handle = getWifiHandle(iface); | |
978 | wifi_error result; | |
979 | HsListCommand *cmd = (HsListCommand *)(wifi_get_cmd(handle, id)); | |
980 | ||
981 | if (cmd == NULL) { | |
982 | cmd = new HsListCommand(id, iface, 0); | |
983 | wifi_register_cmd(handle, id, cmd); | |
984 | } | |
985 | result = (wifi_error)cmd->cancel(); | |
986 | wifi_unregister_cmd(handle, id); | |
987 | return result; | |
988 | } |