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