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