blob: d73ed656e1aace1b4dbe76ee5ea3c9311dfefa79 [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
Wade Guthrie92d06362013-04-25 15:41:30 -07007#include <linux/if.h> // Needs definitions from netinet/ether.h
8#include <netinet/ether.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -07009#include <stdio.h>
mukesh agrawalc7426a42011-06-03 13:04:28 -070010#include <string.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070011
mukesh agrawal8a3188d2011-12-01 20:56:44 +000012#include <algorithm>
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070013#include <limits>
mukesh agrawalab87ea42011-05-18 11:44:49 -070014#include <map>
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>
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070019#include <base/file_path.h>
20#include <base/file_util.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070021#include <base/string_util.h>
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070022#include <base/stringprintf.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"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070030#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070031#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070032#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070033#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070034#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000035#include "shill/metrics.h"
Wade Guthriebb9fca22013-04-10 17:21:42 -070036#include "shill/netlink_manager.h"
Wade Guthriebee87c22013-03-06 11:00:46 -080037#include "shill/nl80211_message.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080038#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070039#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050040#include "shill/rtnl_handler.h"
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070041#include "shill/scan_session.h"
Paul Stewart5581d072012-12-17 17:30:20 -080042#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080043#include "shill/shill_time.h"
Paul Stewart735eab52013-03-29 09:19:23 -070044#include "shill/supplicant_eap_state_handler.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070045#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080046#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070047#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080048#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070049#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080050#include "shill/wifi_provider.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070051#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070052#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070053
Eric Shienbrood3e20a232012-02-16 11:35:56 -050054using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000055using base::StringPrintf;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070056using file_util::PathExists;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070057using std::map;
mukesh agrawalab87ea42011-05-18 11:44:49 -070058using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070059using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070060
Paul Stewartb50f0b92011-05-16 16:31:42 -070061namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070062
63// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080064const char *WiFi::kDefaultBgscanMethod =
Paul Stewart0654ece2013-03-26 15:21:26 -070065 WPASupplicant::kNetworkBgscanMethodSimple;
mukesh agrawal4d0401c2012-01-06 16:05:31 -080066const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
67const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
68const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020069// Scan interval while connected.
70const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal5c05b292012-03-07 10:12:52 -080071// Age (in seconds) beyond which a BSS cache entry will not be preserved,
72// across a suspend/resume.
73const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000074const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070075const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070076const int WiFi::kNumFastScanAttempts = 3;
77const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070078const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070079const int WiFi::kReconnectTimeoutSeconds = 10;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070080const size_t WiFi::kMinumumFrequenciesToScan = 4; // Arbitrary but > 0.
81const char WiFi::kProgressiveScanFlagFile[] = "/home/chronos/.progressive_scan";
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),
Paul Stewart3c504012013-01-17 17:49:58 -080098 provider_(manager->wifi_provider()),
Eric Shienbrood9a245532012-03-07 14:20:39 -050099 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700100 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800101 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200102 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000103 supplicant_state_(kInterfaceStateUnknown),
104 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800105 need_bss_flush_(false),
Wade Guthriebb9fca22013-04-10 17:21:42 -0700106 resumed_at_((struct timeval) {0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700107 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700108 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800109 is_debugging_connection_(false),
Paul Stewart735eab52013-03-29 09:19:23 -0700110 eap_state_handler_(new SupplicantEAPStateHandler()),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800111 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
112 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -0700113 scan_pending_(false),
Wade Guthriebee87c22013-03-06 11:00:46 -0800114 scan_interval_seconds_(kDefaultScanIntervalSeconds),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700115 progressive_scan_enabled_(false),
116 netlink_manager_(NetlinkManager::GetInstance()),
117 min_frequencies_to_scan_(kMinumumFrequenciesToScan),
118 max_frequencies_to_scan_(std::numeric_limits<int>::max()) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700119 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200120 store->RegisterDerivedString(
121 flimflam::kBgscanMethodProperty,
122 StringAccessor(
123 // TODO(petkov): CustomMappedAccessor is used for convenience because
124 // it provides a way to define a custom clearer (unlike
125 // CustomAccessor). We need to implement a fully custom accessor with
126 // no extra argument.
127 new CustomMappedAccessor<WiFi, string, int>(this,
128 &WiFi::ClearBgscanMethod,
129 &WiFi::GetBgscanMethod,
130 &WiFi::SetBgscanMethod,
131 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800132 HelpRegisterDerivedUint16(store,
133 flimflam::kBgscanShortIntervalProperty,
134 &WiFi::GetBgscanShortInterval,
135 &WiFi::SetBgscanShortInterval);
136 HelpRegisterDerivedInt32(store,
137 flimflam::kBgscanSignalThresholdProperty,
138 &WiFi::GetBgscanSignalThreshold,
139 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700140
Chris Masoneb925cc82011-06-22 15:39:57 -0700141 // TODO(quiche): Decide if scan_pending_ is close enough to
142 // "currently scanning" that we don't care, or if we want to track
143 // scan pending/currently scanning/no scan scheduled as a tri-state
144 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700145 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800146 HelpRegisterDerivedUint16(store,
147 flimflam::kScanIntervalProperty,
148 &WiFi::GetScanInterval,
149 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800150 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
151 ScopeLogger::kWiFi,
152 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Wade Guthriebb9fca22013-04-10 17:21:42 -0700153 CHECK(netlink_manager_);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700154 progressive_scan_enabled_ = PathExists(FilePath(kProgressiveScanFlagFile));
Ben Chanfad4a0b2012-04-18 15:49:59 -0700155 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700156}
157
mukesh agrawalaf571952011-07-14 14:31:12 -0700158WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700159
Eric Shienbrood9a245532012-03-07 14:20:39 -0500160void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200161 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
162 if (enabled()) {
163 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700164 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500165 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200166 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500167 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200168 }
169 if (on_supplicant_appear_.IsCancelled()) {
170 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
171 // device instance.
172 on_supplicant_appear_.Reset(
173 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
174 on_supplicant_vanish_.Reset(
175 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700176 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200177 on_supplicant_appear_.callback(),
178 on_supplicant_vanish_.callback());
179 }
180 // Connect to WPA supplicant if it's already present. If not, we'll connect to
181 // it when it appears.
182 ConnectToSupplicant();
Wade Guthriebee87c22013-03-06 11:00:46 -0800183 // Subscribe to multicast events.
Wade Guthriebb9fca22013-04-10 17:21:42 -0700184 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
185 NetlinkManager::kEventTypeConfig);
186 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
187 NetlinkManager::kEventTypeScan);
188 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
189 NetlinkManager::kEventTypeRegulatory);
190 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
191 NetlinkManager::kEventTypeMlme);
Wade Guthrie92d06362013-04-25 15:41:30 -0700192 ConfigureScanFrequencies();
mukesh agrawalab87ea42011-05-18 11:44:49 -0700193}
194
Eric Shienbrood9a245532012-03-07 14:20:39 -0500195void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700196 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200197 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700198 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800199 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
200 it != endpoint_by_rpcid_.end(); ++it) {
201 provider_->OnEndpointRemoved(it->second);
202 }
mukesh agrawal15908392011-11-16 18:29:25 +0000203 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800204 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
205 it != rpcid_by_service_.end(); ++it) {
206 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700207 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700208 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700209 supplicant_interface_proxy_.reset(); // breaks a reference cycle
210 // TODO(quiche): Remove interface from supplicant.
211 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800212 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000213 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800214 is_debugging_connection_ = false;
Paul Stewartd2db2b12013-01-17 13:11:07 -0800215 SetScanPending(false);
Paul Stewart2b05e622012-07-13 20:38:44 -0700216 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700217 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700218
Eric Shienbrood9a245532012-03-07 14:20:39 -0500219 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
220 if (error)
221 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700222 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700223
Ben Chanfad4a0b2012-04-18 15:49:59 -0700224 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
225 << (supplicant_process_proxy_.get() ?
226 "is set." : "is not set.");
227 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
228 << (supplicant_interface_proxy_.get() ?
229 "is set." : "is not set.");
230 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
231 << (pending_service_.get() ? "is set." : "is not set.");
232 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
233 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800234}
235
Wade Guthrie68d41092013-04-02 12:56:02 -0700236void WiFi::Scan(ScanType scan_type, Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700237 LOG(INFO) << __func__;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700238 // Measure scans that are supposed to be "progressive" regardless of whether
239 // we are actually doing a progressive scan.
240 if (scan_type == kProgressiveScan) {
Wade Guthrie68d41092013-04-02 12:56:02 -0700241 metrics()->NotifyDeviceScanStarted(interface_index());
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700242 }
Wade Guthrie68d41092013-04-02 12:56:02 -0700243
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700244 if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
245 SLOG(WiFi, 4) << "Doing progressive scan on " << link_name();
246 if (!scan_session_) {
247 // TODO(wdg): Perform in-depth testing to determine the best values for
248 // the different scans. chromium:235293
249 ScanSession::FractionList scan_fractions;
250 scan_fractions.push_back(.33); // First scan gets 33 percentile.
251 scan_fractions.push_back(.33); // Second scan gets 66th percentile.
252 scan_fractions.push_back(ScanSession::kAllFrequencies);
253 scan_session_.reset(
254 new ScanSession(netlink_manager_,
255 dispatcher(),
256 provider_->GetScanFrequencies(),
257 all_scan_frequencies_,
258 interface_index(),
259 scan_fractions,
260 min_frequencies_to_scan_,
261 max_frequencies_to_scan_,
262 Bind(&WiFi::OnFailedProgressiveScan,
263 weak_ptr_factory_.GetWeakPtr())));
264 for (const auto &ssid : provider_->GetHiddenSSIDList()) {
265 scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
266 }
267 }
268 dispatcher()->PostTask(
269 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
270 } else {
271 SLOG(WiFi, 4) << "Doing full scan - progressive scan "
272 << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED");
273 // Needs to send a D-Bus message, but may be called from D-Bus
274 // signal handler context (via Manager::RequestScan). So defer work
275 // to event loop.
276 dispatcher()->PostTask(
277 Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
278 }
mukesh agrawal32399322011-09-01 10:53:43 -0700279}
280
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000281void WiFi::BSSAdded(const ::DBus::Path &path,
282 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500283 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000284 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500285 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
286 weak_ptr_factory_.GetWeakPtr(),
287 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000288}
289
290void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500291 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000292 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500293 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
294 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700295}
296
Paul Stewartbc6e7392012-05-24 07:07:48 -0700297void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
298 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
299 weak_ptr_factory_.GetWeakPtr(), properties));
300}
301
Paul Stewartdb0f9172012-11-30 16:48:09 -0800302void WiFi::EAPEvent(const string &status, const string &parameter) {
303 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
304 weak_ptr_factory_.GetWeakPtr(),
305 status,
306 parameter));
307}
308
mukesh agrawal7ec71312011-11-10 02:08:26 +0000309void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200310 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000311 // Called from D-Bus signal handler, but may need to send a D-Bus
312 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500313 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
314 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000315}
316
mukesh agrawalb54601c2011-06-07 17:39:22 -0700317void WiFi::ScanDone() {
318 LOG(INFO) << __func__;
319
mukesh agrawal7ec71312011-11-10 02:08:26 +0000320 // Defer handling of scan result processing, because that processing
321 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700322 // registration can't be done in the context of a D-Bus signal
323 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500324 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
325 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700326}
327
mukesh agrawal6e277772011-09-29 15:04:23 -0700328void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000329 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800330 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700331 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700332
mukesh agrawal7ec71312011-11-10 02:08:26 +0000333 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000334 if (pending_service_ && pending_service_ == service) {
335 // TODO(quiche): Return an error to the caller. crosbug.com/23832
Darin Petkov457728b2013-01-09 09:49:08 +0100336 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
337 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000338 << ", which is already pending.";
339 return;
340 }
341
342 if (pending_service_ && pending_service_ != service) {
343 DisconnectFrom(pending_service_);
344 }
mukesh agrawal32399322011-09-01 10:53:43 -0700345
Paul Stewart835934a2012-12-06 19:27:09 -0800346 Error unused_error;
347 network_path = FindNetworkRpcidForService(service, &unused_error);
348 if (network_path.empty()) {
349 try {
350 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700351 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800352 append_uint32(scan_ssid);
353 AppendBgscan(service, &service_params);
354 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
355 CHECK(!network_path.empty()); // No DBus path should be empty.
356 rpcid_by_service_[service] = network_path;
357 } catch (const DBus::Error &e) { // NOLINT
358 LOG(ERROR) << "exception while adding network: " << e.what();
359 return;
360 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700361 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700362
Paul Stewarta47c3c62012-12-18 12:14:29 -0800363 if (service->HasRecentConnectionIssues()) {
364 SetConnectionDebugging(true);
365 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700366 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700367 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000368 CHECK(current_service_.get() != pending_service_.get());
369
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700370 // SelectService here (instead of in LinkEvent, like Ethernet), so
371 // that, if we fail to bring up L2, we can attribute failure correctly.
372 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000373 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700374 // reconsider if this is the right place to change the selected service.
375 // see discussion in crosbug.com/20191.
376 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000377}
378
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000379void WiFi::DisconnectFrom(WiFiService *service) {
380 if (service != current_service_ && service != pending_service_) {
381 // TODO(quiche): Once we have asynchronous reply support, we should
382 // generate a D-Bus error here. (crosbug.com/23832)
383 LOG(WARNING) << "In " << __func__ << "(): "
384 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100385 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000386 << " which is neither current nor pending";
387 return;
388 }
389
390 if (pending_service_ && service != pending_service_) {
391 // TODO(quiche): Once we have asynchronous reply support, we should
392 // generate a D-Bus error here. (crosbug.com/23832)
393 LOG(WARNING) << "In " << __func__ << "(): "
394 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100395 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000396 << " which is not the pending service.";
397 return;
398 }
399
400 if (!pending_service_ && service != current_service_) {
401 // TODO(quiche): Once we have asynchronous reply support, we should
402 // generate a D-Bus error here. (crosbug.com/23832)
403 LOG(WARNING) << "In " << __func__ << "(): "
404 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100405 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000406 << " which is not the current service.";
407 return;
408 }
409
Paul Stewartff96a842012-08-13 15:59:10 -0700410 if (pending_service_) {
411 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
412 // on this to drive the service state back to idle. Do that here.
413 pending_service_->SetState(Service::kStateIdle);
414 }
415
Paul Stewart2b05e622012-07-13 20:38:44 -0700416 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700417 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700418
419 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700420 LOG(ERROR) << "In " << __func__ << "(): "
421 << "wpa_supplicant is not present; silently resetting "
422 << "current_service_.";
423 if (current_service_ == selected_service()) {
424 DropConnection();
425 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700426 current_service_ = NULL;
427 return;
428 }
429
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000430 try {
431 supplicant_interface_proxy_->Disconnect();
432 // We'll call RemoveNetwork and reset |current_service_| after
433 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700434 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000435 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700436 // So effect changes immediately. For instance, this can happen when
437 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800438 Error unused_error;
439 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700440 if (service == selected_service()) {
441 DropConnection();
442 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000443 current_service_ = NULL;
444 }
445
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800446 CHECK(current_service_ == NULL ||
447 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000448}
449
Paul Stewart835934a2012-12-06 19:27:09 -0800450bool WiFi::DisableNetwork(const ::DBus::Path &network) {
451 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
452 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700453 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800454 try {
455 supplicant_network_proxy->SetEnabled(false);
456 } catch (const DBus::Error &e) { // NOLINT
457 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
458 return false;
459 }
460 return true;
461}
462
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700463bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
464 try {
465 supplicant_interface_proxy_->RemoveNetwork(network);
466 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700467 // RemoveNetwork can fail with three different errors.
468 //
469 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
470 // already removed the network object, so return true as if
471 // RemoveNetwork removes the network object successfully.
472 //
473 // As shill always passes a valid network object path, RemoveNetwork
474 // should not fail with an InvalidArgs error. Return false in such case
475 // as something weird may have happened. Similarly, return false in case
476 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700477 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700478 return false;
479 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700480 }
481 return true;
482}
483
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000484bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800485 return !current_service_ && !pending_service_;
486}
487
Paul Stewart835934a2012-12-06 19:27:09 -0800488void WiFi::ClearCachedCredentials(const WiFiService *service) {
489 Error unused_error;
490 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800491}
492
Paul Stewart3c504012013-01-17 17:49:58 -0800493void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700494 provider_->OnEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800495}
496
Darin Petkov4a66cc52012-06-15 10:08:29 +0200497void WiFi::AppendBgscan(WiFiService *service,
498 map<string, DBus::Variant> *service_params) const {
499 int scan_interval = kBackgroundScanIntervalSeconds;
500 string method = bgscan_method_;
501 if (method.empty()) {
502 // If multiple APs are detected for this SSID, configure the default method.
503 // Otherwise, disable background scanning completely.
504 if (service->GetEndpointCount() > 1) {
505 method = kDefaultBgscanMethod;
506 } else {
507 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
508 return;
509 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700510 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700511 LOG(INFO) << "Background scan disabled -- chose None method.";
512 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200513 } else {
514 // If the background scan method was explicitly specified, honor the
515 // configured background scan interval.
516 scan_interval = scan_interval_seconds_;
517 }
518 DCHECK(!method.empty());
519 string config_string = StringPrintf("%s:%d:%d:%d",
520 method.c_str(),
521 bgscan_short_interval_seconds_,
522 bgscan_signal_threshold_dbm_,
523 scan_interval);
524 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700525 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200526 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800527}
528
Darin Petkov4a66cc52012-06-15 10:08:29 +0200529string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
530 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
531}
532
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700533bool WiFi::SetBgscanMethod(
Darin Petkov4a66cc52012-06-15 10:08:29 +0200534 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700535 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
536 method != WPASupplicant::kNetworkBgscanMethodLearn &&
537 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800538 const string error_message =
539 StringPrintf("Unrecognized bgscan method %s", method.c_str());
540 LOG(WARNING) << error_message;
541 error->Populate(Error::kInvalidArguments, error_message);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700542 return false;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800543 }
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700544 if (bgscan_method_ == method) {
545 return false;
546 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800547 bgscan_method_ = method;
548 // We do not update kNetworkPropertyBgscan for |pending_service_| or
549 // |current_service_|, because supplicant does not allow for
550 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700551 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800552}
553
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700554bool WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
555 if (bgscan_short_interval_seconds_ == seconds) {
556 return false;
557 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800558 bgscan_short_interval_seconds_ = seconds;
559 // We do not update kNetworkPropertyBgscan for |pending_service_| or
560 // |current_service_|, because supplicant does not allow for
561 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700562 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800563}
564
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700565bool WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
566 if (bgscan_signal_threshold_dbm_ == dbm) {
567 return false;
568 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800569 bgscan_signal_threshold_dbm_ = dbm;
570 // We do not update kNetworkPropertyBgscan for |pending_service_| or
571 // |current_service_|, because supplicant does not allow for
572 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700573 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800574}
575
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700576bool WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
577 if (scan_interval_seconds_ == seconds) {
578 return false;
579 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800580 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700581 if (running()) {
582 StartScanTimer();
583 }
584 // The scan interval affects both foreground scans (handled by
585 // |scan_timer_callback_|), and background scans (handled by
586 // supplicant). However, we do not update |pending_service_| or
587 // |current_service_|, because supplicant does not allow for
588 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700589 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800590}
591
Darin Petkov4a66cc52012-06-15 10:08:29 +0200592void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
593 bgscan_method_.clear();
594}
595
mukesh agrawal15908392011-11-16 18:29:25 +0000596void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700597 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
598 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000599 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700600 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700601
602 // Any change in CurrentBSS means supplicant is actively changing our
603 // connectivity. We no longer need to track any previously pending
604 // reconnect.
605 StopReconnectTimer();
606
Paul Stewart0654ece2013-03-26 15:21:26 -0700607 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000608 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800609 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700610 // Before disconnecting, wpa_supplicant probably scanned for
611 // APs. So, in the normal case, we defer to the timer for the next scan.
612 //
613 // However, in the case of hidden SSIDs, supplicant knows about
614 // at most one of them. (That would be the hidden SSID we were
615 // connected to, if applicable.)
616 //
617 // So, in this case, we initiate an immediate scan. This scan
618 // will include the hidden SSIDs we know about (up to the limit of
619 // kScanMAxSSIDsPerScan).
620 //
621 // We may want to reconsider this immediate scan, if/when shill
622 // takes greater responsibility for scanning (vs. letting
623 // supplicant handle most of it).
Wade Guthrie68d41092013-04-02 12:56:02 -0700624 Scan(kProgressiveScan, NULL);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700625 }
mukesh agrawal15908392011-11-16 18:29:25 +0000626 } else {
627 HandleRoam(new_bss);
628 }
629
Paul Stewart735eab52013-03-29 09:19:23 -0700630 // Reset the EAP handler only after calling HandleDisconnect() above
631 // so our EAP state could be used to detect a failed authentication.
632 eap_state_handler_->Reset();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800633
Paul Stewart2b05e622012-07-13 20:38:44 -0700634 // If we are selecting a new service, or if we're clearing selection
635 // of a something other than the pending service, call SelectService.
636 // Otherwise skip SelectService, since this will cause the pending
637 // service to be marked as Idle.
638 if (current_service_ || selected_service() != pending_service_) {
639 SelectService(current_service_);
640 }
641
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000642 // Invariant check: a Service can either be current, or pending, but
643 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000644 CHECK(current_service_.get() != pending_service_.get() ||
645 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800646
647 // If we are no longer debugging a problematic WiFi connection, return
648 // to the debugging level indicated by the WiFi debugging scope.
649 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
650 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
651 SetConnectionDebugging(false);
652 }
mukesh agrawal15908392011-11-16 18:29:25 +0000653}
654
655void WiFi::HandleDisconnect() {
656 // Identify the affected service. We expect to get a disconnect
657 // event when we fall off a Service that we were connected
658 // to. However, we also allow for the case where we get a disconnect
659 // event while attempting to connect from a disconnected state.
660 WiFiService *affected_service =
661 current_service_.get() ? current_service_.get() : pending_service_.get();
662
663 current_service_ = NULL;
664 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700665 SLOG(WiFi, 2) << "WiFi " << link_name()
666 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000667 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700668 }
669 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700670 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700671 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000672 }
673
Paul Stewart835934a2012-12-06 19:27:09 -0800674 Error error;
675 if (!DisableNetworkForService(affected_service, &error)) {
676 if (error.type() == Error::kNotFound) {
677 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100678 << " (or failed to connect to) service "
679 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800680 << "but could not find supplicant network to disable.";
681 } else {
682 LOG(FATAL) << "DisableNetwork failed.";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700683 }
mukesh agrawal15908392011-11-16 18:29:25 +0000684 }
685
Ben Chanfad4a0b2012-04-18 15:49:59 -0700686 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100687 << " (or failed to connect to) service "
688 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800689 Service::ConnectFailure failure;
690 if (SuspectCredentials(*affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700691 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700692 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800693 affected_service->SetFailure(failure);
694 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
695 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700696 } else {
697 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700698 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800699 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000700 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000701
702 if (affected_service == pending_service_.get()) {
703 // The attempt to connect to |pending_service_| failed. Clear
704 // |pending_service_|, to indicate we're no longer in the middle
705 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700706 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000707 } else if (pending_service_.get()) {
708 // We've attributed the disconnection to what was the
709 // |current_service_|, rather than the |pending_service_|.
710 //
711 // If we're wrong about that (i.e. supplicant reported this
712 // CurrentBSS change after attempting to connect to
713 // |pending_service_|), we're depending on supplicant to retry
714 // connecting to |pending_service_|, and delivering another
715 // CurrentBSS change signal in the future.
716 //
717 // Log this fact, to help us debug (in case our assumptions are
718 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100719 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
720 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700721 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000722 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700723
724 // If we disconnect, initially scan at a faster frequency, to make sure
725 // we've found all available APs.
726 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000727}
728
729// We use the term "Roam" loosely. In particular, we include the case
730// where we "Roam" to a BSS from the disconnected state.
731void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
732 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
733 if (endpoint_it == endpoint_by_rpcid_.end()) {
734 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
735 << new_bss;
736 return;
737 }
738
Paul Stewart3c504012013-01-17 17:49:58 -0800739 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
740 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000741 if (!service.get()) {
742 LOG(WARNING) << "WiFi " << link_name()
743 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800744 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000745 << " (service will be unchanged)";
746 return;
747 }
748
Ben Chanfad4a0b2012-04-18 15:49:59 -0700749 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800750 << " roamed to Endpoint " << endpoint->bssid_string()
751 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000752
Paul Stewart3c504012013-01-17 17:49:58 -0800753 service->NotifyCurrentEndpoint(endpoint);
Wade Guthrie60a37062013-04-02 11:39:09 -0700754 provider_->IncrementConnectCount(endpoint->frequency());
Thieu Lee41a72d2012-02-06 20:46:51 +0000755
mukesh agrawal15908392011-11-16 18:29:25 +0000756 if (pending_service_.get() &&
757 service.get() != pending_service_.get()) {
758 // The Service we've roamed on to is not the one we asked for.
759 // We assume that this is transient, and that wpa_supplicant
760 // is trying / will try to connect to |pending_service_|.
761 //
762 // If it succeeds, we'll end up back here, but with |service|
763 // pointing at the same service as |pending_service_|.
764 //
765 // If it fails, we'll process things in HandleDisconnect.
766 //
767 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700768 SLOG(WiFi, 2) << "WiFi " << link_name()
769 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800770 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700771 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100772 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000773
774 // Sanity check: if we didn't roam onto |pending_service_|, we
775 // should still be on |current_service_|.
776 if (service.get() != current_service_.get()) {
777 LOG(WARNING) << "WiFi " << link_name()
778 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800779 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000780 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100781 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000782 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100783 << (current_service_ ?
784 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000785 "(NULL)");
786 // Although we didn't expect to get here, we should keep
787 // |current_service_| in sync with what supplicant has done.
788 current_service_ = service;
789 }
790 return;
791 }
792
793 if (pending_service_.get()) {
794 // We assume service.get() == pending_service_.get() here, because
795 // of the return in the previous if clause.
796 //
797 // Boring case: we've connected to the service we asked
798 // for. Simply update |current_service_| and |pending_service_|.
799 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700800 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000801 return;
802 }
803
804 // |pending_service_| was NULL, so we weren't attempting to connect
805 // to a new Service. Sanity check that we're still on
806 // |current_service_|.
807 if (service.get() != current_service_.get()) {
808 LOG(WARNING)
809 << "WiFi " << link_name()
810 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800811 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000812 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000813 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100814 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000815 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000816 // We didn't expect to be here, but let's cope as well as we
817 // can. Update |current_service_| to keep it in sync with
818 // supplicant.
819 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700820
821 // If this service isn't already marked as actively connecting (likely,
822 // since this service is a bit of a surprise) set the service as
823 // associating.
824 if (!current_service_->IsConnecting()) {
825 current_service_->SetState(Service::kStateAssociating);
826 }
827
mukesh agrawal15908392011-11-16 18:29:25 +0000828 return;
829 }
830
831 // At this point, we know that |pending_service_| was NULL, and that
832 // we're still on |current_service_|. This is the most boring case
833 // of all, because there's no state to update here.
834 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700835}
836
Paul Stewart835934a2012-12-06 19:27:09 -0800837string WiFi::FindNetworkRpcidForService(
838 const WiFiService *service, Error *error) {
839 ReverseServiceMap::const_iterator rpcid_it =
840 rpcid_by_service_.find(service);
841 if (rpcid_it == rpcid_by_service_.end()) {
842 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100843 StringPrintf(
844 "WiFi %s cannot find supplicant network rpcid for service %s",
845 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800846 // There are contexts where this is not an error, such as when a service
847 // is clearing whatever cached credentials may not exist.
848 SLOG(WiFi, 2) << error_message;
849 if (error) {
850 error->Populate(Error::kNotFound, error_message);
851 }
852 return "";
853 }
854
855 return rpcid_it->second;
856}
857
858bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
859 string rpcid = FindNetworkRpcidForService(service, error);
860 if (rpcid.empty()) {
861 // Error is already populated.
862 return false;
863 }
864
865 if (!DisableNetwork(rpcid)) {
866 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100867 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800868 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100869 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800870 rpcid.c_str());
871 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
872
873 // Make sure that such errored networks are removed, so problems do not
874 // propogate to future connection attempts.
875 RemoveNetwork(rpcid);
876 rpcid_by_service_.erase(service);
877
878 return false;
879 }
880
881 return true;
882}
883
884bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
885 string rpcid = FindNetworkRpcidForService(service, error);
886 if (rpcid.empty()) {
887 // Error is already populated.
888 return false;
889 }
890
891 // Erase the rpcid from our tables regardless of failure below, since even
892 // if in failure, we never want to use this network again.
893 rpcid_by_service_.erase(service);
894
895 // TODO(quiche): Reconsider giving up immediately. Maybe give
896 // wpa_supplicant some time to retry, first.
897 if (!RemoveNetwork(rpcid)) {
898 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100899 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800900 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100901 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800902 rpcid.c_str());
903 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
904 return false;
905 }
906
907 return true;
908}
909
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000910void WiFi::BSSAddedTask(
911 const ::DBus::Path &path,
912 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000913 // Note: we assume that BSSIDs are unique across endpoints. This
914 // means that if an AP reuses the same BSSID for multiple SSIDs, we
915 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800916 WiFiEndpointRefPtr endpoint(
917 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -0800918 SLOG(WiFi, 1) << "Found endpoint. "
919 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +0100920 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -0800921 << "bssid: " << endpoint->bssid_string() << ", "
922 << "signal: " << endpoint->signal_strength() << ", "
923 << "security: " << endpoint->security_mode() << ", "
924 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000925
926 if (endpoint->ssid_string().empty()) {
927 // Don't bother trying to find or create a Service for an Endpoint
928 // without an SSID. We wouldn't be able to connect to it anyway.
929 return;
930 }
931
mukesh agrawalb3857612012-01-18 16:23:29 -0800932 if (endpoint->ssid()[0] == 0) {
933 // Assume that an SSID starting with NULL is bogus/misconfigured,
934 // and filter it out.
935 return;
936 }
937
Paul Stewart3c504012013-01-17 17:49:58 -0800938 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000939
mukesh agrawale9adda12012-02-09 18:33:48 -0800940 // Do this last, to maintain the invariant that any Endpoint we
941 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800942 //
943 // TODO(quiche): Write test to verify correct behavior in the case
944 // where we get multiple BSSAdded events for a single endpoint.
945 // (Old Endpoint's refcount should fall to zero, and old Endpoint
946 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -0800947 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -0800948 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000949}
950
951void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
952 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
953 if (i == endpoint_by_rpcid_.end()) {
954 LOG(WARNING) << "WiFi " << link_name()
955 << " could not find BSS " << path
956 << " to remove.";
957 return;
958 }
959
960 WiFiEndpointRefPtr endpoint = i->second;
961 CHECK(endpoint);
962 endpoint_by_rpcid_.erase(i);
963
Paul Stewart3c504012013-01-17 17:49:58 -0800964 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
965 if (!service) {
966 return;
967 }
968 Error unused_error;
969 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000970
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000971 bool disconnect_service = !service->HasEndpoints() &&
972 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000973
974 if (disconnect_service) {
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700975 LOG(INFO) << "Disconnecting from service " << service->unique_name()
976 << ": BSSRemoved";
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000977 DisconnectFrom(service);
978 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000979}
980
Paul Stewartbc6e7392012-05-24 07:07:48 -0700981void WiFi::CertificationTask(
982 const map<string, ::DBus::Variant> &properties) {
983 if (!current_service_) {
984 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
985 << " with no current service.";
986 return;
987 }
988
Paul Stewart735eab52013-03-29 09:19:23 -0700989 string subject;
990 uint32 depth;
991 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
992 current_service_->AddEAPCertification(subject, depth);
Paul Stewartbc6e7392012-05-24 07:07:48 -0700993 }
Paul Stewartbc6e7392012-05-24 07:07:48 -0700994}
995
Paul Stewartdb0f9172012-11-30 16:48:09 -0800996void WiFi::EAPEventTask(const string &status, const string &parameter) {
Paul Stewartdb0f9172012-11-30 16:48:09 -0800997 if (!current_service_) {
998 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
999 << " with no current service.";
1000 return;
1001 }
Paul Stewart735eab52013-03-29 09:19:23 -07001002 Service::ConnectFailure failure = Service::kFailureUnknown;
1003 eap_state_handler_->ParseStatus(status, parameter, &failure);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001004 if (failure != Service::kFailureUnknown) {
Paul Stewart735eab52013-03-29 09:19:23 -07001005 // Avoid a reporting failure twice by resetting EAP state handler early.
1006 eap_state_handler_->Reset();
mukesh agrawald4dc0832013-03-25 14:38:26 -07001007 Error unused_error;
mukesh agrawald4dc0832013-03-25 14:38:26 -07001008 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001009 }
1010}
1011
mukesh agrawal15908392011-11-16 18:29:25 +00001012void WiFi::PropertiesChangedTask(
1013 const map<string, ::DBus::Variant> &properties) {
1014 // TODO(quiche): Handle changes in other properties (e.g. signal
1015 // strength).
1016
1017 // Note that order matters here. In particular, we want to process
1018 // changes in the current BSS before changes in state. This is so
1019 // that we update the state of the correct Endpoint/Service.
1020
1021 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -07001022 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +00001023 if (properties_it != properties.end()) {
1024 CurrentBSSChanged(properties_it->second.reader().get_path());
1025 }
1026
Paul Stewart0654ece2013-03-26 15:21:26 -07001027 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +00001028 if (properties_it != properties.end()) {
1029 StateChanged(properties_it->second.reader().get_string());
1030 }
1031}
1032
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001033void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001034 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001035
1036 if (scan_session_) {
1037 // Post |ProgressiveScanTask| so it runs after any |BSSAddedTask|s that have
1038 // been posted. This allows connections on new BSSes to be started before
1039 // we decide whether to abort the progressive scan or continue scanning.
1040 dispatcher()->PostTask(
1041 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
1042 } else {
1043 metrics()->NotifyDeviceScanFinished(interface_index());
1044 }
mukesh agrawal5c05b292012-03-07 10:12:52 -08001045 if (need_bss_flush_) {
1046 CHECK(supplicant_interface_proxy_ != NULL);
1047 // Compute |max_age| relative to |resumed_at_|, to account for the
1048 // time taken to scan.
1049 struct timeval now;
1050 uint32_t max_age;
1051 time_->GetTimeMonotonic(&now);
1052 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1053 supplicant_interface_proxy_->FlushBSS(max_age);
1054 need_bss_flush_ = false;
1055 }
Paul Stewartd2db2b12013-01-17 13:11:07 -08001056 SetScanPending(false);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001057 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001058}
1059
mukesh agrawal32399322011-09-01 10:53:43 -07001060void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001061 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001062 if (!enabled()) {
1063 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
1064 return;
1065 }
1066 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1067 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
1068 return;
1069 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001070 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1071 (current_service_.get() && current_service_->IsConnecting())) {
1072 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1073 return;
1074 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001075 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001076 scan_args[WPASupplicant::kPropertyScanType].writer().
1077 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001078
Paul Stewart3c504012013-01-17 17:49:58 -08001079 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -08001080 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -08001081 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1082 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -07001083 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001084 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001085 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001086 hidden_ssids.end());
1087 }
1088 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1089 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1090 // behavior of doing a broadcast probe.
1091 hidden_ssids.push_back(ByteArray());
1092
Paul Stewart0654ece2013-03-26 15:21:26 -07001093 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001094 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1095 }
1096
Gaurav Shah10109f22011-11-11 20:16:22 -08001097 try {
1098 supplicant_interface_proxy_->Scan(scan_args);
Paul Stewartd2db2b12013-01-17 13:11:07 -08001099 SetScanPending(true);
Ben Chan80326f32012-05-04 17:51:32 -07001100 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001101 // A scan may fail if, for example, the wpa_supplicant vanishing
1102 // notification is posted after this task has already started running.
1103 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001104 }
mukesh agrawal32399322011-09-01 10:53:43 -07001105}
1106
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001107void WiFi::ProgressiveScanTask() {
1108 SLOG(WiFi, 2) << __func__ << " - scan requested for " << link_name();
1109 if (!enabled()) {
1110 LOG(INFO) << "Ignoring scan request while device is not enabled.";
1111 metrics()->NotifyDeviceScanFinished(interface_index());
1112 return;
1113 }
1114 if (!scan_session_) {
1115 SLOG(WiFi, 2) << "No scan session -- returning";
1116 metrics()->NotifyDeviceScanFinished(interface_index());
1117 return;
1118 }
1119 if (!IsIdle()) {
1120 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1121 scan_session_.reset();
1122 metrics()->NotifyDeviceScanFinished(interface_index());
1123 return;
1124 }
1125 if (scan_session_->HasMoreFrequencies()) {
1126 SLOG(WiFi, 2) << "Initiating a scan -- returning";
1127 SetScanPending(true);
1128 // After us initiating a scan, supplicant will gather the scan results and
1129 // send us zero or more |BSSAdded| events followed by a |ScanDone|.
1130 scan_session_->InitiateScan();
1131 return;
1132 }
1133 LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
1134 << "do a regular scan";
1135 scan_session_.reset();
1136 Scan(kFullScan, NULL);
1137}
1138
1139void WiFi::OnFailedProgressiveScan() {
1140 LOG(ERROR) << "Couldn't issue a scan on " << link_name()
1141 << " -- doing a regular scan";
1142 scan_session_.reset();
1143 Scan(kFullScan, NULL);
1144}
1145
Paul Stewartd2db2b12013-01-17 13:11:07 -08001146void WiFi::SetScanPending(bool pending) {
1147 if (scan_pending_ != pending) {
1148 scan_pending_ = pending;
1149 adaptor()->EmitBoolChanged(flimflam::kScanningProperty, pending);
1150 }
1151}
1152
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001153string WiFi::GetServiceLeaseName(const WiFiService &service) {
1154 return service.GetStorageIdentifier();
1155}
1156
1157void WiFi::DestroyServiceLease(const WiFiService &service) {
1158 DestroyIPConfigLease(GetServiceLeaseName(service));
1159}
1160
mukesh agrawal15908392011-11-16 18:29:25 +00001161void WiFi::StateChanged(const string &new_state) {
1162 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001163 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001164 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1165 << old_state << " -> " << new_state;
1166
1167 WiFiService *affected_service;
1168 // Identify the service to which the state change applies. If
1169 // |pending_service_| is non-NULL, then the state change applies to
1170 // |pending_service_|. Otherwise, it applies to |current_service_|.
1171 //
1172 // This policy is driven by the fact that the |pending_service_|
1173 // doesn't become the |current_service_| until wpa_supplicant
1174 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001175 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001176 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001177 affected_service =
1178 pending_service_.get() ? pending_service_.get() : current_service_.get();
1179 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001180 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1181 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001182 return;
1183 }
1184
Paul Stewart0654ece2013-03-26 15:21:26 -07001185 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001186 if (affected_service->IsConnected()) {
1187 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001188 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001189 } else if (has_already_completed_) {
1190 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001191 } else if (AcquireIPConfigWithLeaseName(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001192 GetServiceLeaseName(*affected_service))) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001193 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001194 affected_service->SetState(Service::kStateConfiguring);
1195 } else {
1196 LOG(ERROR) << "Unable to acquire DHCP config.";
1197 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001198 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001199 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001200 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001201 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1202 new_state == WPASupplicant::kInterfaceStateAssociating ||
1203 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1204 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001205 // Ignore transitions into these states from Completed, to avoid
1206 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001207 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001208 affected_service->SetState(Service::kStateAssociating);
1209 // TOOD(quiche): On backwards transitions, we should probably set
1210 // a timeout for getting back into the completed state. At present,
1211 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001212 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001213 // (crosbug.com/23207)
Paul Stewart0654ece2013-03-26 15:21:26 -07001214 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001215 affected_service == current_service_ &&
1216 affected_service->IsConnected()) {
1217 // This means that wpa_supplicant failed in a re-connect attempt, but
1218 // may still be reconnecting. Give wpa_supplicant a limited amount of
1219 // time to transition out this condition by either connecting or changing
1220 // CurrentBSS.
1221 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001222 } else {
1223 // Other transitions do not affect Service state.
1224 //
1225 // Note in particular that we ignore a State change into
1226 // kInterfaceStateDisconnected, in favor of observing the corresponding
1227 // change in CurrentBSS.
1228 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001229}
1230
Paul Stewart1369c2b2013-01-11 05:41:26 -08001231bool WiFi::SuspectCredentials(
1232 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001233 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001234 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewart1369c2b2013-01-11 05:41:26 -08001235 !service.has_ever_connected()) {
1236 if (failure) {
1237 *failure = Service::kFailureBadPassphrase;
1238 }
1239 return true;
1240 }
1241 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
Paul Stewart735eab52013-03-29 09:19:23 -07001242 if (eap_state_handler_->is_eap_in_progress() &&
1243 !service.has_ever_connected()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001244 if (failure) {
1245 *failure = Service::kFailureEAPAuthentication;
1246 }
1247 return true;
1248 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001249 }
1250
Paul Stewart1369c2b2013-01-11 05:41:26 -08001251 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001252}
1253
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001254// static
1255bool WiFi::SanitizeSSID(string *ssid) {
1256 CHECK(ssid);
1257
1258 size_t ssid_len = ssid->length();
1259 size_t i;
1260 bool changed = false;
1261
Gary Morainac1bdb42012-02-16 17:42:29 -08001262 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001263 if (!g_ascii_isprint((*ssid)[i])) {
1264 (*ssid)[i] = '?';
1265 changed = true;
1266 }
1267 }
1268
1269 return changed;
1270}
1271
Darin Petkov50cb78a2013-02-06 16:17:49 +01001272// static
1273string WiFi::LogSSID(const string &ssid) {
1274 string out;
1275 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1276 // Replace '[' and ']' (in addition to non-printable characters) so that
1277 // it's easy to match the right substring through a non-greedy regex.
1278 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1279 base::StringAppendF(&out, "\\x%02x", *it);
1280 } else {
1281 out += *it;
1282 }
1283 }
1284 return StringPrintf("[SSID=%s]", out.c_str());
1285}
1286
Paul Stewart3c508e12012-08-09 11:40:06 -07001287void WiFi::OnLinkMonitorFailure() {
1288 // If we have never found the gateway, let's be conservative and not
1289 // do anything, in case this network topology does not have a gateway.
1290 if (!link_monitor()->IsGatewayFound()) {
1291 LOG(INFO) << "In " << __func__ << "(): "
1292 << "Skipping reassociate since gateway was never found.";
1293 return;
1294 }
1295
1296 if (!supplicant_present_) {
1297 LOG(ERROR) << "In " << __func__ << "(): "
1298 << "wpa_supplicant is not present. Cannot reassociate.";
1299 return;
1300 }
1301
1302 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001303 // This will force a transition out of connected, if we are actually
1304 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001305 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001306 // If we don't eventually get a transition back into a connected state,
1307 // there is something wrong.
1308 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001309 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1310 } catch (const DBus::Error &e) { // NOLINT
1311 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1312 return;
1313 }
1314}
1315
Arman Ugurayed8e6102012-11-29 14:47:20 -08001316bool WiFi::ShouldUseArpGateway() const {
1317 return true;
1318}
1319
Paul Stewart3c504012013-01-17 17:49:58 -08001320void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1321 DisconnectFrom(service);
1322 if (service == selected_service()) {
1323 DropConnection();
1324 }
1325 Error unused_error;
1326 RemoveNetworkForService(service, &unused_error);
1327}
1328
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001329vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1330 vector<GeolocationInfo> objects;
1331 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1332 it != endpoint_by_rpcid_.end();
1333 ++it) {
1334 GeolocationInfo geoinfo;
1335 WiFiEndpointRefPtr endpoint = it->second;
1336 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1337 geoinfo.AddField(kGeoSignalStrengthProperty,
1338 StringPrintf("%d", endpoint->signal_strength()));
1339 geoinfo.AddField(
1340 kGeoChannelProperty,
1341 StringPrintf("%d",
1342 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1343 // TODO(gauravsh): Include age field. crosbug.com/35445
1344 objects.push_back(geoinfo);
1345 }
1346 return objects;
1347}
1348
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001349void WiFi::HelpRegisterDerivedInt32(
1350 PropertyStore *store,
1351 const string &name,
1352 int32(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001353 bool(WiFi::*set)(const int32 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001354 store->RegisterDerivedInt32(
1355 name,
1356 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1357}
1358
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001359void WiFi::HelpRegisterDerivedUint16(
1360 PropertyStore *store,
1361 const string &name,
1362 uint16(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001363 bool(WiFi::*set)(const uint16 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001364 store->RegisterDerivedUint16(
1365 name,
1366 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1367}
1368
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001369void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001370 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001371 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001372
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001373 // We want to flush the BSS cache, but we don't want to conflict
1374 // with a running scan or an active connection attempt. So record
1375 // the need to flush, and take care of flushing when the next scan
1376 // completes.
1377 //
1378 // Note that supplicant will automatically expire old cache
1379 // entries (after, e.g., a BSS is not found in two consecutive
1380 // scans). However, our explicit flush accelerates re-association
1381 // in cases where a BSS disappeared while we were asleep. (See,
1382 // e.g. WiFiRoaming.005SuspendRoam.)
1383 time_->GetTimeMonotonic(&resumed_at_);
1384 need_bss_flush_ = true;
1385
1386 if (!scan_pending_ && IsIdle()) {
1387 // Not scanning/connecting/connected, so let's get things rolling.
Wade Guthrie68d41092013-04-02 12:56:02 -07001388 Scan(kProgressiveScan, NULL);
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001389 } else {
1390 SLOG(WiFi, 1) << __func__
1391 << " skipping scan, already scanning or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001392 }
1393}
1394
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001395void WiFi::OnConnected() {
1396 Device::OnConnected();
1397 EnableHighBitrates();
1398}
1399
Paul Stewarte369ece2012-05-22 09:11:03 -07001400void WiFi::RestartFastScanAttempts() {
1401 fast_scans_remaining_ = kNumFastScanAttempts;
1402 StartScanTimer();
1403}
1404
mukesh agrawalb66c6462012-05-07 11:45:25 -07001405void WiFi::StartScanTimer() {
1406 if (scan_interval_seconds_ == 0) {
1407 StopScanTimer();
1408 return;
1409 }
1410 scan_timer_callback_.Reset(
1411 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001412 // Repeat the first few scans after disconnect relatively quickly so we
1413 // have reasonable trust that no APs we are looking for are present.
1414 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1415 fast_scans_remaining_ > 0 ?
1416 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001417}
1418
1419void WiFi::StopScanTimer() {
1420 scan_timer_callback_.Cancel();
1421}
1422
1423void WiFi::ScanTimerHandler() {
1424 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1425 if (IsIdle() && !scan_pending_) {
Wade Guthrie68d41092013-04-02 12:56:02 -07001426 Scan(kProgressiveScan, NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001427 if (fast_scans_remaining_ > 0) {
1428 --fast_scans_remaining_;
1429 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001430 }
1431 StartScanTimer();
1432}
1433
Paul Stewart2b05e622012-07-13 20:38:44 -07001434void WiFi::StartPendingTimer() {
1435 pending_timeout_callback_.Reset(
1436 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1437 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1438 kPendingTimeoutSeconds * 1000);
1439}
1440
1441void WiFi::StopPendingTimer() {
1442 pending_timeout_callback_.Cancel();
1443}
1444
1445void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001446 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001447 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001448 if (service) {
1449 service->SetState(Service::kStateAssociating);
1450 StartPendingTimer();
1451 } else if (pending_service_) {
1452 StopPendingTimer();
1453 }
1454 pending_service_ = service;
1455}
1456
1457void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001458 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001459 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1460 CHECK(pending_service_);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001461 pending_service_->DisconnectWithFailure(
1462 Service::kFailureOutOfRange, &unused_error);
Paul Stewart17d90652013-04-04 15:09:11 -07001463
1464 // A hidden service may have no endpoints, since wpa_supplicant
1465 // failed to attain a CurrentBSS. If so, the service has no
1466 // reference to |this| device and cannot call WiFi::DisconnectFrom()
1467 // to reset pending_service_. In this case, we must perform the
1468 // disconnect here ourselves.
1469 if (pending_service_) {
1470 CHECK(!pending_service_->HasEndpoints());
1471 LOG(INFO) << "Hidden service was not found.";
1472 DisconnectFrom(pending_service_);
1473 }
Paul Stewart2b05e622012-07-13 20:38:44 -07001474}
1475
Paul Stewart44663922012-07-30 11:03:03 -07001476void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001477 if (!reconnect_timeout_callback_.IsCancelled()) {
1478 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1479 << ": reconnect timer already running.";
1480 return;
1481 }
Paul Stewart44663922012-07-30 11:03:03 -07001482 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1483 reconnect_timeout_callback_.Reset(
1484 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1485 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1486 kReconnectTimeoutSeconds * 1000);
1487}
1488
1489void WiFi::StopReconnectTimer() {
1490 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1491 reconnect_timeout_callback_.Cancel();
1492}
1493
1494void WiFi::ReconnectTimeoutHandler() {
1495 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001496 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001497 CHECK(current_service_);
1498 current_service_->SetFailureSilent(Service::kFailureConnect);
1499 DisconnectFrom(current_service_);
1500}
1501
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001502void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1503 LOG(INFO) << "WPA supplicant appeared.";
1504 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001505 // Restart the WiFi device if it's started already. This will reset the
1506 // state and connect the device to the new WPA supplicant instance.
1507 if (enabled()) {
1508 Restart();
1509 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001510 return;
1511 }
1512 supplicant_present_ = true;
1513 ConnectToSupplicant();
1514}
1515
1516void WiFi::OnSupplicantVanish() {
1517 LOG(INFO) << "WPA supplicant vanished.";
1518 if (!supplicant_present_) {
1519 return;
1520 }
1521 supplicant_present_ = false;
1522 // Restart the WiFi device if it's started already. This will effectively
1523 // suspend the device until the WPA supplicant reappears.
1524 if (enabled()) {
1525 Restart();
1526 }
1527}
1528
Paul Stewart5581d072012-12-17 17:30:20 -08001529void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1530 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1531 if (!supplicant_process_proxy_.get()) {
1532 SLOG(WiFi, 2) << "Suplicant process proxy not present.";
1533 return;
1534 }
1535 string current_level;
1536 try {
1537 current_level = supplicant_process_proxy_->GetDebugLevel();
1538 } catch (const DBus::Error &e) { // NOLINT
1539 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1540 return;
1541 }
1542
Paul Stewart0654ece2013-03-26 15:21:26 -07001543 if (current_level != WPASupplicant::kDebugLevelInfo &&
1544 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001545 SLOG(WiFi, 2) << "WiFi debug level is currently "
1546 << current_level
1547 << "; assuming that it is being controlled elsewhere.";
1548 return;
1549 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001550 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1551 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001552
1553 if (new_level == current_level) {
1554 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1555 << current_level;
1556 return;
1557 }
1558
1559 try {
1560 supplicant_process_proxy_->SetDebugLevel(new_level);
1561 } catch (const DBus::Error &e) { // NOLINT
1562 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1563 }
1564}
1565
Paul Stewarta47c3c62012-12-18 12:14:29 -08001566void WiFi::SetConnectionDebugging(bool enabled) {
1567 if (is_debugging_connection_ == enabled) {
1568 return;
1569 }
1570 OnWiFiDebugScopeChanged(
1571 enabled ||
1572 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1573 is_debugging_connection_ = enabled;
1574}
1575
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001576void WiFi::ConnectToSupplicant() {
1577 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1578 << " supplicant: "
1579 << (supplicant_present_ ? "present" : "absent")
1580 << " proxy: "
1581 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1582 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1583 return;
1584 }
1585 supplicant_process_proxy_.reset(
1586 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001587 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001588 OnWiFiDebugScopeChanged(
1589 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001590 ::DBus::Path interface_path;
1591 try {
1592 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001593 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001594 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001595 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1596 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001597 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001598 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001599 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001600 interface_path =
1601 supplicant_process_proxy_->CreateInterface(create_interface_args);
1602 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001603 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001604 interface_path =
1605 supplicant_process_proxy_->GetInterface(link_name());
1606 // TODO(quiche): Is it okay to crash here, if device is missing?
1607 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001608 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1609 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001610 }
1611 }
1612
1613 supplicant_interface_proxy_.reset(
1614 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001615 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001616
1617 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1618 IFF_UP);
1619 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1620
1621 // Clear out any networks that might previously have been configured
1622 // for this interface.
1623 supplicant_interface_proxy_->RemoveAllNetworks();
1624
1625 // Flush interface's BSS cache, so that we get BSSAdded signals for
1626 // all BSSes (not just new ones since the last scan).
1627 supplicant_interface_proxy_->FlushBSS(0);
1628
1629 try {
1630 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1631 // with RADIUS servers that respond strangely to such requests.
1632 // crosbug.com/25630
1633 supplicant_interface_proxy_->SetFastReauth(false);
1634 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001635 LOG(ERROR) << "Failed to disable fast_reauth. "
1636 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001637 }
1638
1639 try {
1640 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1641 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1642 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001643 LOG(ERROR) << "Failed to set scan_interval. "
1644 << "May be running an older version of wpa_supplicant.";
1645 }
1646
1647 try {
1648 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1649 } catch (const DBus::Error &e) { // NOLINT
1650 LOG(ERROR) << "Failed to disable high bitrates. "
1651 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001652 }
1653
Wade Guthrie68d41092013-04-02 12:56:02 -07001654 Scan(kProgressiveScan, NULL);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001655 StartScanTimer();
1656}
1657
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001658void WiFi::EnableHighBitrates() {
1659 LOG(INFO) << "Enabling high bitrates.";
1660 try {
1661 supplicant_interface_proxy_->EnableHighBitrates();
1662 } catch (const DBus::Error &e) { // NOLINT
1663 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1664 }
1665}
1666
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001667void WiFi::Restart() {
1668 LOG(INFO) << link_name() << " restarting.";
1669 WiFiRefPtr me = this; // Make sure we don't get destructed.
1670 // Go through the manager rather than starting and stopping the device
1671 // directly so that the device can be configured with the profile.
1672 manager()->DeregisterDevice(me);
1673 manager()->RegisterDevice(me);
1674}
1675
Wade Guthrie92d06362013-04-25 15:41:30 -07001676void WiFi::ConfigureScanFrequencies() {
1677 GetWiphyMessage get_wiphy;
1678 get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
1679 interface_index());
1680 netlink_manager_->SendMessage(&get_wiphy,
1681 Bind(&WiFi::OnNewWiphy,
1682 weak_ptr_factory_.GetWeakPtr()));
1683}
1684
1685void WiFi::OnNewWiphy(const NetlinkMessage &netlink_message) {
1686 // Note that we don't fail fatally from this routine because, while it
1687 // provides frequencies for a progressive scan, a failed progressive scan is
1688 // followed by a full scan (which doesn't use the frequency list provided by
1689 // this call).
1690 if (netlink_message.message_type() == ErrorAckMessage::kMessageType) {
1691 const ErrorAckMessage *error_ack_message =
1692 reinterpret_cast<const ErrorAckMessage *>(&netlink_message);
1693 if (error_ack_message->error()) {
1694 LOG(ERROR) << __func__ << ": Message (seq: "
1695 << netlink_message.sequence_number() << ") failed: "
1696 << error_ack_message->ToString();
1697 } else {
1698 SLOG(WiFi, 6) << __func__ << ": Message (seq: "
1699 << netlink_message.sequence_number() << ") ACKed";
1700 }
1701 return;
1702 }
1703
1704 // We only handle a special set of messages, all of which are nl80211
1705 // messages.
1706 if (netlink_message.message_type() != Nl80211Message::GetMessageType()) {
1707 LOG(ERROR) << "Received unexpected message type: "
1708 << netlink_message.message_type();
1709 return;
1710 }
1711
1712 const Nl80211Message *nl80211_message =
1713 dynamic_cast<const Nl80211Message *>(&netlink_message);
1714 // Verify NL80211_CMD_NEW_WIPHY
1715 if (nl80211_message->command() != NewWiphyMessage::kCommand) {
1716 LOG(ERROR) << "Received unexpected command:"
1717 << nl80211_message->command();
1718 return;
1719 }
1720
1721 // The attributes, for this message, are complicated.
1722 // NL80211_ATTR_BANDS contains an array of bands...
1723 AttributeListConstRefPtr wiphy_bands;
1724 if (!nl80211_message->const_attributes()->ConstGetNestedAttributeList(
1725 NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
1726 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
1727 return;
1728 }
1729
1730 AttributeIdIterator band_iter(*wiphy_bands);
1731 for (; !band_iter.AtEnd(); band_iter.Advance()) {
1732 AttributeListConstRefPtr wiphy_band;
1733 if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
1734 &wiphy_band)) {
1735 LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
1736 continue;
1737 }
1738
1739 // ...Each band has a FREQS attribute...
1740 AttributeListConstRefPtr frequencies;
1741 if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
1742 &frequencies)) {
1743 LOG(ERROR) << "BAND " << band_iter.GetId()
1744 << " had no 'frequencies' attribute";
1745 continue;
1746 }
1747
1748 // ...And each FREQS attribute contains an array of information about the
1749 // frequency...
1750 AttributeIdIterator freq_iter(*frequencies);
1751 for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
1752 AttributeListConstRefPtr frequency;
1753 if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
1754 &frequency)) {
1755 // ...Including the frequency, itself (the part we want).
1756 uint32_t frequency_value = 0;
1757 if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
1758 &frequency_value)) {
1759 SLOG(WiFi, 5) << "Found frequency[" << freq_iter.GetId()
1760 << "] = " << frequency_value;
1761 all_scan_frequencies_.insert(frequency_value);
1762 }
1763 }
1764 }
1765 }
1766}
1767
Paul Stewartb50f0b92011-05-16 16:31:42 -07001768} // namespace shill