blob: 740795a3156ce9adf954013e21a9e99c2f197241 [file] [log] [blame]
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartb50f0b92011-05-16 16:31:42 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Chris Masone2b105542011-06-22 10:58:09 -07005#include "shill/wifi.h"
6
Paul Stewartb50f0b92011-05-16 16:31:42 -07007#include <stdio.h>
mukesh agrawalc7426a42011-06-03 13:04:28 -07008#include <string.h>
mukesh agrawalf2f68a52011-09-01 12:15:48 -07009#include <netinet/ether.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -070010#include <linux/if.h> // Needs definitions from netinet/ether.h
Paul Stewartb50f0b92011-05-16 16:31:42 -070011
mukesh agrawal8a3188d2011-12-01 20:56:44 +000012#include <algorithm>
mukesh agrawalab87ea42011-05-18 11:44:49 -070013#include <map>
Paul Stewartced6a0b2011-11-08 15:32:04 -080014#include <set>
Paul Stewartb50f0b92011-05-16 16:31:42 -070015#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070016#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070017
Eric Shienbrood3e20a232012-02-16 11:35:56 -050018#include <base/bind.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070019#include <base/logging.h>
mukesh agrawal15908392011-11-16 18:29:25 +000020#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070021#include <base/string_number_conversions.h>
22#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070023#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080024#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070025
26#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080027#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070028#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070029#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070030#include "shill/event_dispatcher.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070031#include "shill/key_value_store.h"
32#include "shill/ieee80211.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070033#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000034#include "shill/metrics.h"
Gary Morainac1bdb42012-02-16 17:42:29 -080035#include "shill/power_manager.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070036#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080037#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070038#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050039#include "shill/rtnl_handler.h"
Ben Chanfad4a0b2012-04-18 15:49:59 -070040#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080041#include "shill/shill_time.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080042#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070043#include "shill/supplicant_interface_proxy_interface.h"
44#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080045#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070046#include "shill/wifi_endpoint.h"
47#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070048#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070049
Eric Shienbrood3e20a232012-02-16 11:35:56 -050050using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000051using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070052using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080053using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070054using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070055using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070056
Paul Stewartb50f0b92011-05-16 16:31:42 -070057namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070058
59// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080060const char *WiFi::kDefaultBgscanMethod =
61 wpa_supplicant::kNetworkBgscanMethodSimple;
62const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
63const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
64const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070065// Note that WiFi generates some manager-level errors, because it implements
66// the Manager.GetWiFiService flimflam API. The API is implemented here,
67// rather than in manager, to keep WiFi-specific logic in the right place.
mukesh agrawal7a4e4002011-09-06 11:26:05 -070068const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
69const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
70const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070071const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
72 "security mode is unsupported";
mukesh agrawal7a4e4002011-09-06 11:26:05 -070073const char WiFi::kManagerErrorUnsupportedServiceMode[] =
74 "service mode is unsupported";
mukesh agrawal5c05b292012-03-07 10:12:52 -080075// Age (in seconds) beyond which a BSS cache entry will not be preserved,
76// across a suspend/resume.
77const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000078const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070079const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070080const int WiFi::kNumFastScanAttempts = 3;
81const int WiFi::kFastScanIntervalSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070082
Paul Stewartb50f0b92011-05-16 16:31:42 -070083WiFi::WiFi(ControlInterface *control_interface,
84 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080085 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070086 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070087 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080088 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070089 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070090 : Device(control_interface,
91 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080092 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070093 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070094 link,
Chris Masone626719f2011-08-18 16:58:48 -070095 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080096 interface_index,
97 Technology::kWifi),
Eric Shienbrood9a245532012-03-07 14:20:39 -050098 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -070099 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800100 time_(Time::GetInstance()),
mukesh agrawal15908392011-11-16 18:29:25 +0000101 supplicant_state_(kInterfaceStateUnknown),
102 supplicant_bss_("(unknown)"),
Paul Stewart66c86002012-01-30 18:00:52 -0800103 clear_cached_credentials_pending_(false),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800104 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +0200105 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700106 fast_scans_remaining_(kNumFastScanAttempts),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800107 bgscan_method_(kDefaultBgscanMethod),
108 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
109 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700110 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800111 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700112 PropertyStore *store = this->mutable_store();
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800113 HelpRegisterDerivedString(store,
114 flimflam::kBgscanMethodProperty,
115 &WiFi::GetBgscanMethod,
116 &WiFi::SetBgscanMethod);
117 HelpRegisterDerivedUint16(store,
118 flimflam::kBgscanShortIntervalProperty,
119 &WiFi::GetBgscanShortInterval,
120 &WiFi::SetBgscanShortInterval);
121 HelpRegisterDerivedInt32(store,
122 flimflam::kBgscanSignalThresholdProperty,
123 &WiFi::GetBgscanSignalThreshold,
124 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700125
Chris Masoneb925cc82011-06-22 15:39:57 -0700126 // TODO(quiche): Decide if scan_pending_ is close enough to
127 // "currently scanning" that we don't care, or if we want to track
128 // scan pending/currently scanning/no scan scheduled as a tri-state
129 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700130 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800131 HelpRegisterDerivedUint16(store,
132 flimflam::kScanIntervalProperty,
133 &WiFi::GetScanInterval,
134 &WiFi::SetScanInterval);
Ben Chanfad4a0b2012-04-18 15:49:59 -0700135 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700136}
137
mukesh agrawalaf571952011-07-14 14:31:12 -0700138WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700139
Eric Shienbrood9a245532012-03-07 14:20:39 -0500140void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
mukesh agrawalab87ea42011-05-18 11:44:49 -0700141 ::DBus::Path interface_path;
142
mukesh agrawalaf571952011-07-14 14:31:12 -0700143 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700144 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700145 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700146 try {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800147 map<string, DBus::Variant> create_interface_args;
Paul Stewart20550982012-04-16 12:16:11 -0700148 create_interface_args[wpa_supplicant::kInterfacePropertyName].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700149 append_string(link_name().c_str());
Paul Stewart20550982012-04-16 12:16:11 -0700150 create_interface_args[wpa_supplicant::kInterfacePropertyDriver].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700151 append_string(wpa_supplicant::kDriverNL80211);
Paul Stewart20550982012-04-16 12:16:11 -0700152 create_interface_args[
153 wpa_supplicant::kInterfacePropertyConfigFile].writer().
154 append_string(SCRIPTDIR "/wpa_supplicant.conf");
mukesh agrawalc7426a42011-06-03 13:04:28 -0700155 interface_path =
156 supplicant_process_proxy_->CreateInterface(create_interface_args);
Ben Chan80326f32012-05-04 17:51:32 -0700157 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700158 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700159 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700160 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawal7ec71312011-11-10 02:08:26 +0000161 // TODO(quiche): Is it okay to crash here, if device is missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700162 } else {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000163 // TODO(quiche): Log error.
mukesh agrawalc7426a42011-06-03 13:04:28 -0700164 }
165 }
166
mukesh agrawalab87ea42011-05-18 11:44:49 -0700167 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700168 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700169 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700170
Eric Shienbrood9a245532012-03-07 14:20:39 -0500171 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
172 IFF_UP);
mukesh agrawal7ec71312011-11-10 02:08:26 +0000173 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700174
mukesh agrawal7ec71312011-11-10 02:08:26 +0000175 // Clear out any networks that might previously have been configured
mukesh agrawalc7426a42011-06-03 13:04:28 -0700176 // for this interface.
177 supplicant_interface_proxy_->RemoveAllNetworks();
178
mukesh agrawal7ec71312011-11-10 02:08:26 +0000179 // Flush interface's BSS cache, so that we get BSSAdded signals for
mukesh agrawalc7426a42011-06-03 13:04:28 -0700180 // all BSSes (not just new ones since the last scan).
181 supplicant_interface_proxy_->FlushBSS(0);
182
mukesh agrawal23e9f282012-02-17 15:36:36 -0800183 try {
184 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
185 // with RADIUS servers that respond strangely to such requests.
186 // crosbug.com/25630
187 supplicant_interface_proxy_->SetFastReauth(false);
Ben Chan80326f32012-05-04 17:51:32 -0700188 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawalf2028172012-03-13 14:20:22 -0700189 LOG(INFO) << "Failed to disable fast_reauth."
190 << "May be running an older version of wpa_supplicant.";
191 }
192
193 try {
194 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
195 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
Ben Chan80326f32012-05-04 17:51:32 -0700196 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawalf2028172012-03-13 14:20:22 -0700197 LOG(INFO) << "Failed to set scan_interval. "
mukesh agrawal23e9f282012-02-17 15:36:36 -0800198 << "May be running an older version of wpa_supplicant.";
199 }
Paul Stewart2987dcf2012-01-30 15:47:42 -0800200
Gary Morainac1bdb42012-02-16 17:42:29 -0800201 // Register for power state changes. HandlePowerStateChange() will be called
202 // when the power state changes.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500203 manager()->power_manager()->AddStateChangeCallback(
204 UniqueName(),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500205 Bind(&WiFi::HandlePowerStateChange, weak_ptr_factory_.GetWeakPtr()));
Gary Morainac1bdb42012-02-16 17:42:29 -0800206
Darin Petkovc0865312011-09-16 15:31:20 -0700207 Scan(NULL);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700208 StartScanTimer();
Eric Shienbrood9a245532012-03-07 14:20:39 -0500209 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
210 if (error)
211 error->Reset(); // indicate immediate completion
mukesh agrawalab87ea42011-05-18 11:44:49 -0700212}
213
Eric Shienbrood9a245532012-03-07 14:20:39 -0500214void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700215 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal7ec71312011-11-10 02:08:26 +0000216 // TODO(quiche): Remove interface from supplicant.
mukesh agrawalb66c6462012-05-07 11:45:25 -0700217 StopScanTimer();
mukesh agrawal31950242011-07-14 11:53:38 -0700218 supplicant_interface_proxy_.reset(); // breaks a reference cycle
219 supplicant_process_proxy_.reset();
mukesh agrawal15908392011-11-16 18:29:25 +0000220 endpoint_by_rpcid_.clear();
mukesh agrawal15908392011-11-16 18:29:25 +0000221 rpcid_by_service_.clear();
Gary Morain91001122012-02-29 16:22:26 -0800222 manager()->power_manager()->RemoveStateChangeCallback(UniqueName());
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700223
Paul Stewartced6a0b2011-11-08 15:32:04 -0800224 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
225 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700226 ++it) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700227 SLOG(WiFi, 3) << "WiFi " << link_name() << " deregistering service "
228 << (*it)->friendly_name();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700229 manager()->DeregisterService(*it);
230 }
Paul Stewartced6a0b2011-11-08 15:32:04 -0800231 services_.clear(); // breaks reference cycles
mukesh agrawalb20776f2012-02-10 16:00:36 -0800232 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000233 pending_service_ = NULL; // breaks a reference cycle
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700234
Eric Shienbrood9a245532012-03-07 14:20:39 -0500235 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
236 if (error)
237 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700238 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700239
Ben Chanfad4a0b2012-04-18 15:49:59 -0700240 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
241 << (supplicant_process_proxy_.get() ?
242 "is set." : "is not set.");
243 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
244 << (supplicant_interface_proxy_.get() ?
245 "is set." : "is not set.");
246 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
247 << (pending_service_.get() ? "is set." : "is not set.");
248 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
249 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
250 SLOG(WiFi, 3) << "WiFi " << link_name() << " has " << services_.size()
251 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700252}
253
Paul Stewarta41e38d2011-11-11 07:47:29 -0800254bool WiFi::Load(StoreInterface *storage) {
255 LoadHiddenServices(storage);
256 return Device::Load(storage);
257}
258
mukesh agrawal1830fa12011-09-26 14:31:40 -0700259void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700260 LOG(INFO) << __func__;
261
mukesh agrawal7ec71312011-11-10 02:08:26 +0000262 // Needs to send a D-Bus message, but may be called from D-Bus
263 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700264 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500265 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700266}
267
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000268bool WiFi::IsConnectingTo(const WiFiService &service) const {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800269 return pending_service_ == &service ||
270 (current_service_ == &service &&
271 service.state() != Service::kStateConnected);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000272}
273
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000274void WiFi::BSSAdded(const ::DBus::Path &path,
275 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500276 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000277 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500278 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
279 weak_ptr_factory_.GetWeakPtr(),
280 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000281}
282
283void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500284 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000285 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500286 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
287 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700288}
289
Paul Stewartbc6e7392012-05-24 07:07:48 -0700290void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
291 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
292 weak_ptr_factory_.GetWeakPtr(), properties));
293}
294
mukesh agrawal7ec71312011-11-10 02:08:26 +0000295void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
mukesh agrawal15908392011-11-16 18:29:25 +0000296 LOG(INFO) << "In " << __func__ << "(): called";
297 // Called from D-Bus signal handler, but may need to send a D-Bus
298 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500299 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
300 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000301}
302
mukesh agrawalb54601c2011-06-07 17:39:22 -0700303void WiFi::ScanDone() {
304 LOG(INFO) << __func__;
305
mukesh agrawal7ec71312011-11-10 02:08:26 +0000306 // Defer handling of scan result processing, because that processing
307 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700308 // registration can't be done in the context of a D-Bus signal
309 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500310 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
311 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700312}
313
mukesh agrawal6e277772011-09-29 15:04:23 -0700314void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000315 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800316 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700317 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700318
mukesh agrawal7ec71312011-11-10 02:08:26 +0000319 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000320 if (pending_service_ && pending_service_ == service) {
321 // TODO(quiche): Return an error to the caller. crosbug.com/23832
322 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
323 << service->friendly_name()
324 << ", which is already pending.";
325 return;
326 }
327
328 if (pending_service_ && pending_service_ != service) {
329 DisconnectFrom(pending_service_);
330 }
mukesh agrawal32399322011-09-01 10:53:43 -0700331
mukesh agrawal6e277772011-09-29 15:04:23 -0700332 try {
mukesh agrawal15908392011-11-16 18:29:25 +0000333 // TODO(quiche): Set a timeout here. In the normal case, we expect
334 // that, if wpa_supplicant fails to connect, it will eventually send
335 // a signal that its CurrentBSS has changed. But there may be cases
336 // where the signal is not sent. (crosbug.com/23206)
mukesh agrawal64896322011-12-01 01:13:10 +0000337 const uint32_t scan_ssid = 1; // "True": Use directed probe.
338 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
339 append_uint32(scan_ssid);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800340 service_params[wpa_supplicant::kNetworkPropertyBgscan].writer().
341 append_string(CreateBgscanConfigString().c_str());
mukesh agrawal6e277772011-09-29 15:04:23 -0700342 network_path =
343 supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000344 rpcid_by_service_[service] = network_path;
Ben Chan80326f32012-05-04 17:51:32 -0700345 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700346 LOG(ERROR) << "exception while adding network: " << e.what();
347 return;
348 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700349
mukesh agrawal445e72c2011-06-22 11:13:50 -0700350 supplicant_interface_proxy_->SelectNetwork(network_path);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000351 pending_service_ = service;
352 CHECK(current_service_.get() != pending_service_.get());
353
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700354 // SelectService here (instead of in LinkEvent, like Ethernet), so
355 // that, if we fail to bring up L2, we can attribute failure correctly.
356 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000357 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700358 // reconsider if this is the right place to change the selected service.
359 // see discussion in crosbug.com/20191.
360 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000361}
362
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000363void WiFi::DisconnectFrom(WiFiService *service) {
364 if (service != current_service_ && service != pending_service_) {
365 // TODO(quiche): Once we have asynchronous reply support, we should
366 // generate a D-Bus error here. (crosbug.com/23832)
367 LOG(WARNING) << "In " << __func__ << "(): "
368 << " ignoring request to disconnect from service "
369 << service->friendly_name()
370 << " which is neither current nor pending";
371 return;
372 }
373
374 if (pending_service_ && service != pending_service_) {
375 // TODO(quiche): Once we have asynchronous reply support, we should
376 // generate a D-Bus error here. (crosbug.com/23832)
377 LOG(WARNING) << "In " << __func__ << "(): "
378 << " ignoring request to disconnect from service "
379 << service->friendly_name()
380 << " which is not the pending service.";
381 return;
382 }
383
384 if (!pending_service_ && service != current_service_) {
385 // TODO(quiche): Once we have asynchronous reply support, we should
386 // generate a D-Bus error here. (crosbug.com/23832)
387 LOG(WARNING) << "In " << __func__ << "(): "
388 << " ignoring request to disconnect from service "
389 << service->friendly_name()
390 << " which is not the current service.";
391 return;
392 }
393
394 pending_service_ = NULL;
395 try {
396 supplicant_interface_proxy_->Disconnect();
397 // We'll call RemoveNetwork and reset |current_service_| after
398 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700399 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000400 // Can't depend on getting a notification of CurrentBSS change.
401 // So effect changes immediately.
402 ReverseServiceMap::const_iterator rpcid_it =
403 rpcid_by_service_.find(service);
404 DCHECK(rpcid_it != rpcid_by_service_.end());
405 if (rpcid_it == rpcid_by_service_.end()) {
406 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
407 << service->friendly_name() << ": "
408 << "could not find supplicant network to disable.";
409 } else {
410 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
411 }
412 current_service_ = NULL;
413 }
414
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800415 CHECK(current_service_ == NULL ||
416 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000417}
418
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000419bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800420 return !current_service_ && !pending_service_;
421}
422
Paul Stewart66c86002012-01-30 18:00:52 -0800423void WiFi::ClearCachedCredentials() {
424 LOG(INFO) << __func__;
425
426 // Needs to send a D-Bus message, but may be called from D-Bus
427 // caller context (via Manager::PopProfile). So defer work
428 // to event loop.
429 if (!clear_cached_credentials_pending_) {
430 clear_cached_credentials_pending_ = true;
Eric Shienbrood9a245532012-03-07 14:20:39 -0500431 dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
432 weak_ptr_factory_.GetWeakPtr()));
Paul Stewart66c86002012-01-30 18:00:52 -0800433 }
434}
435
mukesh agrawalb20776f2012-02-10 16:00:36 -0800436void WiFi::NotifyEndpointChanged(const WiFiEndpoint &endpoint) {
437 WiFiService *service = FindServiceForEndpoint(endpoint);
438 DCHECK(service);
439 if (service) {
440 service->NotifyEndpointUpdated(endpoint);
441 return;
442 }
443}
444
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800445string WiFi::CreateBgscanConfigString() {
446 return StringPrintf("%s:%d:%d:%d",
447 bgscan_method_.c_str(),
448 bgscan_short_interval_seconds_,
449 bgscan_signal_threshold_dbm_,
450 scan_interval_seconds_);
451}
452
453void WiFi::SetBgscanMethod(const string &method, Error *error) {
454 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
455 method != wpa_supplicant::kNetworkBgscanMethodLearn) {
456 const string error_message =
457 StringPrintf("Unrecognized bgscan method %s", method.c_str());
458 LOG(WARNING) << error_message;
459 error->Populate(Error::kInvalidArguments, error_message);
460 return;
461 }
462
463 bgscan_method_ = method;
464 // We do not update kNetworkPropertyBgscan for |pending_service_| or
465 // |current_service_|, because supplicant does not allow for
466 // reconfiguration without disconnect and reconnect.
467}
468
469void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
470 bgscan_short_interval_seconds_ = seconds;
471 // We do not update kNetworkPropertyBgscan for |pending_service_| or
472 // |current_service_|, because supplicant does not allow for
473 // reconfiguration without disconnect and reconnect.
474}
475
476void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
477 bgscan_signal_threshold_dbm_ = dbm;
478 // We do not update kNetworkPropertyBgscan for |pending_service_| or
479 // |current_service_|, because supplicant does not allow for
480 // reconfiguration without disconnect and reconnect.
481}
482
483void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
484 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700485 if (running()) {
486 StartScanTimer();
487 }
488 // The scan interval affects both foreground scans (handled by
489 // |scan_timer_callback_|), and background scans (handled by
490 // supplicant). However, we do not update |pending_service_| or
491 // |current_service_|, because supplicant does not allow for
492 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800493}
494
mukesh agrawal165e6142011-11-22 02:22:56 +0000495// To avoid creating duplicate services, call FindServiceForEndpoint
496// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000497WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
498 bool hidden_ssid) {
499 WiFiServiceRefPtr service =
500 new WiFiService(control_interface(),
501 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800502 metrics(),
mukesh agrawal15908392011-11-16 18:29:25 +0000503 manager(),
504 this,
505 endpoint.ssid(),
506 endpoint.network_mode(),
507 endpoint.security_mode(),
508 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000509 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000510 return service;
511}
512
513void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700514 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
515 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000516 supplicant_bss_ = new_bss;
517 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
518 HandleDisconnect();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700519 if (!GetHiddenSSIDList().empty()) {
520 // Before disconnecting, wpa_supplicant probably scanned for
521 // APs. So, in the normal case, we defer to the timer for the next scan.
522 //
523 // However, in the case of hidden SSIDs, supplicant knows about
524 // at most one of them. (That would be the hidden SSID we were
525 // connected to, if applicable.)
526 //
527 // So, in this case, we initiate an immediate scan. This scan
528 // will include the hidden SSIDs we know about (up to the limit of
529 // kScanMAxSSIDsPerScan).
530 //
531 // We may want to reconsider this immediate scan, if/when shill
532 // takes greater responsibility for scanning (vs. letting
533 // supplicant handle most of it).
534 Scan(NULL);
535 }
mukesh agrawal15908392011-11-16 18:29:25 +0000536 } else {
537 HandleRoam(new_bss);
538 }
539
540 SelectService(current_service_);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000541 // Invariant check: a Service can either be current, or pending, but
542 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000543 CHECK(current_service_.get() != pending_service_.get() ||
544 current_service_.get() == NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000545}
546
547void WiFi::HandleDisconnect() {
548 // Identify the affected service. We expect to get a disconnect
549 // event when we fall off a Service that we were connected
550 // to. However, we also allow for the case where we get a disconnect
551 // event while attempting to connect from a disconnected state.
552 WiFiService *affected_service =
553 current_service_.get() ? current_service_.get() : pending_service_.get();
554
555 current_service_ = NULL;
556 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700557 SLOG(WiFi, 2) << "WiFi " << link_name()
558 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000559 return;
Paul Stewart20b0a092012-05-22 20:39:57 -0700560 } else if (affected_service == selected_service()) {
561 // If our selected service has disconnected, destroy IP configuration state.
562 DestroyIPConfig();
mukesh agrawal15908392011-11-16 18:29:25 +0000563 }
564
565 ReverseServiceMap::const_iterator rpcid_it =
566 rpcid_by_service_.find(affected_service);
567 if (rpcid_it == rpcid_by_service_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700568 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
569 << " (or failed to connect to) "
570 << affected_service->friendly_name() << ", "
571 << "but could not find supplicant network to disable.";
mukesh agrawalcf24a242012-05-21 16:46:11 -0700572 } else {
573 // TODO(quiche): Reconsider giving up immediately. Maybe give
574 // wpa_supplicant some time to retry, first.
575 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal15908392011-11-16 18:29:25 +0000576 }
577
Ben Chanfad4a0b2012-04-18 15:49:59 -0700578 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
579 << " (or failed to connect to) "
580 << affected_service->friendly_name();
mukesh agrawalcf24a242012-05-21 16:46:11 -0700581 if (SuspectCredentials(*affected_service)) {
582 // If we suspect bad credentials, set failure, to trigger an error
583 // mole in Chrome. Failure is a transient state, and we'll
584 // transition out of it immediately (after this if block).
585 affected_service->SetFailure(Service::kFailureBadCredentials);
586 LOG(ERROR) << "Connection failure during 4-Way Handshake. Bad passphrase?";
587 }
mukesh agrawal568b5c62012-02-28 14:44:47 -0800588 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800589 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000590 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000591
592 if (affected_service == pending_service_.get()) {
593 // The attempt to connect to |pending_service_| failed. Clear
594 // |pending_service_|, to indicate we're no longer in the middle
595 // of a connect request.
596 pending_service_ = NULL;
597 } else if (pending_service_.get()) {
598 // We've attributed the disconnection to what was the
599 // |current_service_|, rather than the |pending_service_|.
600 //
601 // If we're wrong about that (i.e. supplicant reported this
602 // CurrentBSS change after attempting to connect to
603 // |pending_service_|), we're depending on supplicant to retry
604 // connecting to |pending_service_|, and delivering another
605 // CurrentBSS change signal in the future.
606 //
607 // Log this fact, to help us debug (in case our assumptions are
608 // wrong).
Ben Chanfad4a0b2012-04-18 15:49:59 -0700609 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to "
610 << pending_service_->friendly_name()
611 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000612 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700613
614 // If we disconnect, initially scan at a faster frequency, to make sure
615 // we've found all available APs.
616 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000617}
618
619// We use the term "Roam" loosely. In particular, we include the case
620// where we "Roam" to a BSS from the disconnected state.
621void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
622 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
623 if (endpoint_it == endpoint_by_rpcid_.end()) {
624 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
625 << new_bss;
626 return;
627 }
628
629 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000630 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000631 if (!service.get()) {
632 LOG(WARNING) << "WiFi " << link_name()
633 << " could not find Service for Endpoint "
634 << endpoint.bssid_string()
635 << " (service will be unchanged)";
636 return;
637 }
638
Ben Chanfad4a0b2012-04-18 15:49:59 -0700639 SLOG(WiFi, 2) << "WiFi " << link_name()
640 << " roamed to Endpoint " << endpoint.bssid_string()
641 << " (SSID " << endpoint.ssid_string() << ")";
mukesh agrawal15908392011-11-16 18:29:25 +0000642
mukesh agrawale1d90e92012-02-15 17:36:08 -0800643 service->NotifyCurrentEndpoint(&endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000644
mukesh agrawal15908392011-11-16 18:29:25 +0000645 if (pending_service_.get() &&
646 service.get() != pending_service_.get()) {
647 // The Service we've roamed on to is not the one we asked for.
648 // We assume that this is transient, and that wpa_supplicant
649 // is trying / will try to connect to |pending_service_|.
650 //
651 // If it succeeds, we'll end up back here, but with |service|
652 // pointing at the same service as |pending_service_|.
653 //
654 // If it fails, we'll process things in HandleDisconnect.
655 //
656 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700657 SLOG(WiFi, 2) << "WiFi " << link_name()
658 << " new current Endpoint "
659 << endpoint.bssid_string()
660 << " is not part of pending service "
661 << pending_service_->friendly_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000662
663 // Sanity check: if we didn't roam onto |pending_service_|, we
664 // should still be on |current_service_|.
665 if (service.get() != current_service_.get()) {
666 LOG(WARNING) << "WiFi " << link_name()
667 << " new current Endpoint "
668 << endpoint.bssid_string()
669 << " is neither part of pending service "
670 << pending_service_->friendly_name()
671 << " nor part of current service "
672 << (current_service_.get() ?
673 current_service_->friendly_name() :
674 "(NULL)");
675 // Although we didn't expect to get here, we should keep
676 // |current_service_| in sync with what supplicant has done.
677 current_service_ = service;
678 }
679 return;
680 }
681
682 if (pending_service_.get()) {
683 // We assume service.get() == pending_service_.get() here, because
684 // of the return in the previous if clause.
685 //
686 // Boring case: we've connected to the service we asked
687 // for. Simply update |current_service_| and |pending_service_|.
688 current_service_ = service;
689 pending_service_ = NULL;
690 return;
691 }
692
693 // |pending_service_| was NULL, so we weren't attempting to connect
694 // to a new Service. Sanity check that we're still on
695 // |current_service_|.
696 if (service.get() != current_service_.get()) {
697 LOG(WARNING)
698 << "WiFi " << link_name()
699 << " new current Endpoint "
700 << endpoint.bssid_string()
701 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000702 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000703 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000704 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000705 // We didn't expect to be here, but let's cope as well as we
706 // can. Update |current_service_| to keep it in sync with
707 // supplicant.
708 current_service_ = service;
709 return;
710 }
711
712 // At this point, we know that |pending_service_| was NULL, and that
713 // we're still on |current_service_|. This is the most boring case
714 // of all, because there's no state to update here.
715 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700716}
717
Paul Stewarta41e38d2011-11-11 07:47:29 -0800718WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
719 const string &mode,
720 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800721 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
722 it != services_.end();
723 ++it) {
724 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
725 (*it)->IsSecurityMatch(security)) {
726 return *it;
727 }
728 }
729 return NULL;
730}
731
mukesh agrawal165e6142011-11-22 02:22:56 +0000732WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
733 return FindService(endpoint.ssid(),
734 endpoint.network_mode(),
735 endpoint.security_mode());
736}
737
Paul Stewartced6a0b2011-11-08 15:32:04 -0800738ByteArrays WiFi::GetHiddenSSIDList() {
739 // Create a unique set of hidden SSIDs.
740 set<ByteArray> hidden_ssids_set;
741 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
742 it != services_.end();
743 ++it) {
Paul Stewart10ccbb32012-04-26 15:59:30 -0700744 if ((*it)->hidden_ssid() && (*it)->IsRemembered()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800745 hidden_ssids_set.insert((*it)->ssid());
746 }
747 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700748 SLOG(WiFi, 2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800749 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
750 if (!hidden_ssids.empty()) {
751 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
752 // not fit in.
753 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
754 hidden_ssids.erase(
755 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
756 hidden_ssids.end());
757 }
758 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
759 // SSIDs to wpa_supplicant, we need to explicitly specify the default
760 // behavior of doing a broadcast probe.
761 hidden_ssids.push_back(ByteArray());
762 }
763 return hidden_ssids;
764}
765
Paul Stewarta41e38d2011-11-11 07:47:29 -0800766bool WiFi::LoadHiddenServices(StoreInterface *storage) {
767 bool created_hidden_service = false;
768 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
769 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
770 bool is_hidden = false;
771 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700772 SLOG(WiFi, 2) << "Storage group " << *it << " returned by "
773 << "GetGroupsWithKey failed for GetBool("
774 << flimflam::kWifiHiddenSsid
775 << ") -- possible non-bool key";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800776 continue;
777 }
778 if (!is_hidden) {
779 continue;
780 }
781 string ssid_hex;
782 vector<uint8_t> ssid_bytes;
783 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
784 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700785 SLOG(WiFi, 2) << "Hidden network is missing/invalid \""
786 << flimflam::kSSIDProperty << "\" property";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800787 continue;
788 }
789 string device_address;
790 string network_mode;
791 string security;
792 // It is gross that we have to do this, but the only place we can
793 // get information about the service is from its storage name.
794 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
795 &network_mode, &security) ||
796 device_address != address()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700797 SLOG(WiFi, 2) << "Hidden network has unparsable storage identifier \""
798 << *it << "\"";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800799 continue;
800 }
801 if (FindService(ssid_bytes, network_mode, security).get()) {
802 // If service already exists, we have nothing to do, since the
803 // service has already loaded its configuration from storage.
804 // This is guaranteed to happen in both cases where Load() is
805 // called on a device (via a ConfigureDevice() call on the
806 // profile):
807 // - In RegisterDevice() the Device hasn't been started yet,
808 // so it has no services registered, except for those
809 // created by previous iterations of LoadHiddenService().
810 // The latter can happen if another profile in the Manager's
811 // stack defines the same ssid/mode/security. Even this
812 // case is okay, since even if the profiles differ
813 // materially on configuration and credentials, the "right"
814 // one will be configured in the course of the
815 // RegisterService() call below.
816 // - In PushProfile(), all registered services have been
817 // introduced to the profile via ConfigureService() prior
818 // to calling ConfigureDevice() on the profile.
819 continue;
820 }
821 WiFiServiceRefPtr service(new WiFiService(control_interface(),
822 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -0800823 metrics(),
Paul Stewarta41e38d2011-11-11 07:47:29 -0800824 manager(),
825 this,
826 ssid_bytes,
827 network_mode,
828 security,
829 true));
830 services_.push_back(service);
831
832 // By registering the service, the rest of the configuration
833 // will be loaded from the profile into the service via ConfigureService().
834 manager()->RegisterService(service);
835
836 created_hidden_service = true;
837 }
838
839 // If we are idle and we created a service as a result of opening the
840 // profile, we should initiate a scan for our new hidden SSID.
841 if (running() && created_hidden_service &&
842 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
843 Scan(NULL);
844 }
845
846 return created_hidden_service;
847}
848
Paul Stewart66c86002012-01-30 18:00:52 -0800849void WiFi::ClearCachedCredentialsTask() {
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700850 try {
851 supplicant_interface_proxy_->ClearCachedCredentials();
Ben Chan80326f32012-05-04 17:51:32 -0700852 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart57f3d9d2012-04-11 18:21:41 -0700853 LOG(WARNING) << "Clear of cached credentials failed.";
854 }
Paul Stewart66c86002012-01-30 18:00:52 -0800855 clear_cached_credentials_pending_ = false;
856}
857
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000858void WiFi::BSSAddedTask(
859 const ::DBus::Path &path,
860 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000861 // Note: we assume that BSSIDs are unique across endpoints. This
862 // means that if an AP reuses the same BSSID for multiple SSIDs, we
863 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800864 WiFiEndpointRefPtr endpoint(
865 new WiFiEndpoint(proxy_factory_, this, path, properties));
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000866 LOG(INFO) << "Found endpoint. "
mukesh agrawale9adda12012-02-09 18:33:48 -0800867 << "RPC path: " << path << ", "
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000868 << "ssid: " << endpoint->ssid_string() << ", "
869 << "bssid: " << endpoint->bssid_string() << ", "
870 << "signal: " << endpoint->signal_strength() << ", "
Thieu Lee41a72d2012-02-06 20:46:51 +0000871 << "security: " << endpoint->security_mode() << ", "
872 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000873
874 if (endpoint->ssid_string().empty()) {
875 // Don't bother trying to find or create a Service for an Endpoint
876 // without an SSID. We wouldn't be able to connect to it anyway.
877 return;
878 }
879
mukesh agrawalb3857612012-01-18 16:23:29 -0800880 if (endpoint->ssid()[0] == 0) {
881 // Assume that an SSID starting with NULL is bogus/misconfigured,
882 // and filter it out.
883 return;
884 }
885
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000886 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
887 if (service) {
888 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
889 << " to service " << service->friendly_name() << ".";
890 service->AddEndpoint(endpoint);
891
892 if (manager()->HasService(service)) {
893 manager()->UpdateService(service);
894 } else {
895 DCHECK_EQ(1, service->NumEndpoints()); // Expect registered by now if >1.
896 manager()->RegisterService(service);
897 }
mukesh agrawale9adda12012-02-09 18:33:48 -0800898 } else {
899 const bool hidden_ssid = false;
900 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
901 LOG(INFO) << "New service " << service->GetRpcIdentifier()
902 << " (" << service->friendly_name() << ")";
903 service->AddEndpoint(endpoint);
904 manager()->RegisterService(service);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000905 }
906
mukesh agrawale9adda12012-02-09 18:33:48 -0800907 // Do this last, to maintain the invariant that any Endpoint we
908 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800909 //
910 // TODO(quiche): Write test to verify correct behavior in the case
911 // where we get multiple BSSAdded events for a single endpoint.
912 // (Old Endpoint's refcount should fall to zero, and old Endpoint
913 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800914 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800915 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000916}
917
918void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
919 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
920 if (i == endpoint_by_rpcid_.end()) {
921 LOG(WARNING) << "WiFi " << link_name()
922 << " could not find BSS " << path
923 << " to remove.";
924 return;
925 }
926
927 WiFiEndpointRefPtr endpoint = i->second;
928 CHECK(endpoint);
929 endpoint_by_rpcid_.erase(i);
930
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000931 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
mukesh agrawale9adda12012-02-09 18:33:48 -0800932 CHECK(service) << "Can't find Service for Endpoint "
933 << path << " "
934 << "(with BSSID " << endpoint->bssid_string() << ").";
Ben Chanfad4a0b2012-04-18 15:49:59 -0700935 SLOG(WiFi, 2) << "Removing Endpoint " << endpoint->bssid_string()
936 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000937 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000938
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000939 bool disconnect_service = !service->HasEndpoints() &&
940 (service->IsConnecting() || service->IsConnected());
941 bool forget_service =
942 // Forget Services without Endpoints, except that we always keep
943 // hidden services around. (We need them around to populate the
944 // hidden SSIDs list.)
945 !service->HasEndpoints() && !service->hidden_ssid();
946 bool deregister_service =
947 // Only deregister a Service if we're forgetting it. Otherwise,
948 // Manager can't keep our configuration up-to-date (as Profiles
949 // change).
950 forget_service;
951
952 if (disconnect_service) {
953 DisconnectFrom(service);
954 }
955
956 if (deregister_service) {
957 manager()->DeregisterService(service);
958 } else {
959 manager()->UpdateService(service);
960 }
961
962 if (forget_service) {
963 vector<WiFiServiceRefPtr>::iterator it;
964 it = std::find(services_.begin(), services_.end(), service);
965 if (it != services_.end()) {
966 services_.erase(it);
967 }
968 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000969}
970
Paul Stewartbc6e7392012-05-24 07:07:48 -0700971void WiFi::CertificationTask(
972 const map<string, ::DBus::Variant> &properties) {
973 if (!current_service_) {
974 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
975 << " with no current service.";
976 return;
977 }
978
979 map<string, ::DBus::Variant>::const_iterator properties_it =
980 properties.find(wpa_supplicant::kInterfacePropertyDepth);
981 if (properties_it == properties.end()) {
982 LOG(ERROR) << __func__ << " no depth parameter.";
983 return;
984 }
985 uint32 depth = properties_it->second.reader().get_uint32();
986 properties_it = properties.find(wpa_supplicant::kInterfacePropertySubject);
987 if (properties_it == properties.end()) {
988 LOG(ERROR) << __func__ << " no subject parameter.";
989 return;
990 }
991 string subject(properties_it->second.reader().get_string());
992 current_service_->AddEAPCertification(subject, depth);
993}
994
mukesh agrawal15908392011-11-16 18:29:25 +0000995void WiFi::PropertiesChangedTask(
996 const map<string, ::DBus::Variant> &properties) {
997 // TODO(quiche): Handle changes in other properties (e.g. signal
998 // strength).
999
1000 // Note that order matters here. In particular, we want to process
1001 // changes in the current BSS before changes in state. This is so
1002 // that we update the state of the correct Endpoint/Service.
1003
1004 map<string, ::DBus::Variant>::const_iterator properties_it =
1005 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
1006 if (properties_it != properties.end()) {
1007 CurrentBSSChanged(properties_it->second.reader().get_path());
1008 }
1009
1010 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
1011 if (properties_it != properties.end()) {
1012 StateChanged(properties_it->second.reader().get_string());
1013 }
1014}
1015
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001016void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001017 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001018 if (need_bss_flush_) {
1019 CHECK(supplicant_interface_proxy_ != NULL);
1020 // Compute |max_age| relative to |resumed_at_|, to account for the
1021 // time taken to scan.
1022 struct timeval now;
1023 uint32_t max_age;
1024 time_->GetTimeMonotonic(&now);
1025 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1026 supplicant_interface_proxy_->FlushBSS(max_age);
1027 need_bss_flush_ = false;
1028 }
mukesh agrawalb54601c2011-06-07 17:39:22 -07001029 scan_pending_ = false;
mukesh agrawalb66c6462012-05-07 11:45:25 -07001030 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001031}
1032
mukesh agrawal32399322011-09-01 10:53:43 -07001033void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001034 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -08001035 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -07001036 scan_args[wpa_supplicant::kPropertyScanType].writer().
1037 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001038
1039 ByteArrays hidden_ssids = GetHiddenSSIDList();
1040 if (!hidden_ssids.empty()) {
1041 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
1042 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1043 }
1044
Gaurav Shahf8721ee2011-11-07 09:12:46 -08001045 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
Gaurav Shah10109f22011-11-11 20:16:22 -08001046 // FIXME(gauravsh): A scan can fail if, for example, wpa_supplicant
1047 // was restarted. This is done by a few of the 802.1x tests.
1048 // crosbug.com/25657
1049 try {
1050 supplicant_interface_proxy_->Scan(scan_args);
1051 scan_pending_ = true;
Ben Chan80326f32012-05-04 17:51:32 -07001052 } catch (const DBus::Error &e) { // NOLINT
Gaurav Shah10109f22011-11-11 20:16:22 -08001053 LOG(WARNING) << "Scan failed. Attempting to re-connect to the supplicant.";
Eric Shienbrood9a245532012-03-07 14:20:39 -05001054 EnabledStateChangedCallback null_callback;
1055 Stop(NULL, null_callback);
1056 Start(NULL, null_callback);
Gaurav Shah10109f22011-11-11 20:16:22 -08001057 }
mukesh agrawal32399322011-09-01 10:53:43 -07001058}
1059
mukesh agrawal15908392011-11-16 18:29:25 +00001060void WiFi::StateChanged(const string &new_state) {
1061 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001062 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001063 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1064 << old_state << " -> " << new_state;
1065
1066 WiFiService *affected_service;
1067 // Identify the service to which the state change applies. If
1068 // |pending_service_| is non-NULL, then the state change applies to
1069 // |pending_service_|. Otherwise, it applies to |current_service_|.
1070 //
1071 // This policy is driven by the fact that the |pending_service_|
1072 // doesn't become the |current_service_| until wpa_supplicant
1073 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001074 // CurrentBSS change won't be reported until the |pending_service_|
mukesh agrawal15908392011-11-16 18:29:25 +00001075 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
1076 affected_service =
1077 pending_service_.get() ? pending_service_.get() : current_service_.get();
1078 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001079 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1080 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001081 return;
1082 }
1083
mukesh agrawalc01f3982012-01-24 13:48:39 -08001084 if (new_state == wpa_supplicant::kInterfaceStateCompleted &&
1085 !affected_service->IsConnected()) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001086 if (AcquireIPConfigWithLeaseName(
1087 affected_service->GetStorageIdentifier())) {
1088 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001089 affected_service->SetState(Service::kStateConfiguring);
1090 } else {
1091 LOG(ERROR) << "Unable to acquire DHCP config.";
1092 }
mukesh agrawal15908392011-11-16 18:29:25 +00001093 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
1094 affected_service->SetState(Service::kStateAssociating);
1095 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
1096 new_state == wpa_supplicant::kInterfaceStateAssociating ||
1097 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
1098 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
1099 // Ignore transitions into these states from Completed, to avoid
1100 // bothering the user when roaming, or re-keying.
1101 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
1102 affected_service->SetState(Service::kStateAssociating);
1103 // TOOD(quiche): On backwards transitions, we should probably set
1104 // a timeout for getting back into the completed state. At present,
1105 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001106 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001107 // (crosbug.com/23207)
1108 } else {
1109 // Other transitions do not affect Service state.
1110 //
1111 // Note in particular that we ignore a State change into
1112 // kInterfaceStateDisconnected, in favor of observing the corresponding
1113 // change in CurrentBSS.
1114 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001115}
1116
mukesh agrawalcf24a242012-05-21 16:46:11 -07001117bool WiFi::SuspectCredentials(const WiFiService &service) const {
1118 if (!service.IsSecurityMatch(flimflam::kSecurityPsk)) {
1119 // We can only diagnose credentials for WPA/RSN networks. For
1120 // others, assume the failure was not credential related.
1121 return false;
1122 }
1123
1124 return supplicant_state_ == wpa_supplicant::kInterfaceState4WayHandshake &&
1125 !service.has_ever_connected();
1126}
1127
mukesh agrawal7ec71312011-11-10 02:08:26 +00001128// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001129WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
Darin Petkovb65c2452012-02-23 15:17:06 +01001130 CHECK_EQ(args.GetString(flimflam::kTypeProperty), flimflam::kTypeWifi);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001131
1132 if (args.ContainsString(flimflam::kModeProperty) &&
1133 args.GetString(flimflam::kModeProperty) !=
1134 flimflam::kModeManaged) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001135 Error::PopulateAndLog(error, Error::kNotSupported,
1136 kManagerErrorUnsupportedServiceMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001137 return NULL;
1138 }
1139
1140 if (!args.ContainsString(flimflam::kSSIDProperty)) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001141 Error::PopulateAndLog(error, Error::kInvalidArguments,
1142 kManagerErrorSSIDRequired);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001143 return NULL;
1144 }
1145
1146 string ssid = args.GetString(flimflam::kSSIDProperty);
1147 if (ssid.length() < 1) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001148 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1149 kManagerErrorSSIDTooShort);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001150 return NULL;
1151 }
1152
1153 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001154 Error::PopulateAndLog(error, Error::kInvalidNetworkName,
1155 kManagerErrorSSIDTooLong);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001156 return NULL;
1157 }
1158
1159 string security_method;
1160 if (args.ContainsString(flimflam::kSecurityProperty)) {
1161 security_method = args.GetString(flimflam::kSecurityProperty);
1162 } else {
1163 security_method = flimflam::kSecurityNone;
1164 }
1165
1166 if (security_method != flimflam::kSecurityNone &&
1167 security_method != flimflam::kSecurityWep &&
1168 security_method != flimflam::kSecurityPsk &&
1169 security_method != flimflam::kSecurityWpa &&
1170 security_method != flimflam::kSecurityRsn &&
1171 security_method != flimflam::kSecurity8021x) {
mukesh agrawal06175d72012-04-23 16:46:01 -07001172 Error::PopulateAndLog(error, Error::kNotSupported,
1173 kManagerErrorUnsupportedSecurityMode);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001174 return NULL;
1175 }
1176
Paul Stewart6ab23a92011-11-09 17:17:47 -08001177 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001178 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1179 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001180 } else {
1181 // If the service is not found, and the caller hasn't specified otherwise,
1182 // we assume this is is a hidden network.
1183 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001184 }
1185
Paul Stewart6ab23a92011-11-09 17:17:47 -08001186 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1187 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1188 security_method));
1189 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001190 service = new WiFiService(control_interface(),
1191 dispatcher(),
Thieu Le3426c8f2012-01-11 17:35:11 -08001192 metrics(),
mukesh agrawal1a056262011-10-05 14:36:54 -07001193 manager(),
1194 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001195 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001196 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001197 security_method,
1198 hidden_ssid);
1199 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001200 // NB: We do not register the newly created Service with the Manager.
1201 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001202 }
1203
mukesh agrawalb172e4a2012-04-25 18:15:30 -07001204 if ((security_method == flimflam::kSecurityWep ||
1205 security_method == flimflam::kSecurityPsk ||
1206 security_method == flimflam::kSecurityWpa ||
1207 security_method == flimflam::kSecurityRsn) &&
1208 args.ContainsString(flimflam::kPassphraseProperty)) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001209 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
1210 error);
1211 if (error->IsFailure()) {
1212 return NULL;
1213 }
1214 }
1215
Paul Stewartcb59fed2012-03-21 21:14:46 -07001216 service->Configure(args, error);
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001217
Paul Stewart7f61e522012-03-22 11:13:45 -07001218 // TODO(pstew): Schedule a task to forget up all non-hidden services that
1219 // have no endpoints like the one we may have just created. crosbug.com/28224
1220
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001221 return service;
1222}
1223
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001224// static
1225bool WiFi::SanitizeSSID(string *ssid) {
1226 CHECK(ssid);
1227
1228 size_t ssid_len = ssid->length();
1229 size_t i;
1230 bool changed = false;
1231
Gary Morainac1bdb42012-02-16 17:42:29 -08001232 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001233 if (!g_ascii_isprint((*ssid)[i])) {
1234 (*ssid)[i] = '?';
1235 changed = true;
1236 }
1237 }
1238
1239 return changed;
1240}
1241
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001242void WiFi::HelpRegisterDerivedInt32(
1243 PropertyStore *store,
1244 const string &name,
1245 int32(WiFi::*get)(Error *error),
1246 void(WiFi::*set)(const int32 &value, Error *error)) {
1247 store->RegisterDerivedInt32(
1248 name,
1249 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1250}
1251
1252void WiFi::HelpRegisterDerivedString(
1253 PropertyStore *store,
1254 const string &name,
1255 string(WiFi::*get)(Error *error),
1256 void(WiFi::*set)(const string &value, Error *error)) {
1257 store->RegisterDerivedString(
1258 name,
1259 StringAccessor(new CustomAccessor<WiFi, string>(this, get, set)));
1260}
1261
1262void WiFi::HelpRegisterDerivedUint16(
1263 PropertyStore *store,
1264 const string &name,
1265 uint16(WiFi::*get)(Error *error),
1266 void(WiFi::*set)(const uint16 &value, Error *error)) {
1267 store->RegisterDerivedUint16(
1268 name,
1269 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1270}
1271
Gary Morainac1bdb42012-02-16 17:42:29 -08001272void WiFi::HandlePowerStateChange(PowerManager::SuspendState new_state) {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001273 LOG(INFO) << __func__;
1274 if (new_state == PowerManagerProxyDelegate::kOn) {
1275 // We want to flush the BSS cache, but we don't want to conflict
1276 // with a running scan or an active connection attempt. So record
1277 // the need to flush, and take care of flushing when the next scan
1278 // completes.
1279 //
1280 // Note that supplicant will automatically expire old cache
1281 // entries (after, e.g., a BSS is not found in two consecutive
1282 // scans). However, our explicit flush accelerates re-association
1283 // in cases where a BSS disappeared while we were asleep. (See,
1284 // e.g. WiFiRoaming.005SuspendRoam.)
1285 time_->GetTimeMonotonic(&resumed_at_);
1286 need_bss_flush_ = true;
1287
1288 if (!scan_pending_ && IsIdle()) {
1289 // Not scanning/connecting/connected, so let's get things rolling.
1290 Scan(NULL);
mukesh agrawal3ab56ac2012-05-31 14:41:43 -07001291 } else {
1292 SLOG(WiFi, 1) << __func__
1293 << " skipping scan, already scanning or connected.";
mukesh agrawal5c05b292012-03-07 10:12:52 -08001294 }
Gary Morainac1bdb42012-02-16 17:42:29 -08001295 }
1296}
1297
Paul Stewarte369ece2012-05-22 09:11:03 -07001298void WiFi::RestartFastScanAttempts() {
1299 fast_scans_remaining_ = kNumFastScanAttempts;
1300 StartScanTimer();
1301}
1302
mukesh agrawalb66c6462012-05-07 11:45:25 -07001303void WiFi::StartScanTimer() {
1304 if (scan_interval_seconds_ == 0) {
1305 StopScanTimer();
1306 return;
1307 }
1308 scan_timer_callback_.Reset(
1309 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001310 // Repeat the first few scans after disconnect relatively quickly so we
1311 // have reasonable trust that no APs we are looking for are present.
1312 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1313 fast_scans_remaining_ > 0 ?
1314 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001315}
1316
1317void WiFi::StopScanTimer() {
1318 scan_timer_callback_.Cancel();
1319}
1320
1321void WiFi::ScanTimerHandler() {
1322 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1323 if (IsIdle() && !scan_pending_) {
1324 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001325 if (fast_scans_remaining_ > 0) {
1326 --fast_scans_remaining_;
1327 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001328 }
1329 StartScanTimer();
1330}
1331
Paul Stewartb50f0b92011-05-16 16:31:42 -07001332} // namespace shill