1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "wificond/scanning/scanner_impl.h"
18
19 #include <set>
20 #include <string>
21 #include <vector>
22
23 #include <android-base/logging.h>
24
25 #include "wificond/client_interface_impl.h"
26 #include "wificond/scanning/offload/offload_scan_manager.h"
27 #include "wificond/scanning/offload/offload_service_utils.h"
28 #include "wificond/scanning/scan_utils.h"
29
30 using android::binder::Status;
31 using android::net::wifi::IPnoScanEvent;
32 using android::net::wifi::IScanEvent;
33 using android::net::wifi::IWifiScannerImpl;
34 using android::sp;
35 using com::android::server::wifi::wificond::NativeScanResult;
36 using com::android::server::wifi::wificond::PnoSettings;
37 using com::android::server::wifi::wificond::SingleScanSettings;
38
39 using std::string;
40 using std::vector;
41 using std::weak_ptr;
42 using std::shared_ptr;
43
44 using namespace std::placeholders;
45
46 namespace {
47 using android::wificond::WiphyFeatures;
IsScanTypeSupported(int scan_type,const WiphyFeatures & wiphy_features)48 bool IsScanTypeSupported(int scan_type, const WiphyFeatures& wiphy_features) {
49 switch(scan_type) {
50 case IWifiScannerImpl::SCAN_TYPE_LOW_SPAN:
51 return wiphy_features.supports_low_span_oneshot_scan;
52 case IWifiScannerImpl::SCAN_TYPE_LOW_POWER:
53 return wiphy_features.supports_low_power_oneshot_scan;
54 case IWifiScannerImpl::SCAN_TYPE_HIGH_ACCURACY:
55 return wiphy_features.supports_high_accuracy_oneshot_scan;
56 default:
57 CHECK(0) << "Invalid scan type received: " << scan_type;
58 }
59 return {};
60 }
61
62 constexpr const int kPercentNetworksWithFreq = 30;
63 constexpr const int kPnoScanDefaultFreqs[] = {2412, 2417, 2422, 2427, 2432, 2437, 2447, 2452,
64 2457, 2462, 5180, 5200, 5220, 5240, 5745, 5765, 5785, 5805};
65 } // namespace
66
67 namespace android {
68 namespace wificond {
69
ScannerImpl(uint32_t interface_index,const ScanCapabilities & scan_capabilities,const WiphyFeatures & wiphy_features,ClientInterfaceImpl * client_interface,ScanUtils * scan_utils,weak_ptr<OffloadServiceUtils> offload_service_utils)70 ScannerImpl::ScannerImpl(uint32_t interface_index,
71 const ScanCapabilities& scan_capabilities,
72 const WiphyFeatures& wiphy_features,
73 ClientInterfaceImpl* client_interface,
74 ScanUtils* scan_utils,
75 weak_ptr<OffloadServiceUtils> offload_service_utils)
76 : valid_(true),
77 scan_started_(false),
78 pno_scan_started_(false),
79 offload_scan_supported_(false),
80 pno_scan_running_over_offload_(false),
81 pno_scan_results_from_offload_(false),
82 interface_index_(interface_index),
83 scan_capabilities_(scan_capabilities),
84 wiphy_features_(wiphy_features),
85 client_interface_(client_interface),
86 scan_utils_(scan_utils),
87 scan_event_handler_(nullptr) {
88 // Subscribe one-shot scan result notification from kernel.
89 LOG(INFO) << "subscribe scan result for interface with index: "
90 << (int)interface_index_;
91 scan_utils_->SubscribeScanResultNotification(
92 interface_index_,
93 std::bind(&ScannerImpl::OnScanResultsReady, this, _1, _2, _3, _4));
94 // Subscribe scheduled scan result notification from kernel.
95 scan_utils_->SubscribeSchedScanResultNotification(
96 interface_index_,
97 std::bind(&ScannerImpl::OnSchedScanResultsReady,
98 this,
99 _1, _2));
100 std::shared_ptr<OffloadScanCallbackInterfaceImpl>
101 offload_scan_callback_interface =
102 offload_service_utils.lock()->GetOffloadScanCallbackInterface(this);
103 offload_scan_manager_ = offload_service_utils.lock()->GetOffloadScanManager(
104 offload_service_utils, offload_scan_callback_interface);
105 offload_scan_supported_ = offload_service_utils.lock()->IsOffloadScanSupported();
106 }
107
~ScannerImpl()108 ScannerImpl::~ScannerImpl() {}
109
Invalidate()110 void ScannerImpl::Invalidate() {
111 LOG(INFO) << "Unsubscribe scan result for interface with index: "
112 << (int)interface_index_;
113 scan_utils_->UnsubscribeScanResultNotification(interface_index_);
114 scan_utils_->UnsubscribeSchedScanResultNotification(interface_index_);
115 valid_ = false;
116 }
117
CheckIsValid()118 bool ScannerImpl::CheckIsValid() {
119 if (!valid_) {
120 LOG(DEBUG) << "Calling on a invalid scanner object."
121 << "Underlying client interface object was destroyed.";
122 }
123 return valid_;
124 }
125
getScanResults(vector<NativeScanResult> * out_scan_results)126 Status ScannerImpl::getScanResults(vector<NativeScanResult>* out_scan_results) {
127 if (!CheckIsValid()) {
128 return Status::ok();
129 }
130 if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {
131 LOG(ERROR) << "Failed to get scan results via NL80211";
132 }
133 return Status::ok();
134 }
135
getPnoScanResults(vector<NativeScanResult> * out_scan_results)136 Status ScannerImpl::getPnoScanResults(
137 vector<NativeScanResult>* out_scan_results) {
138 if (!CheckIsValid()) {
139 return Status::ok();
140 }
141 if (pno_scan_results_from_offload_) {
142 if (!offload_scan_manager_->getScanResults(out_scan_results)) {
143 LOG(ERROR) << "Failed to get scan results via Offload HAL";
144 }
145 } else {
146 if (!scan_utils_->GetScanResult(interface_index_, out_scan_results)) {
147 LOG(ERROR) << "Failed to get scan results via NL80211";
148 }
149 }
150 return Status::ok();
151 }
152
scan(const SingleScanSettings & scan_settings,bool * out_success)153 Status ScannerImpl::scan(const SingleScanSettings& scan_settings,
154 bool* out_success) {
155 if (!CheckIsValid()) {
156 *out_success = false;
157 return Status::ok();
158 }
159
160 if (scan_started_) {
161 LOG(WARNING) << "Scan already started";
162 }
163 // Only request MAC address randomization when station is not associated.
164 bool request_random_mac =
165 wiphy_features_.supports_random_mac_oneshot_scan &&
166 !client_interface_->IsAssociated();
167 int scan_type = scan_settings.scan_type_;
168 if (!IsScanTypeSupported(scan_settings.scan_type_, wiphy_features_)) {
169 LOG(DEBUG) << "Ignoring scan type because device does not support it";
170 scan_type = SCAN_TYPE_DEFAULT;
171 }
172
173 // Initialize it with an empty ssid for a wild card scan.
174 vector<vector<uint8_t>> ssids = {{}};
175
176 vector<vector<uint8_t>> skipped_scan_ssids;
177 for (auto& network : scan_settings.hidden_networks_) {
178 if (ssids.size() + 1 > scan_capabilities_.max_num_scan_ssids) {
179 skipped_scan_ssids.emplace_back(network.ssid_);
180 continue;
181 }
182 ssids.push_back(network.ssid_);
183 }
184
185 LogSsidList(skipped_scan_ssids, "Skip scan ssid for single scan");
186
187 vector<uint32_t> freqs;
188 for (auto& channel : scan_settings.channel_settings_) {
189 freqs.push_back(channel.frequency_);
190 }
191
192 int error_code = 0;
193 if (!scan_utils_->Scan(interface_index_, request_random_mac, scan_type,
194 ssids, freqs, &error_code)) {
195 CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
196 *out_success = false;
197 return Status::ok();
198 }
199 scan_started_ = true;
200 *out_success = true;
201 return Status::ok();
202 }
203
startPnoScan(const PnoSettings & pno_settings,bool * out_success)204 Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
205 bool* out_success) {
206 pno_settings_ = pno_settings;
207 pno_scan_results_from_offload_ = false;
208 LOG(VERBOSE) << "startPnoScan";
209 if (offload_scan_supported_ && StartPnoScanOffload(pno_settings)) {
210 // scanning over offload succeeded
211 *out_success = true;
212 } else {
213 *out_success = StartPnoScanDefault(pno_settings);
214 }
215 return Status::ok();
216 }
217
StartPnoScanOffload(const PnoSettings & pno_settings)218 bool ScannerImpl::StartPnoScanOffload(const PnoSettings& pno_settings) {
219 OffloadScanManager::ReasonCode reason_code;
220 vector<vector<uint8_t>> scan_ssids;
221 vector<vector<uint8_t>> match_ssids;
222 vector<uint8_t> match_security;
223 // Empty frequency list: scan all frequencies.
224 vector<uint32_t> freqs;
225
226 ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs,
227 &match_security);
228 pno_scan_running_over_offload_ = offload_scan_manager_->startScan(
229 pno_settings.interval_ms_,
230 // TODO: honor both rssi thresholds.
231 pno_settings.min_5g_rssi_, scan_ssids, match_ssids, match_security, freqs,
232 &reason_code);
233 if (pno_scan_running_over_offload_) {
234 LOG(VERBOSE) << "Pno scans requested over Offload HAL";
235 if (pno_scan_event_handler_ != nullptr) {
236 pno_scan_event_handler_->OnPnoScanOverOffloadStarted();
237 }
238 }
239 return pno_scan_running_over_offload_;
240 }
241
ParsePnoSettings(const PnoSettings & pno_settings,vector<vector<uint8_t>> * scan_ssids,vector<vector<uint8_t>> * match_ssids,vector<uint32_t> * freqs,vector<uint8_t> * match_security)242 void ScannerImpl::ParsePnoSettings(const PnoSettings& pno_settings,
243 vector<vector<uint8_t>>* scan_ssids,
244 vector<vector<uint8_t>>* match_ssids,
245 vector<uint32_t>* freqs,
246 vector<uint8_t>* match_security) {
247 // TODO provide actionable security match parameters
248 const uint8_t kNetworkFlagsDefault = 0;
249 vector<vector<uint8_t>> skipped_scan_ssids;
250 vector<vector<uint8_t>> skipped_match_ssids;
251 std::set<int32_t> unique_frequencies;
252 int num_networks_no_freqs = 0;
253 for (auto& network : pno_settings.pno_networks_) {
254 // Add hidden network ssid.
255 if (network.is_hidden_) {
256 // TODO remove pruning for Offload Scans
257 if (scan_ssids->size() + 1 >
258 scan_capabilities_.max_num_sched_scan_ssids) {
259 skipped_scan_ssids.emplace_back(network.ssid_);
260 continue;
261 }
262 scan_ssids->push_back(network.ssid_);
263 }
264
265 if (match_ssids->size() + 1 > scan_capabilities_.max_match_sets) {
266 skipped_match_ssids.emplace_back(network.ssid_);
267 continue;
268 }
269 match_ssids->push_back(network.ssid_);
270 match_security->push_back(kNetworkFlagsDefault);
271
272 // build the set of unique frequencies to scan for.
273 for (const auto& frequency : network.frequencies_) {
274 unique_frequencies.insert(frequency);
275 }
276 if (network.frequencies_.empty()) {
277 num_networks_no_freqs++;
278 }
279 }
280
281 // Also scan the default frequencies if there is frequency data passed down but more than 30% of
282 // networks don't have frequency data.
283 if (unique_frequencies.size() > 0 && num_networks_no_freqs * 100 / match_ssids->size()
284 > kPercentNetworksWithFreq) {
285 unique_frequencies.insert(std::begin(kPnoScanDefaultFreqs), std::end(kPnoScanDefaultFreqs));
286 }
287 for (const auto& frequency : unique_frequencies) {
288 freqs->push_back(frequency);
289 }
290 LogSsidList(skipped_scan_ssids, "Skip scan ssid for pno scan");
291 LogSsidList(skipped_match_ssids, "Skip match ssid for pno scan");
292 }
293
StartPnoScanDefault(const PnoSettings & pno_settings)294 bool ScannerImpl::StartPnoScanDefault(const PnoSettings& pno_settings) {
295 if (!CheckIsValid()) {
296 return false;
297 }
298 if (pno_scan_started_) {
299 LOG(WARNING) << "Pno scan already started";
300 }
301 // An empty ssid for a wild card scan.
302 vector<vector<uint8_t>> scan_ssids = {{}};
303 vector<vector<uint8_t>> match_ssids;
304 vector<uint8_t> unused;
305 // Empty frequency list: scan all frequencies.
306 vector<uint32_t> freqs;
307
308 ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, &unused);
309 // Only request MAC address randomization when station is not associated.
310 bool request_random_mac = wiphy_features_.supports_random_mac_sched_scan &&
311 !client_interface_->IsAssociated();
312 // Always request a low power scan for PNO, if device supports it.
313 bool request_low_power = wiphy_features_.supports_low_power_oneshot_scan;
314
315 bool request_sched_scan_relative_rssi = wiphy_features_.supports_ext_sched_scan_relative_rssi;
316
317 int error_code = 0;
318 struct SchedScanReqFlags req_flags = {};
319 req_flags.request_random_mac = request_random_mac;
320 req_flags.request_low_power = request_low_power;
321 req_flags.request_sched_scan_relative_rssi = request_sched_scan_relative_rssi;
322 if (!scan_utils_->StartScheduledScan(interface_index_,
323 GenerateIntervalSetting(pno_settings),
324 pno_settings.min_2g_rssi_,
325 pno_settings.min_5g_rssi_,
326 req_flags,
327 scan_ssids,
328 match_ssids,
329 freqs,
330 &error_code)) {
331 LOG(ERROR) << "Failed to start pno scan";
332 CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
333 return false;
334 }
335 string freq_string;
336 if (freqs.empty()) {
337 freq_string = "for all supported frequencies";
338 } else {
339 freq_string = "for frequencies: ";
340 for (uint32_t f : freqs) {
341 freq_string += std::to_string(f) + ", ";
342 }
343 }
344 LOG(INFO) << "Pno scan started " << freq_string;
345 pno_scan_started_ = true;
346 return true;
347 }
348
stopPnoScan(bool * out_success)349 Status ScannerImpl::stopPnoScan(bool* out_success) {
350 if (offload_scan_supported_ && StopPnoScanOffload()) {
351 // Pno scans over offload stopped successfully
352 *out_success = true;
353 } else {
354 // Pno scans were not requested over offload
355 *out_success = StopPnoScanDefault();
356 }
357 return Status::ok();
358 }
359
StopPnoScanOffload()360 bool ScannerImpl::StopPnoScanOffload() {
361 OffloadScanManager::ReasonCode reason_code;
362 if (!pno_scan_running_over_offload_) {
363 return false;
364 }
365 if (!offload_scan_manager_->stopScan(&reason_code)) {
366 LOG(WARNING) << "Unable to unsubscribe to Offload scan results";
367 }
368 pno_scan_running_over_offload_ = false;
369 LOG(VERBOSE) << "Pno scans over Offload stopped";
370 return true;
371 }
372
StopPnoScanDefault()373 bool ScannerImpl::StopPnoScanDefault() {
374 if (!CheckIsValid()) {
375 return false;
376 }
377
378 if (!pno_scan_started_) {
379 LOG(WARNING) << "No pno scan started";
380 }
381 if (!scan_utils_->StopScheduledScan(interface_index_)) {
382 return false;
383 }
384 LOG(INFO) << "Pno scan stopped";
385 pno_scan_started_ = false;
386 return true;
387 }
388
abortScan()389 Status ScannerImpl::abortScan() {
390 if (!CheckIsValid()) {
391 return Status::ok();
392 }
393
394 if (!scan_started_) {
395 LOG(WARNING) << "Scan is not started. Ignore abort request";
396 return Status::ok();
397 }
398 if (!scan_utils_->AbortScan(interface_index_)) {
399 LOG(WARNING) << "Abort scan failed";
400 }
401 return Status::ok();
402 }
403
subscribeScanEvents(const sp<IScanEvent> & handler)404 Status ScannerImpl::subscribeScanEvents(const sp<IScanEvent>& handler) {
405 if (!CheckIsValid()) {
406 return Status::ok();
407 }
408
409 if (scan_event_handler_ != nullptr) {
410 LOG(ERROR) << "Found existing scan events subscriber."
411 << " This subscription request will unsubscribe it";
412 }
413 scan_event_handler_ = handler;
414 return Status::ok();
415 }
416
unsubscribeScanEvents()417 Status ScannerImpl::unsubscribeScanEvents() {
418 scan_event_handler_ = nullptr;
419 return Status::ok();
420 }
421
subscribePnoScanEvents(const sp<IPnoScanEvent> & handler)422 Status ScannerImpl::subscribePnoScanEvents(const sp<IPnoScanEvent>& handler) {
423 if (!CheckIsValid()) {
424 return Status::ok();
425 }
426
427 if (pno_scan_event_handler_ != nullptr) {
428 LOG(ERROR) << "Found existing pno scan events subscriber."
429 << " This subscription request will unsubscribe it";
430 }
431 pno_scan_event_handler_ = handler;
432
433 return Status::ok();
434 }
435
unsubscribePnoScanEvents()436 Status ScannerImpl::unsubscribePnoScanEvents() {
437 pno_scan_event_handler_ = nullptr;
438 return Status::ok();
439 }
440
OnScanResultsReady(uint32_t interface_index,bool aborted,vector<vector<uint8_t>> & ssids,vector<uint32_t> & frequencies)441 void ScannerImpl::OnScanResultsReady(uint32_t interface_index, bool aborted,
442 vector<vector<uint8_t>>& ssids,
443 vector<uint32_t>& frequencies) {
444 if (!scan_started_) {
445 LOG(INFO) << "Received external scan result notification from kernel.";
446 }
447 scan_started_ = false;
448 if (scan_event_handler_ != nullptr) {
449 // TODO: Pass other parameters back once we find framework needs them.
450 if (aborted) {
451 LOG(WARNING) << "Scan aborted";
452 scan_event_handler_->OnScanFailed();
453 } else {
454 scan_event_handler_->OnScanResultReady();
455 }
456 } else {
457 LOG(WARNING) << "No scan event handler found.";
458 }
459 }
460
OnSchedScanResultsReady(uint32_t interface_index,bool scan_stopped)461 void ScannerImpl::OnSchedScanResultsReady(uint32_t interface_index,
462 bool scan_stopped) {
463 if (pno_scan_event_handler_ != nullptr) {
464 if (scan_stopped) {
465 // If |pno_scan_started_| is false.
466 // This stop notification might result from our own request.
467 // See the document for NL80211_CMD_SCHED_SCAN_STOPPED in nl80211.h.
468 if (pno_scan_started_) {
469 LOG(WARNING) << "Unexpected pno scan stopped event";
470 pno_scan_event_handler_->OnPnoScanFailed();
471 }
472 pno_scan_started_ = false;
473 } else {
474 LOG(INFO) << "Pno scan result ready event";
475 pno_scan_results_from_offload_ = false;
476 pno_scan_event_handler_->OnPnoNetworkFound();
477 }
478 }
479 }
480
GenerateIntervalSetting(const::com::android::server::wifi::wificond::PnoSettings & pno_settings) const481 SchedScanIntervalSetting ScannerImpl::GenerateIntervalSetting(
482 const ::com::android::server::wifi::wificond::PnoSettings&
483 pno_settings) const {
484 bool support_num_scan_plans = scan_capabilities_.max_num_scan_plans >= 2;
485 bool support_scan_plan_interval =
486 scan_capabilities_.max_scan_plan_interval * 1000 >=
487 pno_settings.interval_ms_ * PnoSettings::kSlowScanIntervalMultiplier;
488 bool support_scan_plan_iterations =
489 scan_capabilities_.max_scan_plan_iterations >=
490 PnoSettings::kFastScanIterations;
491
492 uint32_t fast_scan_interval =
493 static_cast<uint32_t>(pno_settings.interval_ms_);
494 if (support_num_scan_plans && support_scan_plan_interval &&
495 support_scan_plan_iterations) {
496 return SchedScanIntervalSetting{
497 {{fast_scan_interval, PnoSettings::kFastScanIterations}},
498 fast_scan_interval * PnoSettings::kSlowScanIntervalMultiplier};
499 } else {
500 // Device doesn't support the provided scan plans.
501 // Specify single interval instead.
502 // In this case, the driver/firmware is expected to implement back off
503 // logic internally using |pno_settings.interval_ms_| as "fast scan"
504 // interval.
505 return SchedScanIntervalSetting{{}, fast_scan_interval};
506 }
507 }
508
OnOffloadScanResult()509 void ScannerImpl::OnOffloadScanResult() {
510 if (!pno_scan_running_over_offload_) {
511 LOG(WARNING) << "Scan results from Offload HAL but scan not requested over "
512 "this interface";
513 return;
514 }
515 LOG(INFO) << "Offload Scan results received";
516 pno_scan_results_from_offload_ = true;
517 if (pno_scan_event_handler_ != nullptr) {
518 pno_scan_event_handler_->OnPnoNetworkFound();
519 } else {
520 LOG(WARNING) << "No scan event handler Offload Scan result";
521 }
522 }
523
OnOffloadError(OffloadScanCallbackInterface::AsyncErrorReason error_code)524 void ScannerImpl::OnOffloadError(
525 OffloadScanCallbackInterface::AsyncErrorReason error_code) {
526 if (!pno_scan_running_over_offload_) {
527 // Ignore irrelevant error notifications
528 LOG(WARNING) << "Offload HAL Async Error occured but Offload HAL is not "
529 "subscribed to";
530 return;
531 }
532 LOG(ERROR) << "Offload Service Async Failure error_code=" << error_code;
533 switch (error_code) {
534 case OffloadScanCallbackInterface::AsyncErrorReason::BINDER_DEATH:
535 LOG(ERROR) << "Binder death";
536 if (pno_scan_event_handler_ != nullptr) {
537 pno_scan_event_handler_->OnPnoScanOverOffloadFailed(
538 net::wifi::IPnoScanEvent::PNO_SCAN_OVER_OFFLOAD_BINDER_FAILURE);
539 }
540 break;
541 case OffloadScanCallbackInterface::AsyncErrorReason::REMOTE_FAILURE:
542 LOG(ERROR) << "Remote failure";
543 if (pno_scan_event_handler_ != nullptr) {
544 pno_scan_event_handler_->OnPnoScanOverOffloadFailed(
545 net::wifi::IPnoScanEvent::PNO_SCAN_OVER_OFFLOAD_REMOTE_FAILURE);
546 }
547 break;
548 default:
549 LOG(WARNING) << "Invalid Error code";
550 break;
551 }
552 bool success = false;
553 // Stop scans over Offload HAL and request them over netlink
554 stopPnoScan(&success);
555 if (success) {
556 LOG(INFO) << "Pno scans stopped";
557 }
558 // Restart PNO scans over netlink interface
559 success = StartPnoScanDefault(pno_settings_);
560 if (success) {
561 LOG(INFO) << "Pno scans restarted";
562 } else {
563 LOG(ERROR) << "Unable to fall back to netlink pno scan";
564 pno_scan_event_handler_->OnPnoScanFailed();
565 }
566 }
567
LogSsidList(vector<vector<uint8_t>> & ssid_list,string prefix)568 void ScannerImpl::LogSsidList(vector<vector<uint8_t>>& ssid_list,
569 string prefix) {
570 if (ssid_list.empty()) {
571 return;
572 }
573 string ssid_list_string;
574 for (auto& ssid : ssid_list) {
575 ssid_list_string += string(ssid.begin(), ssid.end());
576 if (&ssid != &ssid_list.back()) {
577 ssid_list_string += ", ";
578 }
579 }
580 LOG(WARNING) << prefix << ": " << ssid_list_string;
581 }
582
583 } // namespace wificond
584 } // namespace android
585