blob: bc523b2be581ea209a255274b89f802867c07281 [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 Stewartb50f0b92011-05-16 16:31:42 -070014#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070015#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016
Eric Shienbrood3e20a232012-02-16 11:35:56 -050017#include <base/bind.h>
mukesh agrawal15908392011-11-16 18:29:25 +000018#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070019#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070020#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080021#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070022
Wade Guthried6153612012-08-23 11:36:14 -070023#include "shill/config80211.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070024#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080025#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070026#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070027#include "shill/error.h"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070028#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070029#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070030#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070031#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070032#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000033#include "shill/metrics.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080034#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070035#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050036#include "shill/rtnl_handler.h"
Paul Stewart5581d072012-12-17 17:30:20 -080037#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080038#include "shill/shill_time.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070039#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080040#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070041#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080042#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070043#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080044#include "shill/wifi_provider.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070045#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070046#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070047
Eric Shienbrood3e20a232012-02-16 11:35:56 -050048using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000049using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070050using std::map;
mukesh agrawalab87ea42011-05-18 11:44:49 -070051using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070052using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070053
Paul Stewartb50f0b92011-05-16 16:31:42 -070054namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070055
56// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080057const char *WiFi::kDefaultBgscanMethod =
Paul Stewart0654ece2013-03-26 15:21:26 -070058 WPASupplicant::kNetworkBgscanMethodSimple;
mukesh agrawal4d0401c2012-01-06 16:05:31 -080059const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
60const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
61const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020062// Scan interval while connected.
63const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal5c05b292012-03-07 10:12:52 -080064// Age (in seconds) beyond which a BSS cache entry will not be preserved,
65// across a suspend/resume.
66const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000067const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070068const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070069const int WiFi::kNumFastScanAttempts = 3;
70const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070071const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070072const int WiFi::kReconnectTimeoutSeconds = 10;
mukesh agrawalb54601c2011-06-07 17:39:22 -070073
Paul Stewartb50f0b92011-05-16 16:31:42 -070074WiFi::WiFi(ControlInterface *control_interface,
75 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080076 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070077 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070078 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080079 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070080 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070081 : Device(control_interface,
82 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080083 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070084 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070085 link,
Chris Masone626719f2011-08-18 16:58:48 -070086 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080087 interface_index,
88 Technology::kWifi),
Paul Stewart3c504012013-01-17 17:49:58 -080089 provider_(manager->wifi_provider()),
Eric Shienbrood9a245532012-03-07 14:20:39 -050090 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -070091 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -080092 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +020093 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +000094 supplicant_state_(kInterfaceStateUnknown),
95 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -080096 need_bss_flush_(false),
Darin Petkove636c692012-05-31 10:22:17 +020097 resumed_at_((struct timeval){0}),
Paul Stewarte369ece2012-05-22 09:11:03 -070098 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -070099 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800100 is_debugging_connection_(false),
Paul Stewart1369c2b2013-01-11 05:41:26 -0800101 is_eap_in_progress_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800102 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
103 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700104 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800105 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700106 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200107 store->RegisterDerivedString(
108 flimflam::kBgscanMethodProperty,
109 StringAccessor(
110 // TODO(petkov): CustomMappedAccessor is used for convenience because
111 // it provides a way to define a custom clearer (unlike
112 // CustomAccessor). We need to implement a fully custom accessor with
113 // no extra argument.
114 new CustomMappedAccessor<WiFi, string, int>(this,
115 &WiFi::ClearBgscanMethod,
116 &WiFi::GetBgscanMethod,
117 &WiFi::SetBgscanMethod,
118 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800119 HelpRegisterDerivedUint16(store,
120 flimflam::kBgscanShortIntervalProperty,
121 &WiFi::GetBgscanShortInterval,
122 &WiFi::SetBgscanShortInterval);
123 HelpRegisterDerivedInt32(store,
124 flimflam::kBgscanSignalThresholdProperty,
125 &WiFi::GetBgscanSignalThreshold,
126 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700127
Chris Masoneb925cc82011-06-22 15:39:57 -0700128 // TODO(quiche): Decide if scan_pending_ is close enough to
129 // "currently scanning" that we don't care, or if we want to track
130 // scan pending/currently scanning/no scan scheduled as a tri-state
131 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700132 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800133 HelpRegisterDerivedUint16(store,
134 flimflam::kScanIntervalProperty,
135 &WiFi::GetScanInterval,
136 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800137 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
138 ScopeLogger::kWiFi,
139 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Ben Chanfad4a0b2012-04-18 15:49:59 -0700140 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700141}
142
mukesh agrawalaf571952011-07-14 14:31:12 -0700143WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700144
Eric Shienbrood9a245532012-03-07 14:20:39 -0500145void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200146 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
147 if (enabled()) {
148 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700149 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500150 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200151 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500152 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200153 }
154 if (on_supplicant_appear_.IsCancelled()) {
155 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
156 // device instance.
157 on_supplicant_appear_.Reset(
158 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
159 on_supplicant_vanish_.Reset(
160 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700161 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200162 on_supplicant_appear_.callback(),
163 on_supplicant_vanish_.callback());
164 }
165 // Connect to WPA supplicant if it's already present. If not, we'll connect to
166 // it when it appears.
167 ConnectToSupplicant();
Wade Guthried6153612012-08-23 11:36:14 -0700168 Config80211 *config80211 = Config80211::GetInstance();
169 if (config80211) {
170 config80211->SetWifiState(Config80211::kWifiUp);
171 }
mukesh agrawalab87ea42011-05-18 11:44:49 -0700172}
173
Eric Shienbrood9a245532012-03-07 14:20:39 -0500174void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700175 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200176 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700177 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800178 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
179 it != endpoint_by_rpcid_.end(); ++it) {
180 provider_->OnEndpointRemoved(it->second);
181 }
mukesh agrawal15908392011-11-16 18:29:25 +0000182 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800183 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
184 it != rpcid_by_service_.end(); ++it) {
185 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700186 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700187 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700188 supplicant_interface_proxy_.reset(); // breaks a reference cycle
189 // TODO(quiche): Remove interface from supplicant.
190 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800191 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000192 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800193 is_debugging_connection_ = false;
Paul Stewartd2db2b12013-01-17 13:11:07 -0800194 SetScanPending(false);
Paul Stewart2b05e622012-07-13 20:38:44 -0700195 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700196 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700197
Eric Shienbrood9a245532012-03-07 14:20:39 -0500198 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
199 if (error)
200 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700201 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700202
Ben Chanfad4a0b2012-04-18 15:49:59 -0700203 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
204 << (supplicant_process_proxy_.get() ?
205 "is set." : "is not set.");
206 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
207 << (supplicant_interface_proxy_.get() ?
208 "is set." : "is not set.");
209 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
210 << (pending_service_.get() ? "is set." : "is not set.");
211 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
212 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800213}
214
mukesh agrawal1830fa12011-09-26 14:31:40 -0700215void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700216 LOG(INFO) << __func__;
217
mukesh agrawal7ec71312011-11-10 02:08:26 +0000218 // Needs to send a D-Bus message, but may be called from D-Bus
219 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700220 // to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500221 dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
mukesh agrawal32399322011-09-01 10:53:43 -0700222}
223
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000224void WiFi::BSSAdded(const ::DBus::Path &path,
225 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500226 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000227 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500228 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
229 weak_ptr_factory_.GetWeakPtr(),
230 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000231}
232
233void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500234 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000235 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500236 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
237 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700238}
239
Paul Stewartbc6e7392012-05-24 07:07:48 -0700240void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
241 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
242 weak_ptr_factory_.GetWeakPtr(), properties));
243}
244
Paul Stewartdb0f9172012-11-30 16:48:09 -0800245void WiFi::EAPEvent(const string &status, const string &parameter) {
246 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
247 weak_ptr_factory_.GetWeakPtr(),
248 status,
249 parameter));
250}
251
mukesh agrawal7ec71312011-11-10 02:08:26 +0000252void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200253 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000254 // Called from D-Bus signal handler, but may need to send a D-Bus
255 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500256 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
257 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000258}
259
mukesh agrawalb54601c2011-06-07 17:39:22 -0700260void WiFi::ScanDone() {
261 LOG(INFO) << __func__;
262
mukesh agrawal7ec71312011-11-10 02:08:26 +0000263 // Defer handling of scan result processing, because that processing
264 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700265 // registration can't be done in the context of a D-Bus signal
266 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500267 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
268 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700269}
270
mukesh agrawal6e277772011-09-29 15:04:23 -0700271void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000272 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800273 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700274 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700275
mukesh agrawal7ec71312011-11-10 02:08:26 +0000276 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000277 if (pending_service_ && pending_service_ == service) {
278 // TODO(quiche): Return an error to the caller. crosbug.com/23832
Darin Petkov457728b2013-01-09 09:49:08 +0100279 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
280 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000281 << ", which is already pending.";
282 return;
283 }
284
285 if (pending_service_ && pending_service_ != service) {
286 DisconnectFrom(pending_service_);
287 }
mukesh agrawal32399322011-09-01 10:53:43 -0700288
Paul Stewart835934a2012-12-06 19:27:09 -0800289 Error unused_error;
290 network_path = FindNetworkRpcidForService(service, &unused_error);
291 if (network_path.empty()) {
292 try {
293 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700294 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800295 append_uint32(scan_ssid);
296 AppendBgscan(service, &service_params);
297 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
298 CHECK(!network_path.empty()); // No DBus path should be empty.
299 rpcid_by_service_[service] = network_path;
300 } catch (const DBus::Error &e) { // NOLINT
301 LOG(ERROR) << "exception while adding network: " << e.what();
302 return;
303 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700304 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700305
Paul Stewarta47c3c62012-12-18 12:14:29 -0800306 if (service->HasRecentConnectionIssues()) {
307 SetConnectionDebugging(true);
308 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700309 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700310 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000311 CHECK(current_service_.get() != pending_service_.get());
312
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700313 // SelectService here (instead of in LinkEvent, like Ethernet), so
314 // that, if we fail to bring up L2, we can attribute failure correctly.
315 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000316 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700317 // reconsider if this is the right place to change the selected service.
318 // see discussion in crosbug.com/20191.
319 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000320}
321
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000322void WiFi::DisconnectFrom(WiFiService *service) {
323 if (service != current_service_ && service != pending_service_) {
324 // TODO(quiche): Once we have asynchronous reply support, we should
325 // generate a D-Bus error here. (crosbug.com/23832)
326 LOG(WARNING) << "In " << __func__ << "(): "
327 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100328 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000329 << " which is neither current nor pending";
330 return;
331 }
332
333 if (pending_service_ && service != pending_service_) {
334 // TODO(quiche): Once we have asynchronous reply support, we should
335 // generate a D-Bus error here. (crosbug.com/23832)
336 LOG(WARNING) << "In " << __func__ << "(): "
337 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100338 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000339 << " which is not the pending service.";
340 return;
341 }
342
343 if (!pending_service_ && service != current_service_) {
344 // TODO(quiche): Once we have asynchronous reply support, we should
345 // generate a D-Bus error here. (crosbug.com/23832)
346 LOG(WARNING) << "In " << __func__ << "(): "
347 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100348 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000349 << " which is not the current service.";
350 return;
351 }
352
Paul Stewartff96a842012-08-13 15:59:10 -0700353 if (pending_service_) {
354 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
355 // on this to drive the service state back to idle. Do that here.
356 pending_service_->SetState(Service::kStateIdle);
357 }
358
Paul Stewart2b05e622012-07-13 20:38:44 -0700359 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700360 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700361
362 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700363 LOG(ERROR) << "In " << __func__ << "(): "
364 << "wpa_supplicant is not present; silently resetting "
365 << "current_service_.";
366 if (current_service_ == selected_service()) {
367 DropConnection();
368 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700369 current_service_ = NULL;
370 return;
371 }
372
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000373 try {
374 supplicant_interface_proxy_->Disconnect();
375 // We'll call RemoveNetwork and reset |current_service_| after
376 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700377 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000378 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700379 // So effect changes immediately. For instance, this can happen when
380 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800381 Error unused_error;
382 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700383 if (service == selected_service()) {
384 DropConnection();
385 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000386 current_service_ = NULL;
387 }
388
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800389 CHECK(current_service_ == NULL ||
390 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000391}
392
Paul Stewart835934a2012-12-06 19:27:09 -0800393bool WiFi::DisableNetwork(const ::DBus::Path &network) {
394 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
395 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700396 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800397 try {
398 supplicant_network_proxy->SetEnabled(false);
399 } catch (const DBus::Error &e) { // NOLINT
400 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
401 return false;
402 }
403 return true;
404}
405
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700406bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
407 try {
408 supplicant_interface_proxy_->RemoveNetwork(network);
409 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700410 // RemoveNetwork can fail with three different errors.
411 //
412 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
413 // already removed the network object, so return true as if
414 // RemoveNetwork removes the network object successfully.
415 //
416 // As shill always passes a valid network object path, RemoveNetwork
417 // should not fail with an InvalidArgs error. Return false in such case
418 // as something weird may have happened. Similarly, return false in case
419 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700420 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700421 return false;
422 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700423 }
424 return true;
425}
426
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000427bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800428 return !current_service_ && !pending_service_;
429}
430
Paul Stewart835934a2012-12-06 19:27:09 -0800431void WiFi::ClearCachedCredentials(const WiFiService *service) {
432 Error unused_error;
433 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800434}
435
Paul Stewart3c504012013-01-17 17:49:58 -0800436void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
437 WiFiService *service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800438 DCHECK(service);
439 if (service) {
440 service->NotifyEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800441 }
442}
443
Darin Petkov4a66cc52012-06-15 10:08:29 +0200444void WiFi::AppendBgscan(WiFiService *service,
445 map<string, DBus::Variant> *service_params) const {
446 int scan_interval = kBackgroundScanIntervalSeconds;
447 string method = bgscan_method_;
448 if (method.empty()) {
449 // If multiple APs are detected for this SSID, configure the default method.
450 // Otherwise, disable background scanning completely.
451 if (service->GetEndpointCount() > 1) {
452 method = kDefaultBgscanMethod;
453 } else {
454 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
455 return;
456 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700457 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700458 LOG(INFO) << "Background scan disabled -- chose None method.";
459 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200460 } else {
461 // If the background scan method was explicitly specified, honor the
462 // configured background scan interval.
463 scan_interval = scan_interval_seconds_;
464 }
465 DCHECK(!method.empty());
466 string config_string = StringPrintf("%s:%d:%d:%d",
467 method.c_str(),
468 bgscan_short_interval_seconds_,
469 bgscan_signal_threshold_dbm_,
470 scan_interval);
471 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700472 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200473 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800474}
475
Darin Petkov4a66cc52012-06-15 10:08:29 +0200476string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
477 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
478}
479
480void WiFi::SetBgscanMethod(
481 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700482 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
483 method != WPASupplicant::kNetworkBgscanMethodLearn &&
484 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800485 const string error_message =
486 StringPrintf("Unrecognized bgscan method %s", method.c_str());
487 LOG(WARNING) << error_message;
488 error->Populate(Error::kInvalidArguments, error_message);
489 return;
490 }
491
492 bgscan_method_ = method;
493 // We do not update kNetworkPropertyBgscan for |pending_service_| or
494 // |current_service_|, because supplicant does not allow for
495 // reconfiguration without disconnect and reconnect.
496}
497
498void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
499 bgscan_short_interval_seconds_ = seconds;
500 // We do not update kNetworkPropertyBgscan for |pending_service_| or
501 // |current_service_|, because supplicant does not allow for
502 // reconfiguration without disconnect and reconnect.
503}
504
505void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
506 bgscan_signal_threshold_dbm_ = dbm;
507 // We do not update kNetworkPropertyBgscan for |pending_service_| or
508 // |current_service_|, because supplicant does not allow for
509 // reconfiguration without disconnect and reconnect.
510}
511
512void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
513 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700514 if (running()) {
515 StartScanTimer();
516 }
517 // The scan interval affects both foreground scans (handled by
518 // |scan_timer_callback_|), and background scans (handled by
519 // supplicant). However, we do not update |pending_service_| or
520 // |current_service_|, because supplicant does not allow for
521 // reconfiguration without disconnect and reconnect.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800522}
523
Darin Petkov4a66cc52012-06-15 10:08:29 +0200524void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
525 bgscan_method_.clear();
526}
527
mukesh agrawal15908392011-11-16 18:29:25 +0000528void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700529 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
530 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000531 supplicant_bss_ = new_bss;
Paul Stewartdb0f9172012-11-30 16:48:09 -0800532 supplicant_tls_error_ = "";
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700533 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700534
535 // Any change in CurrentBSS means supplicant is actively changing our
536 // connectivity. We no longer need to track any previously pending
537 // reconnect.
538 StopReconnectTimer();
539
Paul Stewart0654ece2013-03-26 15:21:26 -0700540 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000541 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800542 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700543 // Before disconnecting, wpa_supplicant probably scanned for
544 // APs. So, in the normal case, we defer to the timer for the next scan.
545 //
546 // However, in the case of hidden SSIDs, supplicant knows about
547 // at most one of them. (That would be the hidden SSID we were
548 // connected to, if applicable.)
549 //
550 // So, in this case, we initiate an immediate scan. This scan
551 // will include the hidden SSIDs we know about (up to the limit of
552 // kScanMAxSSIDsPerScan).
553 //
554 // We may want to reconsider this immediate scan, if/when shill
555 // takes greater responsibility for scanning (vs. letting
556 // supplicant handle most of it).
557 Scan(NULL);
558 }
mukesh agrawal15908392011-11-16 18:29:25 +0000559 } else {
560 HandleRoam(new_bss);
561 }
562
Paul Stewart1369c2b2013-01-11 05:41:26 -0800563 // Reset eap_in_progress_ after calling HandleDisconnect() so it can
564 // be used to detect disconnects during EAP authentication.
565 is_eap_in_progress_ = false;
566
Paul Stewart2b05e622012-07-13 20:38:44 -0700567 // If we are selecting a new service, or if we're clearing selection
568 // of a something other than the pending service, call SelectService.
569 // Otherwise skip SelectService, since this will cause the pending
570 // service to be marked as Idle.
571 if (current_service_ || selected_service() != pending_service_) {
572 SelectService(current_service_);
573 }
574
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000575 // Invariant check: a Service can either be current, or pending, but
576 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000577 CHECK(current_service_.get() != pending_service_.get() ||
578 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800579
580 // If we are no longer debugging a problematic WiFi connection, return
581 // to the debugging level indicated by the WiFi debugging scope.
582 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
583 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
584 SetConnectionDebugging(false);
585 }
mukesh agrawal15908392011-11-16 18:29:25 +0000586}
587
588void WiFi::HandleDisconnect() {
589 // Identify the affected service. We expect to get a disconnect
590 // event when we fall off a Service that we were connected
591 // to. However, we also allow for the case where we get a disconnect
592 // event while attempting to connect from a disconnected state.
593 WiFiService *affected_service =
594 current_service_.get() ? current_service_.get() : pending_service_.get();
595
596 current_service_ = NULL;
597 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700598 SLOG(WiFi, 2) << "WiFi " << link_name()
599 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000600 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700601 }
602 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700603 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700604 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000605 }
606
Paul Stewart835934a2012-12-06 19:27:09 -0800607 Error error;
608 if (!DisableNetworkForService(affected_service, &error)) {
609 if (error.type() == Error::kNotFound) {
610 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100611 << " (or failed to connect to) service "
612 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800613 << "but could not find supplicant network to disable.";
614 } else {
615 LOG(FATAL) << "DisableNetwork failed.";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700616 }
mukesh agrawal15908392011-11-16 18:29:25 +0000617 }
618
Ben Chanfad4a0b2012-04-18 15:49:59 -0700619 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100620 << " (or failed to connect to) service "
621 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800622 Service::ConnectFailure failure;
623 if (SuspectCredentials(*affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700624 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700625 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800626 affected_service->SetFailure(failure);
627 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
628 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700629 } else {
630 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700631 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800632 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000633 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000634
635 if (affected_service == pending_service_.get()) {
636 // The attempt to connect to |pending_service_| failed. Clear
637 // |pending_service_|, to indicate we're no longer in the middle
638 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700639 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000640 } else if (pending_service_.get()) {
641 // We've attributed the disconnection to what was the
642 // |current_service_|, rather than the |pending_service_|.
643 //
644 // If we're wrong about that (i.e. supplicant reported this
645 // CurrentBSS change after attempting to connect to
646 // |pending_service_|), we're depending on supplicant to retry
647 // connecting to |pending_service_|, and delivering another
648 // CurrentBSS change signal in the future.
649 //
650 // Log this fact, to help us debug (in case our assumptions are
651 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100652 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
653 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700654 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000655 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700656
657 // If we disconnect, initially scan at a faster frequency, to make sure
658 // we've found all available APs.
659 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000660}
661
662// We use the term "Roam" loosely. In particular, we include the case
663// where we "Roam" to a BSS from the disconnected state.
664void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
665 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
666 if (endpoint_it == endpoint_by_rpcid_.end()) {
667 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
668 << new_bss;
669 return;
670 }
671
Paul Stewart3c504012013-01-17 17:49:58 -0800672 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
673 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000674 if (!service.get()) {
675 LOG(WARNING) << "WiFi " << link_name()
676 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800677 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000678 << " (service will be unchanged)";
679 return;
680 }
681
Ben Chanfad4a0b2012-04-18 15:49:59 -0700682 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800683 << " roamed to Endpoint " << endpoint->bssid_string()
684 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000685
Paul Stewart3c504012013-01-17 17:49:58 -0800686 service->NotifyCurrentEndpoint(endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000687
mukesh agrawal15908392011-11-16 18:29:25 +0000688 if (pending_service_.get() &&
689 service.get() != pending_service_.get()) {
690 // The Service we've roamed on to is not the one we asked for.
691 // We assume that this is transient, and that wpa_supplicant
692 // is trying / will try to connect to |pending_service_|.
693 //
694 // If it succeeds, we'll end up back here, but with |service|
695 // pointing at the same service as |pending_service_|.
696 //
697 // If it fails, we'll process things in HandleDisconnect.
698 //
699 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700700 SLOG(WiFi, 2) << "WiFi " << link_name()
701 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800702 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700703 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100704 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000705
706 // Sanity check: if we didn't roam onto |pending_service_|, we
707 // should still be on |current_service_|.
708 if (service.get() != current_service_.get()) {
709 LOG(WARNING) << "WiFi " << link_name()
710 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800711 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000712 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100713 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000714 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100715 << (current_service_ ?
716 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000717 "(NULL)");
718 // Although we didn't expect to get here, we should keep
719 // |current_service_| in sync with what supplicant has done.
720 current_service_ = service;
721 }
722 return;
723 }
724
725 if (pending_service_.get()) {
726 // We assume service.get() == pending_service_.get() here, because
727 // of the return in the previous if clause.
728 //
729 // Boring case: we've connected to the service we asked
730 // for. Simply update |current_service_| and |pending_service_|.
731 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700732 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000733 return;
734 }
735
736 // |pending_service_| was NULL, so we weren't attempting to connect
737 // to a new Service. Sanity check that we're still on
738 // |current_service_|.
739 if (service.get() != current_service_.get()) {
740 LOG(WARNING)
741 << "WiFi " << link_name()
742 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800743 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000744 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000745 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100746 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000747 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000748 // We didn't expect to be here, but let's cope as well as we
749 // can. Update |current_service_| to keep it in sync with
750 // supplicant.
751 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700752
753 // If this service isn't already marked as actively connecting (likely,
754 // since this service is a bit of a surprise) set the service as
755 // associating.
756 if (!current_service_->IsConnecting()) {
757 current_service_->SetState(Service::kStateAssociating);
758 }
759
mukesh agrawal15908392011-11-16 18:29:25 +0000760 return;
761 }
762
763 // At this point, we know that |pending_service_| was NULL, and that
764 // we're still on |current_service_|. This is the most boring case
765 // of all, because there's no state to update here.
766 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700767}
768
Paul Stewart835934a2012-12-06 19:27:09 -0800769string WiFi::FindNetworkRpcidForService(
770 const WiFiService *service, Error *error) {
771 ReverseServiceMap::const_iterator rpcid_it =
772 rpcid_by_service_.find(service);
773 if (rpcid_it == rpcid_by_service_.end()) {
774 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100775 StringPrintf(
776 "WiFi %s cannot find supplicant network rpcid for service %s",
777 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800778 // There are contexts where this is not an error, such as when a service
779 // is clearing whatever cached credentials may not exist.
780 SLOG(WiFi, 2) << error_message;
781 if (error) {
782 error->Populate(Error::kNotFound, error_message);
783 }
784 return "";
785 }
786
787 return rpcid_it->second;
788}
789
790bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
791 string rpcid = FindNetworkRpcidForService(service, error);
792 if (rpcid.empty()) {
793 // Error is already populated.
794 return false;
795 }
796
797 if (!DisableNetwork(rpcid)) {
798 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100799 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800800 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100801 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800802 rpcid.c_str());
803 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
804
805 // Make sure that such errored networks are removed, so problems do not
806 // propogate to future connection attempts.
807 RemoveNetwork(rpcid);
808 rpcid_by_service_.erase(service);
809
810 return false;
811 }
812
813 return true;
814}
815
816bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
817 string rpcid = FindNetworkRpcidForService(service, error);
818 if (rpcid.empty()) {
819 // Error is already populated.
820 return false;
821 }
822
823 // Erase the rpcid from our tables regardless of failure below, since even
824 // if in failure, we never want to use this network again.
825 rpcid_by_service_.erase(service);
826
827 // TODO(quiche): Reconsider giving up immediately. Maybe give
828 // wpa_supplicant some time to retry, first.
829 if (!RemoveNetwork(rpcid)) {
830 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100831 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800832 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100833 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800834 rpcid.c_str());
835 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
836 return false;
837 }
838
839 return true;
840}
841
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000842void WiFi::BSSAddedTask(
843 const ::DBus::Path &path,
844 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000845 // Note: we assume that BSSIDs are unique across endpoints. This
846 // means that if an AP reuses the same BSSID for multiple SSIDs, we
847 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800848 WiFiEndpointRefPtr endpoint(
849 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -0800850 SLOG(WiFi, 1) << "Found endpoint. "
851 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +0100852 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -0800853 << "bssid: " << endpoint->bssid_string() << ", "
854 << "signal: " << endpoint->signal_strength() << ", "
855 << "security: " << endpoint->security_mode() << ", "
856 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000857
858 if (endpoint->ssid_string().empty()) {
859 // Don't bother trying to find or create a Service for an Endpoint
860 // without an SSID. We wouldn't be able to connect to it anyway.
861 return;
862 }
863
mukesh agrawalb3857612012-01-18 16:23:29 -0800864 if (endpoint->ssid()[0] == 0) {
865 // Assume that an SSID starting with NULL is bogus/misconfigured,
866 // and filter it out.
867 return;
868 }
869
Paul Stewart3c504012013-01-17 17:49:58 -0800870 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000871
mukesh agrawale9adda12012-02-09 18:33:48 -0800872 // Do this last, to maintain the invariant that any Endpoint we
873 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800874 //
875 // TODO(quiche): Write test to verify correct behavior in the case
876 // where we get multiple BSSAdded events for a single endpoint.
877 // (Old Endpoint's refcount should fall to zero, and old Endpoint
878 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800879 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800880 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000881}
882
883void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
884 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
885 if (i == endpoint_by_rpcid_.end()) {
886 LOG(WARNING) << "WiFi " << link_name()
887 << " could not find BSS " << path
888 << " to remove.";
889 return;
890 }
891
892 WiFiEndpointRefPtr endpoint = i->second;
893 CHECK(endpoint);
894 endpoint_by_rpcid_.erase(i);
895
Paul Stewart3c504012013-01-17 17:49:58 -0800896 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
897 if (!service) {
898 return;
899 }
900 Error unused_error;
901 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000902
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000903 bool disconnect_service = !service->HasEndpoints() &&
904 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000905
906 if (disconnect_service) {
907 DisconnectFrom(service);
908 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000909}
910
Paul Stewartbc6e7392012-05-24 07:07:48 -0700911void WiFi::CertificationTask(
912 const map<string, ::DBus::Variant> &properties) {
913 if (!current_service_) {
914 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
915 << " with no current service.";
916 return;
917 }
918
919 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -0700920 properties.find(WPASupplicant::kInterfacePropertyDepth);
Paul Stewartbc6e7392012-05-24 07:07:48 -0700921 if (properties_it == properties.end()) {
922 LOG(ERROR) << __func__ << " no depth parameter.";
923 return;
924 }
925 uint32 depth = properties_it->second.reader().get_uint32();
Paul Stewart0654ece2013-03-26 15:21:26 -0700926 properties_it = properties.find(WPASupplicant::kInterfacePropertySubject);
Paul Stewartbc6e7392012-05-24 07:07:48 -0700927 if (properties_it == properties.end()) {
928 LOG(ERROR) << __func__ << " no subject parameter.";
929 return;
930 }
931 string subject(properties_it->second.reader().get_string());
932 current_service_->AddEAPCertification(subject, depth);
933}
934
Paul Stewartdb0f9172012-11-30 16:48:09 -0800935void WiFi::EAPEventTask(const string &status, const string &parameter) {
936 Service::ConnectFailure failure = Service::kFailureUnknown;
937
938 if (!current_service_) {
939 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
940 << " with no current service.";
941 return;
942 }
943
Paul Stewart0654ece2013-03-26 15:21:26 -0700944 if (status == WPASupplicant::kEAPStatusAcceptProposedMethod) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800945 LOG(INFO) << link_name() << ": accepted EAP method " << parameter;
Paul Stewart0654ece2013-03-26 15:21:26 -0700946 } else if (status == WPASupplicant::kEAPStatusCompletion) {
947 if (parameter == WPASupplicant::kEAPParameterSuccess) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800948 SLOG(WiFi, 2) << link_name() << ": EAP: "
949 << "Completed authentication.";
Paul Stewart1369c2b2013-01-11 05:41:26 -0800950 is_eap_in_progress_ = false;
Paul Stewart0654ece2013-03-26 15:21:26 -0700951 } else if (parameter == WPASupplicant::kEAPParameterFailure) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800952 // If there was a TLS error, use this instead of the generic failure.
Paul Stewart0654ece2013-03-26 15:21:26 -0700953 if (supplicant_tls_error_ == WPASupplicant::kEAPStatusLocalTLSAlert) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800954 failure = Service::kFailureEAPLocalTLS;
955 } else if (supplicant_tls_error_ ==
Paul Stewart0654ece2013-03-26 15:21:26 -0700956 WPASupplicant::kEAPStatusRemoteTLSAlert) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800957 failure = Service::kFailureEAPRemoteTLS;
958 } else {
959 failure = Service::kFailureEAPAuthentication;
960 }
961 } else {
962 LOG(ERROR) << link_name() << ": EAP: "
963 << "Unexpected " << status << " parameter: " << parameter;
964 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700965 } else if (status == WPASupplicant::kEAPStatusLocalTLSAlert ||
966 status == WPASupplicant::kEAPStatusRemoteTLSAlert) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800967 supplicant_tls_error_ = status;
968 } else if (status ==
Paul Stewart0654ece2013-03-26 15:21:26 -0700969 WPASupplicant::kEAPStatusRemoteCertificateVerification) {
970 if (parameter == WPASupplicant::kEAPParameterSuccess) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800971 SLOG(WiFi, 2) << link_name() << ": EAP: "
972 << "Completed remote certificate verification.";
973 } else {
974 // wpa_supplicant doesn't currently have a verification failure
975 // message. We will instead get a RemoteTLSAlert above.
976 LOG(ERROR) << link_name() << ": EAP: "
977 << "Unexpected " << status << " parameter: " << parameter;
978 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700979 } else if (status == WPASupplicant::kEAPStatusParameterNeeded) {
Paul Stewart1369c2b2013-01-11 05:41:26 -0800980 LOG(ERROR) << link_name() << ": EAP: "
981 << "Authentication due to missing authentication parameter: "
982 << parameter;
983 failure = Service::kFailureEAPAuthentication;
Paul Stewart0654ece2013-03-26 15:21:26 -0700984 } else if (status == WPASupplicant::kEAPStatusStarted) {
Paul Stewart1369c2b2013-01-11 05:41:26 -0800985 SLOG(WiFi, 2) << link_name() << ": EAP authentication starting.";
986 is_eap_in_progress_ = true;
Paul Stewartdb0f9172012-11-30 16:48:09 -0800987 }
988
989 if (failure != Service::kFailureUnknown) {
Paul Stewart1369c2b2013-01-11 05:41:26 -0800990 // Avoid a reporting failure twice by clearing eap_in_progress_ early.
mukesh agrawald4dc0832013-03-25 14:38:26 -0700991 Error unused_error;
Paul Stewart1369c2b2013-01-11 05:41:26 -0800992 is_eap_in_progress_ = false;
mukesh agrawald4dc0832013-03-25 14:38:26 -0700993 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -0800994 }
995}
996
mukesh agrawal15908392011-11-16 18:29:25 +0000997void WiFi::PropertiesChangedTask(
998 const map<string, ::DBus::Variant> &properties) {
999 // TODO(quiche): Handle changes in other properties (e.g. signal
1000 // strength).
1001
1002 // Note that order matters here. In particular, we want to process
1003 // changes in the current BSS before changes in state. This is so
1004 // that we update the state of the correct Endpoint/Service.
1005
1006 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -07001007 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +00001008 if (properties_it != properties.end()) {
1009 CurrentBSSChanged(properties_it->second.reader().get_path());
1010 }
1011
Paul Stewart0654ece2013-03-26 15:21:26 -07001012 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +00001013 if (properties_it != properties.end()) {
1014 StateChanged(properties_it->second.reader().get_string());
1015 }
1016}
1017
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001018void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001019 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
mukesh agrawal5c05b292012-03-07 10:12:52 -08001020 if (need_bss_flush_) {
1021 CHECK(supplicant_interface_proxy_ != NULL);
1022 // Compute |max_age| relative to |resumed_at_|, to account for the
1023 // time taken to scan.
1024 struct timeval now;
1025 uint32_t max_age;
1026 time_->GetTimeMonotonic(&now);
1027 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1028 supplicant_interface_proxy_->FlushBSS(max_age);
1029 need_bss_flush_ = false;
1030 }
Paul Stewartd2db2b12013-01-17 13:11:07 -08001031 SetScanPending(false);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001032 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001033}
1034
mukesh agrawal32399322011-09-01 10:53:43 -07001035void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001036 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001037 if (!enabled()) {
1038 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1039 return;
1040 }
1041 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1042 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1043 return;
1044 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001045 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1046 (current_service_.get() && current_service_->IsConnecting())) {
1047 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1048 return;
1049 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001050 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001051 scan_args[WPASupplicant::kPropertyScanType].writer().
1052 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001053
Paul Stewart3c504012013-01-17 17:49:58 -08001054 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -08001055 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -08001056 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1057 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -07001058 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001059 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001060 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001061 hidden_ssids.end());
1062 }
1063 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1064 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1065 // behavior of doing a broadcast probe.
1066 hidden_ssids.push_back(ByteArray());
1067
Paul Stewart0654ece2013-03-26 15:21:26 -07001068 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001069 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1070 }
1071
Gaurav Shah10109f22011-11-11 20:16:22 -08001072 try {
1073 supplicant_interface_proxy_->Scan(scan_args);
Paul Stewartd2db2b12013-01-17 13:11:07 -08001074 SetScanPending(true);
Ben Chan80326f32012-05-04 17:51:32 -07001075 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001076 // A scan may fail if, for example, the wpa_supplicant vanishing
1077 // notification is posted after this task has already started running.
1078 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001079 }
mukesh agrawal32399322011-09-01 10:53:43 -07001080}
1081
Paul Stewartd2db2b12013-01-17 13:11:07 -08001082void WiFi::SetScanPending(bool pending) {
1083 if (scan_pending_ != pending) {
1084 scan_pending_ = pending;
1085 adaptor()->EmitBoolChanged(flimflam::kScanningProperty, pending);
1086 }
1087}
1088
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001089string WiFi::GetServiceLeaseName(const WiFiService &service) {
1090 return service.GetStorageIdentifier();
1091}
1092
1093void WiFi::DestroyServiceLease(const WiFiService &service) {
1094 DestroyIPConfigLease(GetServiceLeaseName(service));
1095}
1096
mukesh agrawal15908392011-11-16 18:29:25 +00001097void WiFi::StateChanged(const string &new_state) {
1098 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001099 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001100 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1101 << old_state << " -> " << new_state;
1102
1103 WiFiService *affected_service;
1104 // Identify the service to which the state change applies. If
1105 // |pending_service_| is non-NULL, then the state change applies to
1106 // |pending_service_|. Otherwise, it applies to |current_service_|.
1107 //
1108 // This policy is driven by the fact that the |pending_service_|
1109 // doesn't become the |current_service_| until wpa_supplicant
1110 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001111 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001112 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001113 affected_service =
1114 pending_service_.get() ? pending_service_.get() : current_service_.get();
1115 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001116 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1117 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001118 return;
1119 }
1120
Paul Stewart0654ece2013-03-26 15:21:26 -07001121 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001122 if (affected_service->IsConnected()) {
1123 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001124 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001125 } else if (has_already_completed_) {
1126 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001127 } else if (AcquireIPConfigWithLeaseName(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001128 GetServiceLeaseName(*affected_service))) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001129 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001130 affected_service->SetState(Service::kStateConfiguring);
1131 } else {
1132 LOG(ERROR) << "Unable to acquire DHCP config.";
1133 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001134 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001135 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001136 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001137 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1138 new_state == WPASupplicant::kInterfaceStateAssociating ||
1139 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1140 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001141 // Ignore transitions into these states from Completed, to avoid
1142 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001143 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001144 affected_service->SetState(Service::kStateAssociating);
1145 // TOOD(quiche): On backwards transitions, we should probably set
1146 // a timeout for getting back into the completed state. At present,
1147 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001148 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001149 // (crosbug.com/23207)
Paul Stewart0654ece2013-03-26 15:21:26 -07001150 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001151 affected_service == current_service_ &&
1152 affected_service->IsConnected()) {
1153 // This means that wpa_supplicant failed in a re-connect attempt, but
1154 // may still be reconnecting. Give wpa_supplicant a limited amount of
1155 // time to transition out this condition by either connecting or changing
1156 // CurrentBSS.
1157 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001158 } else {
1159 // Other transitions do not affect Service state.
1160 //
1161 // Note in particular that we ignore a State change into
1162 // kInterfaceStateDisconnected, in favor of observing the corresponding
1163 // change in CurrentBSS.
1164 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001165}
1166
Paul Stewart1369c2b2013-01-11 05:41:26 -08001167bool WiFi::SuspectCredentials(
1168 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001169 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001170 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewart1369c2b2013-01-11 05:41:26 -08001171 !service.has_ever_connected()) {
1172 if (failure) {
1173 *failure = Service::kFailureBadPassphrase;
1174 }
1175 return true;
1176 }
1177 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
1178 if (is_eap_in_progress_ && !service.has_ever_connected()) {
1179 if (failure) {
1180 *failure = Service::kFailureEAPAuthentication;
1181 }
1182 return true;
1183 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001184 }
1185
Paul Stewart1369c2b2013-01-11 05:41:26 -08001186 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001187}
1188
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001189// static
1190bool WiFi::SanitizeSSID(string *ssid) {
1191 CHECK(ssid);
1192
1193 size_t ssid_len = ssid->length();
1194 size_t i;
1195 bool changed = false;
1196
Gary Morainac1bdb42012-02-16 17:42:29 -08001197 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001198 if (!g_ascii_isprint((*ssid)[i])) {
1199 (*ssid)[i] = '?';
1200 changed = true;
1201 }
1202 }
1203
1204 return changed;
1205}
1206
Darin Petkov50cb78a2013-02-06 16:17:49 +01001207// static
1208string WiFi::LogSSID(const string &ssid) {
1209 string out;
1210 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1211 // Replace '[' and ']' (in addition to non-printable characters) so that
1212 // it's easy to match the right substring through a non-greedy regex.
1213 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1214 base::StringAppendF(&out, "\\x%02x", *it);
1215 } else {
1216 out += *it;
1217 }
1218 }
1219 return StringPrintf("[SSID=%s]", out.c_str());
1220}
1221
Paul Stewart3c508e12012-08-09 11:40:06 -07001222void WiFi::OnLinkMonitorFailure() {
1223 // If we have never found the gateway, let's be conservative and not
1224 // do anything, in case this network topology does not have a gateway.
1225 if (!link_monitor()->IsGatewayFound()) {
1226 LOG(INFO) << "In " << __func__ << "(): "
1227 << "Skipping reassociate since gateway was never found.";
1228 return;
1229 }
1230
1231 if (!supplicant_present_) {
1232 LOG(ERROR) << "In " << __func__ << "(): "
1233 << "wpa_supplicant is not present. Cannot reassociate.";
1234 return;
1235 }
1236
1237 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001238 // This will force a transition out of connected, if we are actually
1239 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001240 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001241 // If we don't eventually get a transition back into a connected state,
1242 // there is something wrong.
1243 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001244 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1245 } catch (const DBus::Error &e) { // NOLINT
1246 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1247 return;
1248 }
1249}
1250
Arman Ugurayed8e6102012-11-29 14:47:20 -08001251bool WiFi::ShouldUseArpGateway() const {
1252 return true;
1253}
1254
Paul Stewart3c504012013-01-17 17:49:58 -08001255void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1256 DisconnectFrom(service);
1257 if (service == selected_service()) {
1258 DropConnection();
1259 }
1260 Error unused_error;
1261 RemoveNetworkForService(service, &unused_error);
1262}
1263
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001264vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1265 vector<GeolocationInfo> objects;
1266 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1267 it != endpoint_by_rpcid_.end();
1268 ++it) {
1269 GeolocationInfo geoinfo;
1270 WiFiEndpointRefPtr endpoint = it->second;
1271 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1272 geoinfo.AddField(kGeoSignalStrengthProperty,
1273 StringPrintf("%d", endpoint->signal_strength()));
1274 geoinfo.AddField(
1275 kGeoChannelProperty,
1276 StringPrintf("%d",
1277 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1278 // TODO(gauravsh): Include age field. crosbug.com/35445
1279 objects.push_back(geoinfo);
1280 }
1281 return objects;
1282}
1283
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001284void WiFi::HelpRegisterDerivedInt32(
1285 PropertyStore *store,
1286 const string &name,
1287 int32(WiFi::*get)(Error *error),
1288 void(WiFi::*set)(const int32 &value, Error *error)) {
1289 store->RegisterDerivedInt32(
1290 name,
1291 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1292}
1293
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001294void WiFi::HelpRegisterDerivedUint16(
1295 PropertyStore *store,
1296 const string &name,
1297 uint16(WiFi::*get)(Error *error),
1298 void(WiFi::*set)(const uint16 &value, Error *error)) {
1299 store->RegisterDerivedUint16(
1300 name,
1301 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1302}
1303
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001304void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001305 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001306 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001307
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001308 // We want to flush the BSS cache, but we don't want to conflict
1309 // with a running scan or an active connection attempt. So record
1310 // the need to flush, and take care of flushing when the next scan
1311 // completes.
1312 //
1313 // Note that supplicant will automatically expire old cache
1314 // entries (after, e.g., a BSS is not found in two consecutive
1315 // scans). However, our explicit flush accelerates re-association
1316 // in cases where a BSS disappeared while we were asleep. (See,
1317 // e.g. WiFiRoaming.005SuspendRoam.)
1318 time_->GetTimeMonotonic(&resumed_at_);
1319 need_bss_flush_ = true;
1320
1321 if (!scan_pending_ && IsIdle()) {
1322 // Not scanning/connecting/connected, so let's get things rolling.
1323 Scan(NULL);
1324 } else {
1325 SLOG(WiFi, 1) << __func__
1326 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001327 }
1328}
1329
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001330void WiFi::OnConnected() {
1331 Device::OnConnected();
1332 EnableHighBitrates();
1333}
1334
Paul Stewarte369ece2012-05-22 09:11:03 -07001335void WiFi::RestartFastScanAttempts() {
1336 fast_scans_remaining_ = kNumFastScanAttempts;
1337 StartScanTimer();
1338}
1339
mukesh agrawalb66c6462012-05-07 11:45:25 -07001340void WiFi::StartScanTimer() {
1341 if (scan_interval_seconds_ == 0) {
1342 StopScanTimer();
1343 return;
1344 }
1345 scan_timer_callback_.Reset(
1346 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001347 // Repeat the first few scans after disconnect relatively quickly so we
1348 // have reasonable trust that no APs we are looking for are present.
1349 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1350 fast_scans_remaining_ > 0 ?
1351 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001352}
1353
1354void WiFi::StopScanTimer() {
1355 scan_timer_callback_.Cancel();
1356}
1357
1358void WiFi::ScanTimerHandler() {
1359 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1360 if (IsIdle() && !scan_pending_) {
1361 Scan(NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001362 if (fast_scans_remaining_ > 0) {
1363 --fast_scans_remaining_;
1364 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001365 }
1366 StartScanTimer();
1367}
1368
Paul Stewart2b05e622012-07-13 20:38:44 -07001369void WiFi::StartPendingTimer() {
1370 pending_timeout_callback_.Reset(
1371 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1372 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1373 kPendingTimeoutSeconds * 1000);
1374}
1375
1376void WiFi::StopPendingTimer() {
1377 pending_timeout_callback_.Cancel();
1378}
1379
1380void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001381 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001382 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001383 if (service) {
1384 service->SetState(Service::kStateAssociating);
1385 StartPendingTimer();
1386 } else if (pending_service_) {
1387 StopPendingTimer();
1388 }
1389 pending_service_ = service;
1390}
1391
1392void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001393 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001394 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1395 CHECK(pending_service_);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001396 pending_service_->DisconnectWithFailure(
1397 Service::kFailureOutOfRange, &unused_error);
Paul Stewart2b05e622012-07-13 20:38:44 -07001398}
1399
Paul Stewart44663922012-07-30 11:03:03 -07001400void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001401 if (!reconnect_timeout_callback_.IsCancelled()) {
1402 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1403 << ": reconnect timer already running.";
1404 return;
1405 }
Paul Stewart44663922012-07-30 11:03:03 -07001406 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1407 reconnect_timeout_callback_.Reset(
1408 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1409 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1410 kReconnectTimeoutSeconds * 1000);
1411}
1412
1413void WiFi::StopReconnectTimer() {
1414 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1415 reconnect_timeout_callback_.Cancel();
1416}
1417
1418void WiFi::ReconnectTimeoutHandler() {
1419 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001420 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001421 CHECK(current_service_);
1422 current_service_->SetFailureSilent(Service::kFailureConnect);
1423 DisconnectFrom(current_service_);
1424}
1425
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001426void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1427 LOG(INFO) << "WPA supplicant appeared.";
1428 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001429 // Restart the WiFi device if it's started already. This will reset the
1430 // state and connect the device to the new WPA supplicant instance.
1431 if (enabled()) {
1432 Restart();
1433 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001434 return;
1435 }
1436 supplicant_present_ = true;
1437 ConnectToSupplicant();
1438}
1439
1440void WiFi::OnSupplicantVanish() {
1441 LOG(INFO) << "WPA supplicant vanished.";
1442 if (!supplicant_present_) {
1443 return;
1444 }
1445 supplicant_present_ = false;
1446 // Restart the WiFi device if it's started already. This will effectively
1447 // suspend the device until the WPA supplicant reappears.
1448 if (enabled()) {
1449 Restart();
1450 }
1451}
1452
Paul Stewart5581d072012-12-17 17:30:20 -08001453void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1454 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1455 if (!supplicant_process_proxy_.get()) {
1456 SLOG(WiFi, 2) << "Suplicant process proxy not present.";
1457 return;
1458 }
1459 string current_level;
1460 try {
1461 current_level = supplicant_process_proxy_->GetDebugLevel();
1462 } catch (const DBus::Error &e) { // NOLINT
1463 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1464 return;
1465 }
1466
Paul Stewart0654ece2013-03-26 15:21:26 -07001467 if (current_level != WPASupplicant::kDebugLevelInfo &&
1468 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001469 SLOG(WiFi, 2) << "WiFi debug level is currently "
1470 << current_level
1471 << "; assuming that it is being controlled elsewhere.";
1472 return;
1473 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001474 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1475 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001476
1477 if (new_level == current_level) {
1478 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1479 << current_level;
1480 return;
1481 }
1482
1483 try {
1484 supplicant_process_proxy_->SetDebugLevel(new_level);
1485 } catch (const DBus::Error &e) { // NOLINT
1486 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1487 }
1488}
1489
Paul Stewarta47c3c62012-12-18 12:14:29 -08001490void WiFi::SetConnectionDebugging(bool enabled) {
1491 if (is_debugging_connection_ == enabled) {
1492 return;
1493 }
1494 OnWiFiDebugScopeChanged(
1495 enabled ||
1496 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1497 is_debugging_connection_ = enabled;
1498}
1499
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001500void WiFi::ConnectToSupplicant() {
1501 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1502 << " supplicant: "
1503 << (supplicant_present_ ? "present" : "absent")
1504 << " proxy: "
1505 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1506 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1507 return;
1508 }
1509 supplicant_process_proxy_.reset(
1510 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001511 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001512 OnWiFiDebugScopeChanged(
1513 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001514 ::DBus::Path interface_path;
1515 try {
1516 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001517 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001518 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001519 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1520 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001521 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001522 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001523 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001524 interface_path =
1525 supplicant_process_proxy_->CreateInterface(create_interface_args);
1526 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001527 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001528 interface_path =
1529 supplicant_process_proxy_->GetInterface(link_name());
1530 // TODO(quiche): Is it okay to crash here, if device is missing?
1531 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001532 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1533 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001534 }
1535 }
1536
1537 supplicant_interface_proxy_.reset(
1538 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001539 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001540
1541 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1542 IFF_UP);
1543 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1544
1545 // Clear out any networks that might previously have been configured
1546 // for this interface.
1547 supplicant_interface_proxy_->RemoveAllNetworks();
1548
1549 // Flush interface's BSS cache, so that we get BSSAdded signals for
1550 // all BSSes (not just new ones since the last scan).
1551 supplicant_interface_proxy_->FlushBSS(0);
1552
1553 try {
1554 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1555 // with RADIUS servers that respond strangely to such requests.
1556 // crosbug.com/25630
1557 supplicant_interface_proxy_->SetFastReauth(false);
1558 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001559 LOG(ERROR) << "Failed to disable fast_reauth. "
1560 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001561 }
1562
1563 try {
1564 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1565 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1566 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001567 LOG(ERROR) << "Failed to set scan_interval. "
1568 << "May be running an older version of wpa_supplicant.";
1569 }
1570
1571 try {
1572 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1573 } catch (const DBus::Error &e) { // NOLINT
1574 LOG(ERROR) << "Failed to disable high bitrates. "
1575 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001576 }
1577
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001578 Scan(NULL);
1579 StartScanTimer();
1580}
1581
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001582void WiFi::EnableHighBitrates() {
1583 LOG(INFO) << "Enabling high bitrates.";
1584 try {
1585 supplicant_interface_proxy_->EnableHighBitrates();
1586 } catch (const DBus::Error &e) { // NOLINT
1587 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1588 }
1589}
1590
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001591void WiFi::Restart() {
1592 LOG(INFO) << link_name() << " restarting.";
1593 WiFiRefPtr me = this; // Make sure we don't get destructed.
1594 // Go through the manager rather than starting and stopping the device
1595 // directly so that the device can be configured with the profile.
1596 manager()->DeregisterDevice(me);
1597 manager()->RegisterDevice(me);
1598}
1599
Paul Stewartb50f0b92011-05-16 16:31:42 -07001600} // namespace shill