blob: 4414d06a65c850d89193ab84a3307db71d565e08 [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"
Wade Guthrie2edd58b2013-05-23 11:16:08 -070030#include "shill/file_reader.h"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070031#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070032#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070033#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070034#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070035#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000036#include "shill/metrics.h"
Wade Guthriebb9fca22013-04-10 17:21:42 -070037#include "shill/netlink_manager.h"
Wade Guthrie7347bf22013-04-30 11:21:51 -070038#include "shill/netlink_message.h"
Wade Guthriebee87c22013-03-06 11:00:46 -080039#include "shill/nl80211_message.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080040#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070041#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050042#include "shill/rtnl_handler.h"
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070043#include "shill/scan_session.h"
Paul Stewart5581d072012-12-17 17:30:20 -080044#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080045#include "shill/shill_time.h"
Paul Stewart735eab52013-03-29 09:19:23 -070046#include "shill/supplicant_eap_state_handler.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070047#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080048#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070049#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080050#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070051#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080052#include "shill/wifi_provider.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070053#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070054#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070055
Eric Shienbrood3e20a232012-02-16 11:35:56 -050056using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000057using base::StringPrintf;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070058using file_util::PathExists;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070059using std::map;
mukesh agrawalab87ea42011-05-18 11:44:49 -070060using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070061using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070062
Paul Stewartb50f0b92011-05-16 16:31:42 -070063namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070064
65// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080066const char *WiFi::kDefaultBgscanMethod =
Paul Stewart0654ece2013-03-26 15:21:26 -070067 WPASupplicant::kNetworkBgscanMethodSimple;
mukesh agrawal4d0401c2012-01-06 16:05:31 -080068const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
69const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
70const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Darin Petkov4a66cc52012-06-15 10:08:29 +020071// Scan interval while connected.
72const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal5c05b292012-03-07 10:12:52 -080073// Age (in seconds) beyond which a BSS cache entry will not be preserved,
74// across a suspend/resume.
75const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000076const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070077const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070078const int WiFi::kNumFastScanAttempts = 3;
79const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070080const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070081const int WiFi::kReconnectTimeoutSeconds = 10;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070082const size_t WiFi::kMinumumFrequenciesToScan = 4; // Arbitrary but > 0.
Wade Guthrie2edd58b2013-05-23 11:16:08 -070083const float WiFi::kDefaultFractionPerScan = 0.34;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070084const char WiFi::kProgressiveScanFlagFile[] = "/home/chronos/.progressive_scan";
Wade Guthrie2edd58b2013-05-23 11:16:08 -070085const char WiFi::kProgressiveScanFieldTrialFlagFile[] =
86 "/home/chronos/.progressive_scan_variation";
mukesh agrawalb54601c2011-06-07 17:39:22 -070087
Paul Stewartb50f0b92011-05-16 16:31:42 -070088WiFi::WiFi(ControlInterface *control_interface,
89 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080090 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070091 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070092 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080093 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070094 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070095 : Device(control_interface,
96 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080097 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -070098 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070099 link,
Chris Masone626719f2011-08-18 16:58:48 -0700100 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800101 interface_index,
102 Technology::kWifi),
Paul Stewart3c504012013-01-17 17:49:58 -0800103 provider_(manager->wifi_provider()),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500104 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700105 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800106 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200107 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000108 supplicant_state_(kInterfaceStateUnknown),
109 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800110 need_bss_flush_(false),
Wade Guthriebb9fca22013-04-10 17:21:42 -0700111 resumed_at_((struct timeval) {0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700112 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700113 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800114 is_debugging_connection_(false),
Paul Stewart735eab52013-03-29 09:19:23 -0700115 eap_state_handler_(new SupplicantEAPStateHandler()),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800116 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
117 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Wade Guthriebee87c22013-03-06 11:00:46 -0800118 scan_interval_seconds_(kDefaultScanIntervalSeconds),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700119 progressive_scan_enabled_(false),
120 netlink_manager_(NetlinkManager::GetInstance()),
121 min_frequencies_to_scan_(kMinumumFrequenciesToScan),
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700122 max_frequencies_to_scan_(std::numeric_limits<int>::max()),
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700123 fraction_per_scan_(kDefaultFractionPerScan),
124 scan_state_(kScanIdle),
125 scan_method_(kScanMethodNone) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700126 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200127 store->RegisterDerivedString(
128 flimflam::kBgscanMethodProperty,
129 StringAccessor(
130 // TODO(petkov): CustomMappedAccessor is used for convenience because
131 // it provides a way to define a custom clearer (unlike
132 // CustomAccessor). We need to implement a fully custom accessor with
133 // no extra argument.
134 new CustomMappedAccessor<WiFi, string, int>(this,
135 &WiFi::ClearBgscanMethod,
136 &WiFi::GetBgscanMethod,
137 &WiFi::SetBgscanMethod,
138 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800139 HelpRegisterDerivedUint16(store,
140 flimflam::kBgscanShortIntervalProperty,
141 &WiFi::GetBgscanShortInterval,
142 &WiFi::SetBgscanShortInterval);
143 HelpRegisterDerivedInt32(store,
144 flimflam::kBgscanSignalThresholdProperty,
145 &WiFi::GetBgscanSignalThreshold,
146 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700147
Chris Masoneb925cc82011-06-22 15:39:57 -0700148 // TODO(quiche): Decide if scan_pending_ is close enough to
149 // "currently scanning" that we don't care, or if we want to track
150 // scan pending/currently scanning/no scan scheduled as a tri-state
151 // kind of thing.
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700152 HelpRegisterConstDerivedBool(store,
153 flimflam::kScanningProperty,
154 &WiFi::GetScanPending);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800155 HelpRegisterDerivedUint16(store,
156 flimflam::kScanIntervalProperty,
157 &WiFi::GetScanInterval,
158 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800159 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
160 ScopeLogger::kWiFi,
161 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Wade Guthriebb9fca22013-04-10 17:21:42 -0700162 CHECK(netlink_manager_);
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700163 if (PathExists(FilePath(kProgressiveScanFlagFile))) {
164 // Setup manually-enrolled progressive scan.
165 progressive_scan_enabled_ = true;
166 } else {
167 // TODO(wdg): Remove after progressive scan field trial is over.
168 // Only do the field trial if the user hasn't already enabled progressive
169 // scan manually. crbug.com/250945
170 ParseFieldTrialFile(FilePath(kProgressiveScanFieldTrialFlagFile));
171 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700172 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700173}
174
mukesh agrawalaf571952011-07-14 14:31:12 -0700175WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700176
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700177void WiFi::ParseFieldTrialFile(const FilePath &info_file_path) {
178 FileReader file_reader;
179 if (!file_reader.Open(info_file_path)) {
180 SLOG(WiFi, 7) << "Not enrolled in progressive scan field trial.";
181 return;
182 }
183 string line;
184 file_reader.ReadLine(&line);
185 switch (line[0]) {
186 case '1':
187 min_frequencies_to_scan_ = 4;
188 max_frequencies_to_scan_ = 4;
189 fraction_per_scan_ = .34;
190 progressive_scan_enabled_ = true;
191 break;
192 case '2':
193 min_frequencies_to_scan_ = 4;
194 max_frequencies_to_scan_ = 4;
195 fraction_per_scan_ = .51;
196 progressive_scan_enabled_ = true;
197 break;
198 case '3':
199 min_frequencies_to_scan_ = 8;
200 max_frequencies_to_scan_ = 8;
201 fraction_per_scan_ = .51;
202 progressive_scan_enabled_ = true;
203 break;
204 case '4':
205 min_frequencies_to_scan_ = 8;
206 max_frequencies_to_scan_ = 8;
207 fraction_per_scan_ = 1.1;
208 progressive_scan_enabled_ = true;
209 break;
210 case 'c': // FALLTHROUGH.
211 case 'x': // FALLTHROUGH.
212 default:
213 progressive_scan_enabled_ = false;
214 break;
215 }
216 LOG(INFO) << "Progressive scan (via field_trial) "
217 << (progressive_scan_enabled_ ? "enabled" : "disabled");
218 if (progressive_scan_enabled_) {
219 LOG(INFO) << " min_frequencies_to_scan_ = " << min_frequencies_to_scan_;
220 LOG(INFO) << " max_frequencies_to_scan_ = " << max_frequencies_to_scan_;
221 LOG(INFO) << " fraction_per_scan_ = " << fraction_per_scan_;
222 }
223
224 file_reader.Close();
225}
226
Eric Shienbrood9a245532012-03-07 14:20:39 -0500227void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200228 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
229 if (enabled()) {
230 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700231 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500232 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200233 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500234 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200235 }
236 if (on_supplicant_appear_.IsCancelled()) {
237 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
238 // device instance.
239 on_supplicant_appear_.Reset(
240 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
241 on_supplicant_vanish_.Reset(
242 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700243 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200244 on_supplicant_appear_.callback(),
245 on_supplicant_vanish_.callback());
246 }
Wade Guthriebee87c22013-03-06 11:00:46 -0800247 // Subscribe to multicast events.
Wade Guthriebb9fca22013-04-10 17:21:42 -0700248 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
249 NetlinkManager::kEventTypeConfig);
250 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
251 NetlinkManager::kEventTypeScan);
252 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
253 NetlinkManager::kEventTypeRegulatory);
254 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
255 NetlinkManager::kEventTypeMlme);
Wade Guthrie92d06362013-04-25 15:41:30 -0700256 ConfigureScanFrequencies();
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700257 // Connect to WPA supplicant if it's already present. If not, we'll connect to
258 // it when it appears.
259 ConnectToSupplicant();
mukesh agrawalab87ea42011-05-18 11:44:49 -0700260}
261
Eric Shienbrood9a245532012-03-07 14:20:39 -0500262void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700263 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200264 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700265 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800266 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
267 it != endpoint_by_rpcid_.end(); ++it) {
268 provider_->OnEndpointRemoved(it->second);
269 }
mukesh agrawal15908392011-11-16 18:29:25 +0000270 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800271 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
272 it != rpcid_by_service_.end(); ++it) {
273 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700274 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700275 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700276 supplicant_interface_proxy_.reset(); // breaks a reference cycle
277 // TODO(quiche): Remove interface from supplicant.
278 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800279 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000280 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800281 is_debugging_connection_ = false;
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700282 SetScanState(kScanIdle, kScanMethodNone);
Paul Stewart2b05e622012-07-13 20:38:44 -0700283 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700284 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700285
Eric Shienbrood9a245532012-03-07 14:20:39 -0500286 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
287 if (error)
288 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700289 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700290
Ben Chanfad4a0b2012-04-18 15:49:59 -0700291 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
292 << (supplicant_process_proxy_.get() ?
293 "is set." : "is not set.");
294 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
295 << (supplicant_interface_proxy_.get() ?
296 "is set." : "is not set.");
297 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
298 << (pending_service_.get() ? "is set." : "is not set.");
299 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
300 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800301}
302
Wade Guthrie68d41092013-04-02 12:56:02 -0700303void WiFi::Scan(ScanType scan_type, Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700304 LOG(INFO) << __func__;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700305 // Measure scans that are supposed to be "progressive" regardless of whether
306 // we are actually doing a progressive scan.
307 if (scan_type == kProgressiveScan) {
Wade Guthrie68d41092013-04-02 12:56:02 -0700308 metrics()->NotifyDeviceScanStarted(interface_index());
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700309 }
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700310 if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
Wade Guthrie299c4272013-06-25 16:18:32 -0700311 LOG(INFO) << "Doing progressive scan on " << link_name();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700312 if (!scan_session_) {
313 // TODO(wdg): Perform in-depth testing to determine the best values for
314 // the different scans. chromium:235293
315 ScanSession::FractionList scan_fractions;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700316 float total_fraction = 0.0;
317 do {
318 total_fraction += fraction_per_scan_;
319 scan_fractions.push_back(fraction_per_scan_);
320 } while (total_fraction < 1.0);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700321 scan_session_.reset(
322 new ScanSession(netlink_manager_,
323 dispatcher(),
324 provider_->GetScanFrequencies(),
325 all_scan_frequencies_,
326 interface_index(),
327 scan_fractions,
328 min_frequencies_to_scan_,
329 max_frequencies_to_scan_,
330 Bind(&WiFi::OnFailedProgressiveScan,
Wade Guthrief22681f2013-05-31 11:46:31 -0700331 weak_ptr_factory_.GetWeakPtr()),
332 metrics()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700333 for (const auto &ssid : provider_->GetHiddenSSIDList()) {
334 scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
335 }
336 }
337 dispatcher()->PostTask(
338 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
339 } else {
Wade Guthrie299c4272013-06-25 16:18:32 -0700340 LOG(INFO) << "Doing full scan on " << link_name() << " - progressive scan "
341 << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED");
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700342 // Needs to send a D-Bus message, but may be called from D-Bus
343 // signal handler context (via Manager::RequestScan). So defer work
344 // to event loop.
345 dispatcher()->PostTask(
346 Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
347 }
mukesh agrawal32399322011-09-01 10:53:43 -0700348}
349
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000350void WiFi::BSSAdded(const ::DBus::Path &path,
351 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500352 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000353 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500354 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
355 weak_ptr_factory_.GetWeakPtr(),
356 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000357}
358
359void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500360 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000361 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500362 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
363 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700364}
365
Paul Stewartbc6e7392012-05-24 07:07:48 -0700366void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
367 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
368 weak_ptr_factory_.GetWeakPtr(), properties));
369}
370
Paul Stewartdb0f9172012-11-30 16:48:09 -0800371void WiFi::EAPEvent(const string &status, const string &parameter) {
372 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
373 weak_ptr_factory_.GetWeakPtr(),
374 status,
375 parameter));
376}
377
mukesh agrawal7ec71312011-11-10 02:08:26 +0000378void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200379 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000380 // Called from D-Bus signal handler, but may need to send a D-Bus
381 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500382 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
383 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000384}
385
mukesh agrawalb54601c2011-06-07 17:39:22 -0700386void WiFi::ScanDone() {
387 LOG(INFO) << __func__;
388
mukesh agrawal7ec71312011-11-10 02:08:26 +0000389 // Defer handling of scan result processing, because that processing
390 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700391 // registration can't be done in the context of a D-Bus signal
392 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500393 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
394 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700395}
396
mukesh agrawal6e277772011-09-29 15:04:23 -0700397void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000398 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800399 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700400 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700401
mukesh agrawal7ec71312011-11-10 02:08:26 +0000402 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000403 if (pending_service_ && pending_service_ == service) {
404 // TODO(quiche): Return an error to the caller. crosbug.com/23832
Darin Petkov457728b2013-01-09 09:49:08 +0100405 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
406 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000407 << ", which is already pending.";
408 return;
409 }
410
411 if (pending_service_ && pending_service_ != service) {
412 DisconnectFrom(pending_service_);
413 }
mukesh agrawal32399322011-09-01 10:53:43 -0700414
Paul Stewart835934a2012-12-06 19:27:09 -0800415 Error unused_error;
416 network_path = FindNetworkRpcidForService(service, &unused_error);
417 if (network_path.empty()) {
418 try {
419 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700420 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800421 append_uint32(scan_ssid);
422 AppendBgscan(service, &service_params);
423 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
424 CHECK(!network_path.empty()); // No DBus path should be empty.
425 rpcid_by_service_[service] = network_path;
426 } catch (const DBus::Error &e) { // NOLINT
427 LOG(ERROR) << "exception while adding network: " << e.what();
428 return;
429 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700430 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700431
Paul Stewarta47c3c62012-12-18 12:14:29 -0800432 if (service->HasRecentConnectionIssues()) {
433 SetConnectionDebugging(true);
434 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700435 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700436 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000437 CHECK(current_service_.get() != pending_service_.get());
438
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700439 // SelectService here (instead of in LinkEvent, like Ethernet), so
440 // that, if we fail to bring up L2, we can attribute failure correctly.
441 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000442 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700443 // reconsider if this is the right place to change the selected service.
444 // see discussion in crosbug.com/20191.
445 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000446}
447
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000448void WiFi::DisconnectFrom(WiFiService *service) {
449 if (service != current_service_ && service != pending_service_) {
450 // TODO(quiche): Once we have asynchronous reply support, we should
451 // generate a D-Bus error here. (crosbug.com/23832)
452 LOG(WARNING) << "In " << __func__ << "(): "
453 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100454 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000455 << " which is neither current nor pending";
456 return;
457 }
458
459 if (pending_service_ && service != pending_service_) {
460 // TODO(quiche): Once we have asynchronous reply support, we should
461 // generate a D-Bus error here. (crosbug.com/23832)
462 LOG(WARNING) << "In " << __func__ << "(): "
463 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100464 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000465 << " which is not the pending service.";
466 return;
467 }
468
469 if (!pending_service_ && service != current_service_) {
470 // TODO(quiche): Once we have asynchronous reply support, we should
471 // generate a D-Bus error here. (crosbug.com/23832)
472 LOG(WARNING) << "In " << __func__ << "(): "
473 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100474 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000475 << " which is not the current service.";
476 return;
477 }
478
Paul Stewartff96a842012-08-13 15:59:10 -0700479 if (pending_service_) {
480 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
481 // on this to drive the service state back to idle. Do that here.
482 pending_service_->SetState(Service::kStateIdle);
483 }
484
Paul Stewart2b05e622012-07-13 20:38:44 -0700485 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700486 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700487
488 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700489 LOG(ERROR) << "In " << __func__ << "(): "
490 << "wpa_supplicant is not present; silently resetting "
491 << "current_service_.";
492 if (current_service_ == selected_service()) {
493 DropConnection();
494 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700495 current_service_ = NULL;
496 return;
497 }
498
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000499 try {
500 supplicant_interface_proxy_->Disconnect();
501 // We'll call RemoveNetwork and reset |current_service_| after
502 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700503 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000504 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700505 // So effect changes immediately. For instance, this can happen when
506 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800507 Error unused_error;
508 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700509 if (service == selected_service()) {
510 DropConnection();
511 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000512 current_service_ = NULL;
513 }
514
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800515 CHECK(current_service_ == NULL ||
516 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000517}
518
Paul Stewart835934a2012-12-06 19:27:09 -0800519bool WiFi::DisableNetwork(const ::DBus::Path &network) {
520 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
521 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700522 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800523 try {
524 supplicant_network_proxy->SetEnabled(false);
525 } catch (const DBus::Error &e) { // NOLINT
526 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
527 return false;
528 }
529 return true;
530}
531
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700532bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
533 try {
534 supplicant_interface_proxy_->RemoveNetwork(network);
535 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700536 // RemoveNetwork can fail with three different errors.
537 //
538 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
539 // already removed the network object, so return true as if
540 // RemoveNetwork removes the network object successfully.
541 //
542 // As shill always passes a valid network object path, RemoveNetwork
543 // should not fail with an InvalidArgs error. Return false in such case
544 // as something weird may have happened. Similarly, return false in case
545 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700546 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700547 return false;
548 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700549 }
550 return true;
551}
552
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000553bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800554 return !current_service_ && !pending_service_;
555}
556
Paul Stewart835934a2012-12-06 19:27:09 -0800557void WiFi::ClearCachedCredentials(const WiFiService *service) {
558 Error unused_error;
559 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800560}
561
Paul Stewart3c504012013-01-17 17:49:58 -0800562void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700563 provider_->OnEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800564}
565
Darin Petkov4a66cc52012-06-15 10:08:29 +0200566void WiFi::AppendBgscan(WiFiService *service,
567 map<string, DBus::Variant> *service_params) const {
568 int scan_interval = kBackgroundScanIntervalSeconds;
569 string method = bgscan_method_;
570 if (method.empty()) {
571 // If multiple APs are detected for this SSID, configure the default method.
572 // Otherwise, disable background scanning completely.
573 if (service->GetEndpointCount() > 1) {
574 method = kDefaultBgscanMethod;
575 } else {
576 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
577 return;
578 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700579 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700580 LOG(INFO) << "Background scan disabled -- chose None method.";
581 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200582 } else {
583 // If the background scan method was explicitly specified, honor the
584 // configured background scan interval.
585 scan_interval = scan_interval_seconds_;
586 }
587 DCHECK(!method.empty());
588 string config_string = StringPrintf("%s:%d:%d:%d",
589 method.c_str(),
590 bgscan_short_interval_seconds_,
591 bgscan_signal_threshold_dbm_,
592 scan_interval);
593 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700594 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200595 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800596}
597
Darin Petkov4a66cc52012-06-15 10:08:29 +0200598string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
599 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
600}
601
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700602bool WiFi::SetBgscanMethod(
Darin Petkov4a66cc52012-06-15 10:08:29 +0200603 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700604 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
605 method != WPASupplicant::kNetworkBgscanMethodLearn &&
606 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800607 const string error_message =
608 StringPrintf("Unrecognized bgscan method %s", method.c_str());
609 LOG(WARNING) << error_message;
610 error->Populate(Error::kInvalidArguments, error_message);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700611 return false;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800612 }
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700613 if (bgscan_method_ == method) {
614 return false;
615 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800616 bgscan_method_ = method;
617 // We do not update kNetworkPropertyBgscan for |pending_service_| or
618 // |current_service_|, because supplicant does not allow for
619 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700620 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800621}
622
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700623bool WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
624 if (bgscan_short_interval_seconds_ == seconds) {
625 return false;
626 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800627 bgscan_short_interval_seconds_ = seconds;
628 // We do not update kNetworkPropertyBgscan for |pending_service_| or
629 // |current_service_|, because supplicant does not allow for
630 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700631 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800632}
633
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700634bool WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
635 if (bgscan_signal_threshold_dbm_ == dbm) {
636 return false;
637 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800638 bgscan_signal_threshold_dbm_ = dbm;
639 // We do not update kNetworkPropertyBgscan for |pending_service_| or
640 // |current_service_|, because supplicant does not allow for
641 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700642 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800643}
644
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700645bool WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
646 if (scan_interval_seconds_ == seconds) {
647 return false;
648 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800649 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700650 if (running()) {
651 StartScanTimer();
652 }
653 // The scan interval affects both foreground scans (handled by
654 // |scan_timer_callback_|), and background scans (handled by
655 // supplicant). However, we do not update |pending_service_| or
656 // |current_service_|, because supplicant does not allow for
657 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700658 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800659}
660
Darin Petkov4a66cc52012-06-15 10:08:29 +0200661void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
662 bgscan_method_.clear();
663}
664
mukesh agrawal15908392011-11-16 18:29:25 +0000665void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700666 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
667 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000668 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700669 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700670
671 // Any change in CurrentBSS means supplicant is actively changing our
672 // connectivity. We no longer need to track any previously pending
673 // reconnect.
674 StopReconnectTimer();
675
Paul Stewart0654ece2013-03-26 15:21:26 -0700676 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000677 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800678 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700679 // Before disconnecting, wpa_supplicant probably scanned for
680 // APs. So, in the normal case, we defer to the timer for the next scan.
681 //
682 // However, in the case of hidden SSIDs, supplicant knows about
683 // at most one of them. (That would be the hidden SSID we were
684 // connected to, if applicable.)
685 //
686 // So, in this case, we initiate an immediate scan. This scan
687 // will include the hidden SSIDs we know about (up to the limit of
688 // kScanMAxSSIDsPerScan).
689 //
690 // We may want to reconsider this immediate scan, if/when shill
691 // takes greater responsibility for scanning (vs. letting
692 // supplicant handle most of it).
Wade Guthrie68d41092013-04-02 12:56:02 -0700693 Scan(kProgressiveScan, NULL);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700694 }
mukesh agrawal15908392011-11-16 18:29:25 +0000695 } else {
696 HandleRoam(new_bss);
697 }
698
Paul Stewart735eab52013-03-29 09:19:23 -0700699 // Reset the EAP handler only after calling HandleDisconnect() above
700 // so our EAP state could be used to detect a failed authentication.
701 eap_state_handler_->Reset();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800702
Paul Stewart2b05e622012-07-13 20:38:44 -0700703 // If we are selecting a new service, or if we're clearing selection
704 // of a something other than the pending service, call SelectService.
705 // Otherwise skip SelectService, since this will cause the pending
706 // service to be marked as Idle.
707 if (current_service_ || selected_service() != pending_service_) {
708 SelectService(current_service_);
709 }
710
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000711 // Invariant check: a Service can either be current, or pending, but
712 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000713 CHECK(current_service_.get() != pending_service_.get() ||
714 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800715
716 // If we are no longer debugging a problematic WiFi connection, return
717 // to the debugging level indicated by the WiFi debugging scope.
718 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
719 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
720 SetConnectionDebugging(false);
721 }
mukesh agrawal15908392011-11-16 18:29:25 +0000722}
723
724void WiFi::HandleDisconnect() {
725 // Identify the affected service. We expect to get a disconnect
726 // event when we fall off a Service that we were connected
727 // to. However, we also allow for the case where we get a disconnect
728 // event while attempting to connect from a disconnected state.
729 WiFiService *affected_service =
730 current_service_.get() ? current_service_.get() : pending_service_.get();
731
732 current_service_ = NULL;
733 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700734 SLOG(WiFi, 2) << "WiFi " << link_name()
735 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000736 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700737 }
738 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700739 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700740 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000741 }
742
Paul Stewart835934a2012-12-06 19:27:09 -0800743 Error error;
744 if (!DisableNetworkForService(affected_service, &error)) {
745 if (error.type() == Error::kNotFound) {
746 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100747 << " (or failed to connect to) service "
748 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800749 << "but could not find supplicant network to disable.";
750 } else {
Wade Guthrie727e0202013-06-27 11:22:38 -0700751 LOG(FATAL) << "DisableNetwork failed on " << link_name()
752 << "for service " << affected_service->unique_name() << ".";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700753 }
mukesh agrawal15908392011-11-16 18:29:25 +0000754 }
755
Ben Chanfad4a0b2012-04-18 15:49:59 -0700756 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100757 << " (or failed to connect to) service "
758 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800759 Service::ConnectFailure failure;
760 if (SuspectCredentials(*affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700761 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700762 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800763 affected_service->SetFailure(failure);
764 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
765 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700766 } else {
767 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700768 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800769 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000770 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000771
772 if (affected_service == pending_service_.get()) {
773 // The attempt to connect to |pending_service_| failed. Clear
774 // |pending_service_|, to indicate we're no longer in the middle
775 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700776 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000777 } else if (pending_service_.get()) {
778 // We've attributed the disconnection to what was the
779 // |current_service_|, rather than the |pending_service_|.
780 //
781 // If we're wrong about that (i.e. supplicant reported this
782 // CurrentBSS change after attempting to connect to
783 // |pending_service_|), we're depending on supplicant to retry
784 // connecting to |pending_service_|, and delivering another
785 // CurrentBSS change signal in the future.
786 //
787 // Log this fact, to help us debug (in case our assumptions are
788 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100789 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
790 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700791 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000792 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700793
794 // If we disconnect, initially scan at a faster frequency, to make sure
795 // we've found all available APs.
796 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000797}
798
799// We use the term "Roam" loosely. In particular, we include the case
800// where we "Roam" to a BSS from the disconnected state.
801void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
802 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
803 if (endpoint_it == endpoint_by_rpcid_.end()) {
804 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
805 << new_bss;
806 return;
807 }
808
Paul Stewart3c504012013-01-17 17:49:58 -0800809 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
810 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000811 if (!service.get()) {
812 LOG(WARNING) << "WiFi " << link_name()
813 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800814 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000815 << " (service will be unchanged)";
816 return;
817 }
818
Ben Chanfad4a0b2012-04-18 15:49:59 -0700819 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800820 << " roamed to Endpoint " << endpoint->bssid_string()
821 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000822
Paul Stewart3c504012013-01-17 17:49:58 -0800823 service->NotifyCurrentEndpoint(endpoint);
Wade Guthrie60a37062013-04-02 11:39:09 -0700824 provider_->IncrementConnectCount(endpoint->frequency());
Thieu Lee41a72d2012-02-06 20:46:51 +0000825
mukesh agrawal15908392011-11-16 18:29:25 +0000826 if (pending_service_.get() &&
827 service.get() != pending_service_.get()) {
828 // The Service we've roamed on to is not the one we asked for.
829 // We assume that this is transient, and that wpa_supplicant
830 // is trying / will try to connect to |pending_service_|.
831 //
832 // If it succeeds, we'll end up back here, but with |service|
833 // pointing at the same service as |pending_service_|.
834 //
835 // If it fails, we'll process things in HandleDisconnect.
836 //
837 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700838 SLOG(WiFi, 2) << "WiFi " << link_name()
839 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800840 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700841 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100842 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000843
844 // Sanity check: if we didn't roam onto |pending_service_|, we
845 // should still be on |current_service_|.
846 if (service.get() != current_service_.get()) {
847 LOG(WARNING) << "WiFi " << link_name()
848 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800849 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000850 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100851 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000852 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100853 << (current_service_ ?
854 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000855 "(NULL)");
856 // Although we didn't expect to get here, we should keep
857 // |current_service_| in sync with what supplicant has done.
858 current_service_ = service;
859 }
860 return;
861 }
862
863 if (pending_service_.get()) {
864 // We assume service.get() == pending_service_.get() here, because
865 // of the return in the previous if clause.
866 //
867 // Boring case: we've connected to the service we asked
868 // for. Simply update |current_service_| and |pending_service_|.
869 current_service_ = service;
Paul Stewart2b05e622012-07-13 20:38:44 -0700870 SetPendingService(NULL);
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700871 SetScanState(kScanConnected, scan_method_);
mukesh agrawal15908392011-11-16 18:29:25 +0000872 return;
873 }
874
875 // |pending_service_| was NULL, so we weren't attempting to connect
876 // to a new Service. Sanity check that we're still on
877 // |current_service_|.
878 if (service.get() != current_service_.get()) {
879 LOG(WARNING)
880 << "WiFi " << link_name()
881 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800882 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000883 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000884 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100885 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000886 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000887 // We didn't expect to be here, but let's cope as well as we
888 // can. Update |current_service_| to keep it in sync with
889 // supplicant.
890 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700891
892 // If this service isn't already marked as actively connecting (likely,
893 // since this service is a bit of a surprise) set the service as
894 // associating.
895 if (!current_service_->IsConnecting()) {
896 current_service_->SetState(Service::kStateAssociating);
897 }
898
mukesh agrawal15908392011-11-16 18:29:25 +0000899 return;
900 }
901
902 // At this point, we know that |pending_service_| was NULL, and that
903 // we're still on |current_service_|. This is the most boring case
904 // of all, because there's no state to update here.
905 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700906}
907
Paul Stewart835934a2012-12-06 19:27:09 -0800908string WiFi::FindNetworkRpcidForService(
909 const WiFiService *service, Error *error) {
910 ReverseServiceMap::const_iterator rpcid_it =
911 rpcid_by_service_.find(service);
912 if (rpcid_it == rpcid_by_service_.end()) {
913 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100914 StringPrintf(
915 "WiFi %s cannot find supplicant network rpcid for service %s",
916 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800917 // There are contexts where this is not an error, such as when a service
918 // is clearing whatever cached credentials may not exist.
919 SLOG(WiFi, 2) << error_message;
920 if (error) {
921 error->Populate(Error::kNotFound, error_message);
922 }
923 return "";
924 }
925
926 return rpcid_it->second;
927}
928
929bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
930 string rpcid = FindNetworkRpcidForService(service, error);
931 if (rpcid.empty()) {
932 // Error is already populated.
933 return false;
934 }
935
936 if (!DisableNetwork(rpcid)) {
937 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100938 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800939 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100940 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800941 rpcid.c_str());
942 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
943
944 // Make sure that such errored networks are removed, so problems do not
945 // propogate to future connection attempts.
946 RemoveNetwork(rpcid);
947 rpcid_by_service_.erase(service);
948
949 return false;
950 }
951
952 return true;
953}
954
955bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
956 string rpcid = FindNetworkRpcidForService(service, error);
957 if (rpcid.empty()) {
958 // Error is already populated.
959 return false;
960 }
961
962 // Erase the rpcid from our tables regardless of failure below, since even
963 // if in failure, we never want to use this network again.
964 rpcid_by_service_.erase(service);
965
966 // TODO(quiche): Reconsider giving up immediately. Maybe give
967 // wpa_supplicant some time to retry, first.
968 if (!RemoveNetwork(rpcid)) {
969 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100970 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800971 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100972 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800973 rpcid.c_str());
974 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
975 return false;
976 }
977
978 return true;
979}
980
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000981void WiFi::BSSAddedTask(
982 const ::DBus::Path &path,
983 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000984 // Note: we assume that BSSIDs are unique across endpoints. This
985 // means that if an AP reuses the same BSSID for multiple SSIDs, we
986 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -0800987 WiFiEndpointRefPtr endpoint(
988 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -0800989 SLOG(WiFi, 1) << "Found endpoint. "
990 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +0100991 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -0800992 << "bssid: " << endpoint->bssid_string() << ", "
993 << "signal: " << endpoint->signal_strength() << ", "
994 << "security: " << endpoint->security_mode() << ", "
995 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000996
997 if (endpoint->ssid_string().empty()) {
998 // Don't bother trying to find or create a Service for an Endpoint
999 // without an SSID. We wouldn't be able to connect to it anyway.
1000 return;
1001 }
1002
mukesh agrawalb3857612012-01-18 16:23:29 -08001003 if (endpoint->ssid()[0] == 0) {
1004 // Assume that an SSID starting with NULL is bogus/misconfigured,
1005 // and filter it out.
1006 return;
1007 }
1008
Paul Stewart3c504012013-01-17 17:49:58 -08001009 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001010
mukesh agrawale9adda12012-02-09 18:33:48 -08001011 // Do this last, to maintain the invariant that any Endpoint we
1012 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001013 //
1014 // TODO(quiche): Write test to verify correct behavior in the case
1015 // where we get multiple BSSAdded events for a single endpoint.
1016 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1017 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -08001018 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -08001019 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001020}
1021
1022void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
1023 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1024 if (i == endpoint_by_rpcid_.end()) {
1025 LOG(WARNING) << "WiFi " << link_name()
1026 << " could not find BSS " << path
1027 << " to remove.";
1028 return;
1029 }
1030
1031 WiFiEndpointRefPtr endpoint = i->second;
1032 CHECK(endpoint);
1033 endpoint_by_rpcid_.erase(i);
1034
Paul Stewart3c504012013-01-17 17:49:58 -08001035 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
1036 if (!service) {
1037 return;
1038 }
1039 Error unused_error;
1040 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001041
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001042 bool disconnect_service = !service->HasEndpoints() &&
1043 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001044
1045 if (disconnect_service) {
mukesh agrawaldc7b8442012-09-27 13:48:14 -07001046 LOG(INFO) << "Disconnecting from service " << service->unique_name()
1047 << ": BSSRemoved";
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001048 DisconnectFrom(service);
1049 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001050}
1051
Paul Stewartbc6e7392012-05-24 07:07:48 -07001052void WiFi::CertificationTask(
1053 const map<string, ::DBus::Variant> &properties) {
1054 if (!current_service_) {
1055 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1056 << " with no current service.";
1057 return;
1058 }
1059
Paul Stewart735eab52013-03-29 09:19:23 -07001060 string subject;
1061 uint32 depth;
1062 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
1063 current_service_->AddEAPCertification(subject, depth);
Paul Stewartbc6e7392012-05-24 07:07:48 -07001064 }
Paul Stewartbc6e7392012-05-24 07:07:48 -07001065}
1066
Paul Stewartdb0f9172012-11-30 16:48:09 -08001067void WiFi::EAPEventTask(const string &status, const string &parameter) {
Paul Stewartdb0f9172012-11-30 16:48:09 -08001068 if (!current_service_) {
1069 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1070 << " with no current service.";
1071 return;
1072 }
Paul Stewart735eab52013-03-29 09:19:23 -07001073 Service::ConnectFailure failure = Service::kFailureUnknown;
1074 eap_state_handler_->ParseStatus(status, parameter, &failure);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001075 if (failure != Service::kFailureUnknown) {
Paul Stewart735eab52013-03-29 09:19:23 -07001076 // Avoid a reporting failure twice by resetting EAP state handler early.
1077 eap_state_handler_->Reset();
mukesh agrawald4dc0832013-03-25 14:38:26 -07001078 Error unused_error;
mukesh agrawald4dc0832013-03-25 14:38:26 -07001079 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001080 }
1081}
1082
mukesh agrawal15908392011-11-16 18:29:25 +00001083void WiFi::PropertiesChangedTask(
1084 const map<string, ::DBus::Variant> &properties) {
1085 // TODO(quiche): Handle changes in other properties (e.g. signal
1086 // strength).
1087
1088 // Note that order matters here. In particular, we want to process
1089 // changes in the current BSS before changes in state. This is so
1090 // that we update the state of the correct Endpoint/Service.
1091
1092 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -07001093 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +00001094 if (properties_it != properties.end()) {
1095 CurrentBSSChanged(properties_it->second.reader().get_path());
1096 }
1097
Paul Stewart0654ece2013-03-26 15:21:26 -07001098 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +00001099 if (properties_it != properties.end()) {
1100 StateChanged(properties_it->second.reader().get_string());
1101 }
1102}
1103
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001104void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001105 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001106 if (scan_session_) {
1107 // Post |ProgressiveScanTask| so it runs after any |BSSAddedTask|s that have
1108 // been posted. This allows connections on new BSSes to be started before
1109 // we decide whether to abort the progressive scan or continue scanning.
1110 dispatcher()->PostTask(
1111 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
1112 } else {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001113 // Post |UpdateScanStateAfterScanDone| so it runs after any |BSSAddedTask|s
1114 // that have been posted. This allows connections on new BSSes to be
1115 // started before we decide whether the scan was fruitful.
1116 dispatcher()->PostTask(Bind(&WiFi::UpdateScanStateAfterScanDone,
1117 weak_ptr_factory_.GetWeakPtr()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001118 }
mukesh agrawal5c05b292012-03-07 10:12:52 -08001119 if (need_bss_flush_) {
1120 CHECK(supplicant_interface_proxy_ != NULL);
1121 // Compute |max_age| relative to |resumed_at_|, to account for the
1122 // time taken to scan.
1123 struct timeval now;
1124 uint32_t max_age;
1125 time_->GetTimeMonotonic(&now);
1126 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1127 supplicant_interface_proxy_->FlushBSS(max_age);
1128 need_bss_flush_ = false;
1129 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001130 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001131}
1132
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001133void WiFi::UpdateScanStateAfterScanDone() {
1134 if (scan_state_ != kScanIdle) {
1135 if (IsIdle()) {
1136 SetScanState(kScanFoundNothing, scan_method_);
1137 } else {
1138 SetScanState(kScanConnecting, scan_method_);
1139 }
1140 }
1141}
1142
mukesh agrawal32399322011-09-01 10:53:43 -07001143void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001144 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001145 if (!enabled()) {
1146 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001147 SetScanState(kScanIdle, kScanMethodNone); // Probably redundant.
Paul Stewartfae4dae2012-09-13 07:43:32 -07001148 return;
1149 }
1150 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1151 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001152 SetScanState(kScanIdle, kScanMethodNone);
Paul Stewartfae4dae2012-09-13 07:43:32 -07001153 return;
1154 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001155 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1156 (current_service_.get() && current_service_->IsConnecting())) {
1157 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001158 SetScanState(kScanConnecting, scan_method_);
Christopher Wileyc68c8672012-11-20 16:52:21 -08001159 return;
1160 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001161 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001162 scan_args[WPASupplicant::kPropertyScanType].writer().
1163 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001164
Paul Stewart3c504012013-01-17 17:49:58 -08001165 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -08001166 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -08001167 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1168 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -07001169 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001170 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001171 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001172 hidden_ssids.end());
1173 }
1174 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1175 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1176 // behavior of doing a broadcast probe.
1177 hidden_ssids.push_back(ByteArray());
1178
Paul Stewart0654ece2013-03-26 15:21:26 -07001179 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001180 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1181 }
1182
Gaurav Shah10109f22011-11-11 20:16:22 -08001183 try {
1184 supplicant_interface_proxy_->Scan(scan_args);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001185 // Only set the scan state/method if we are starting a full scan from
1186 // scratch. Keep the existing method if this is a failover from a
1187 // progressive scan.
1188 if (scan_state_ != kScanScanning) {
1189 SetScanState(kScanScanning, kScanMethodFull);
1190 }
Ben Chan80326f32012-05-04 17:51:32 -07001191 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001192 // A scan may fail if, for example, the wpa_supplicant vanishing
1193 // notification is posted after this task has already started running.
1194 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001195 }
mukesh agrawal32399322011-09-01 10:53:43 -07001196}
1197
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001198void WiFi::ProgressiveScanTask() {
1199 SLOG(WiFi, 2) << __func__ << " - scan requested for " << link_name();
1200 if (!enabled()) {
1201 LOG(INFO) << "Ignoring scan request while device is not enabled.";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001202 SetScanState(kScanIdle, kScanMethodNone); // Probably redundant.
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001203 return;
1204 }
1205 if (!scan_session_) {
1206 SLOG(WiFi, 2) << "No scan session -- returning";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001207 SetScanState(kScanIdle, kScanMethodNone);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001208 return;
1209 }
1210 if (!IsIdle()) {
1211 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1212 scan_session_.reset();
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001213 SetScanState(kScanConnecting, scan_method_);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001214 return;
1215 }
1216 if (scan_session_->HasMoreFrequencies()) {
1217 SLOG(WiFi, 2) << "Initiating a scan -- returning";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001218 SetScanState(kScanScanning, kScanMethodProgressive);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001219 // After us initiating a scan, supplicant will gather the scan results and
1220 // send us zero or more |BSSAdded| events followed by a |ScanDone|.
1221 scan_session_->InitiateScan();
1222 return;
1223 }
1224 LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
1225 << "do a regular scan";
1226 scan_session_.reset();
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001227 SetScanState(kScanScanning, kScanMethodProgressiveFinishedToFull);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001228 Scan(kFullScan, NULL);
1229}
1230
1231void WiFi::OnFailedProgressiveScan() {
1232 LOG(ERROR) << "Couldn't issue a scan on " << link_name()
1233 << " -- doing a regular scan";
1234 scan_session_.reset();
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001235 SetScanState(kScanScanning, kScanMethodProgressiveErrorToFull);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001236 Scan(kFullScan, NULL);
1237}
1238
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001239string WiFi::GetServiceLeaseName(const WiFiService &service) {
1240 return service.GetStorageIdentifier();
1241}
1242
1243void WiFi::DestroyServiceLease(const WiFiService &service) {
1244 DestroyIPConfigLease(GetServiceLeaseName(service));
1245}
1246
mukesh agrawal15908392011-11-16 18:29:25 +00001247void WiFi::StateChanged(const string &new_state) {
1248 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001249 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001250 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1251 << old_state << " -> " << new_state;
1252
1253 WiFiService *affected_service;
1254 // Identify the service to which the state change applies. If
1255 // |pending_service_| is non-NULL, then the state change applies to
1256 // |pending_service_|. Otherwise, it applies to |current_service_|.
1257 //
1258 // This policy is driven by the fact that the |pending_service_|
1259 // doesn't become the |current_service_| until wpa_supplicant
1260 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001261 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001262 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001263 affected_service =
1264 pending_service_.get() ? pending_service_.get() : current_service_.get();
1265 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001266 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1267 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001268 return;
1269 }
1270
Paul Stewart0654ece2013-03-26 15:21:26 -07001271 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001272 if (affected_service->IsConnected()) {
1273 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001274 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001275 } else if (has_already_completed_) {
1276 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001277 } else if (AcquireIPConfigWithLeaseName(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001278 GetServiceLeaseName(*affected_service))) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001279 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001280 affected_service->SetState(Service::kStateConfiguring);
1281 } else {
1282 LOG(ERROR) << "Unable to acquire DHCP config.";
1283 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001284 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001285 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001286 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001287 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1288 new_state == WPASupplicant::kInterfaceStateAssociating ||
1289 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1290 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001291 // Ignore transitions into these states from Completed, to avoid
1292 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001293 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001294 affected_service->SetState(Service::kStateAssociating);
1295 // TOOD(quiche): On backwards transitions, we should probably set
1296 // a timeout for getting back into the completed state. At present,
1297 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001298 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +00001299 // (crosbug.com/23207)
Paul Stewart0654ece2013-03-26 15:21:26 -07001300 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001301 affected_service == current_service_ &&
1302 affected_service->IsConnected()) {
1303 // This means that wpa_supplicant failed in a re-connect attempt, but
1304 // may still be reconnecting. Give wpa_supplicant a limited amount of
1305 // time to transition out this condition by either connecting or changing
1306 // CurrentBSS.
1307 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001308 } else {
1309 // Other transitions do not affect Service state.
1310 //
1311 // Note in particular that we ignore a State change into
1312 // kInterfaceStateDisconnected, in favor of observing the corresponding
1313 // change in CurrentBSS.
1314 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001315}
1316
Paul Stewart1369c2b2013-01-11 05:41:26 -08001317bool WiFi::SuspectCredentials(
1318 const WiFiService &service, Service::ConnectFailure *failure) const {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001319 if (service.IsSecurityMatch(flimflam::kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001320 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewart1369c2b2013-01-11 05:41:26 -08001321 !service.has_ever_connected()) {
1322 if (failure) {
1323 *failure = Service::kFailureBadPassphrase;
1324 }
1325 return true;
1326 }
1327 } else if (service.IsSecurityMatch(flimflam::kSecurity8021x)) {
Paul Stewart735eab52013-03-29 09:19:23 -07001328 if (eap_state_handler_->is_eap_in_progress() &&
1329 !service.has_ever_connected()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001330 if (failure) {
1331 *failure = Service::kFailureEAPAuthentication;
1332 }
1333 return true;
1334 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001335 }
1336
Paul Stewart1369c2b2013-01-11 05:41:26 -08001337 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001338}
1339
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001340// static
1341bool WiFi::SanitizeSSID(string *ssid) {
1342 CHECK(ssid);
1343
1344 size_t ssid_len = ssid->length();
1345 size_t i;
1346 bool changed = false;
1347
Gary Morainac1bdb42012-02-16 17:42:29 -08001348 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001349 if (!g_ascii_isprint((*ssid)[i])) {
1350 (*ssid)[i] = '?';
1351 changed = true;
1352 }
1353 }
1354
1355 return changed;
1356}
1357
Darin Petkov50cb78a2013-02-06 16:17:49 +01001358// static
1359string WiFi::LogSSID(const string &ssid) {
1360 string out;
1361 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1362 // Replace '[' and ']' (in addition to non-printable characters) so that
1363 // it's easy to match the right substring through a non-greedy regex.
1364 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1365 base::StringAppendF(&out, "\\x%02x", *it);
1366 } else {
1367 out += *it;
1368 }
1369 }
1370 return StringPrintf("[SSID=%s]", out.c_str());
1371}
1372
Paul Stewart3c508e12012-08-09 11:40:06 -07001373void WiFi::OnLinkMonitorFailure() {
1374 // If we have never found the gateway, let's be conservative and not
1375 // do anything, in case this network topology does not have a gateway.
1376 if (!link_monitor()->IsGatewayFound()) {
1377 LOG(INFO) << "In " << __func__ << "(): "
1378 << "Skipping reassociate since gateway was never found.";
1379 return;
1380 }
1381
1382 if (!supplicant_present_) {
1383 LOG(ERROR) << "In " << __func__ << "(): "
1384 << "wpa_supplicant is not present. Cannot reassociate.";
1385 return;
1386 }
1387
1388 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001389 // This will force a transition out of connected, if we are actually
1390 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001391 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001392 // If we don't eventually get a transition back into a connected state,
1393 // there is something wrong.
1394 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001395 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1396 } catch (const DBus::Error &e) { // NOLINT
1397 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1398 return;
1399 }
1400}
1401
Arman Ugurayed8e6102012-11-29 14:47:20 -08001402bool WiFi::ShouldUseArpGateway() const {
1403 return true;
1404}
1405
Paul Stewart3c504012013-01-17 17:49:58 -08001406void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1407 DisconnectFrom(service);
1408 if (service == selected_service()) {
1409 DropConnection();
1410 }
1411 Error unused_error;
1412 RemoveNetworkForService(service, &unused_error);
1413}
1414
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001415vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1416 vector<GeolocationInfo> objects;
1417 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1418 it != endpoint_by_rpcid_.end();
1419 ++it) {
1420 GeolocationInfo geoinfo;
1421 WiFiEndpointRefPtr endpoint = it->second;
1422 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1423 geoinfo.AddField(kGeoSignalStrengthProperty,
1424 StringPrintf("%d", endpoint->signal_strength()));
1425 geoinfo.AddField(
1426 kGeoChannelProperty,
1427 StringPrintf("%d",
1428 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
1429 // TODO(gauravsh): Include age field. crosbug.com/35445
1430 objects.push_back(geoinfo);
1431 }
1432 return objects;
1433}
1434
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001435void WiFi::HelpRegisterDerivedInt32(
1436 PropertyStore *store,
1437 const string &name,
1438 int32(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001439 bool(WiFi::*set)(const int32 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001440 store->RegisterDerivedInt32(
1441 name,
1442 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1443}
1444
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001445void WiFi::HelpRegisterDerivedUint16(
1446 PropertyStore *store,
1447 const string &name,
1448 uint16(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001449 bool(WiFi::*set)(const uint16 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001450 store->RegisterDerivedUint16(
1451 name,
1452 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1453}
1454
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001455void WiFi::HelpRegisterConstDerivedBool(
1456 PropertyStore *store,
1457 const string &name,
1458 bool(WiFi::*get)(Error *error)) {
1459 store->RegisterDerivedBool(
1460 name,
1461 BoolAccessor(new CustomAccessor<WiFi, bool>(this, get, NULL)));
1462}
1463
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001464void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001465 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001466 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001467
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001468 // We want to flush the BSS cache, but we don't want to conflict
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001469 // with an active connection attempt. So record the need to flush,
1470 // and take care of flushing when the next scan completes.
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001471 //
1472 // Note that supplicant will automatically expire old cache
1473 // entries (after, e.g., a BSS is not found in two consecutive
1474 // scans). However, our explicit flush accelerates re-association
1475 // in cases where a BSS disappeared while we were asleep. (See,
1476 // e.g. WiFiRoaming.005SuspendRoam.)
1477 time_->GetTimeMonotonic(&resumed_at_);
1478 need_bss_flush_ = true;
1479
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001480 // Abort any current scan (at the shill-level; let any request that's
1481 // already gone out finish) since we don't know when it started.
1482 AbortScan();
1483
1484 if (IsIdle()) {
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001485 // Not scanning/connecting/connected, so let's get things rolling.
Wade Guthrie68d41092013-04-02 12:56:02 -07001486 Scan(kProgressiveScan, NULL);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001487 RestartFastScanAttempts();
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001488 } else {
1489 SLOG(WiFi, 1) << __func__
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001490 << " skipping scan, already connecting or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001491 }
1492}
1493
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001494void WiFi::AbortScan() {
1495 if (scan_session_) {
1496 scan_session_.reset();
1497 }
1498 SetScanState(kScanIdle, kScanMethodNone);
1499}
1500
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001501void WiFi::OnConnected() {
1502 Device::OnConnected();
1503 EnableHighBitrates();
1504}
1505
Paul Stewarte369ece2012-05-22 09:11:03 -07001506void WiFi::RestartFastScanAttempts() {
1507 fast_scans_remaining_ = kNumFastScanAttempts;
1508 StartScanTimer();
1509}
1510
mukesh agrawalb66c6462012-05-07 11:45:25 -07001511void WiFi::StartScanTimer() {
1512 if (scan_interval_seconds_ == 0) {
1513 StopScanTimer();
1514 return;
1515 }
1516 scan_timer_callback_.Reset(
1517 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001518 // Repeat the first few scans after disconnect relatively quickly so we
1519 // have reasonable trust that no APs we are looking for are present.
1520 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1521 fast_scans_remaining_ > 0 ?
1522 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001523}
1524
1525void WiFi::StopScanTimer() {
1526 scan_timer_callback_.Cancel();
1527}
1528
1529void WiFi::ScanTimerHandler() {
1530 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001531 if (scan_state_ == kScanIdle && IsIdle()) {
Wade Guthrie68d41092013-04-02 12:56:02 -07001532 Scan(kProgressiveScan, NULL);
Paul Stewarte369ece2012-05-22 09:11:03 -07001533 if (fast_scans_remaining_ > 0) {
1534 --fast_scans_remaining_;
1535 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001536 }
1537 StartScanTimer();
1538}
1539
Paul Stewart2b05e622012-07-13 20:38:44 -07001540void WiFi::StartPendingTimer() {
1541 pending_timeout_callback_.Reset(
1542 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1543 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1544 kPendingTimeoutSeconds * 1000);
1545}
1546
1547void WiFi::StopPendingTimer() {
1548 pending_timeout_callback_.Cancel();
1549}
1550
1551void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001552 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001553 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001554 if (service) {
1555 service->SetState(Service::kStateAssociating);
1556 StartPendingTimer();
1557 } else if (pending_service_) {
1558 StopPendingTimer();
1559 }
1560 pending_service_ = service;
1561}
1562
1563void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001564 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001565 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1566 CHECK(pending_service_);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001567 pending_service_->DisconnectWithFailure(
1568 Service::kFailureOutOfRange, &unused_error);
Paul Stewart17d90652013-04-04 15:09:11 -07001569
1570 // A hidden service may have no endpoints, since wpa_supplicant
1571 // failed to attain a CurrentBSS. If so, the service has no
1572 // reference to |this| device and cannot call WiFi::DisconnectFrom()
1573 // to reset pending_service_. In this case, we must perform the
1574 // disconnect here ourselves.
1575 if (pending_service_) {
1576 CHECK(!pending_service_->HasEndpoints());
1577 LOG(INFO) << "Hidden service was not found.";
1578 DisconnectFrom(pending_service_);
1579 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001580 SetScanState(kScanFoundNothing, scan_method_);
Paul Stewart2b05e622012-07-13 20:38:44 -07001581}
1582
Paul Stewart44663922012-07-30 11:03:03 -07001583void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001584 if (!reconnect_timeout_callback_.IsCancelled()) {
1585 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1586 << ": reconnect timer already running.";
1587 return;
1588 }
Paul Stewart44663922012-07-30 11:03:03 -07001589 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1590 reconnect_timeout_callback_.Reset(
1591 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1592 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1593 kReconnectTimeoutSeconds * 1000);
1594}
1595
1596void WiFi::StopReconnectTimer() {
1597 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1598 reconnect_timeout_callback_.Cancel();
1599}
1600
1601void WiFi::ReconnectTimeoutHandler() {
1602 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001603 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001604 CHECK(current_service_);
1605 current_service_->SetFailureSilent(Service::kFailureConnect);
1606 DisconnectFrom(current_service_);
1607}
1608
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001609void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1610 LOG(INFO) << "WPA supplicant appeared.";
1611 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001612 // Restart the WiFi device if it's started already. This will reset the
1613 // state and connect the device to the new WPA supplicant instance.
1614 if (enabled()) {
1615 Restart();
1616 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001617 return;
1618 }
1619 supplicant_present_ = true;
1620 ConnectToSupplicant();
1621}
1622
1623void WiFi::OnSupplicantVanish() {
1624 LOG(INFO) << "WPA supplicant vanished.";
1625 if (!supplicant_present_) {
1626 return;
1627 }
1628 supplicant_present_ = false;
1629 // Restart the WiFi device if it's started already. This will effectively
1630 // suspend the device until the WPA supplicant reappears.
1631 if (enabled()) {
1632 Restart();
1633 }
1634}
1635
Paul Stewart5581d072012-12-17 17:30:20 -08001636void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1637 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1638 if (!supplicant_process_proxy_.get()) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001639 SLOG(WiFi, 2) << "Supplicant process proxy not present.";
Paul Stewart5581d072012-12-17 17:30:20 -08001640 return;
1641 }
1642 string current_level;
1643 try {
1644 current_level = supplicant_process_proxy_->GetDebugLevel();
1645 } catch (const DBus::Error &e) { // NOLINT
1646 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1647 return;
1648 }
1649
Paul Stewart0654ece2013-03-26 15:21:26 -07001650 if (current_level != WPASupplicant::kDebugLevelInfo &&
1651 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001652 SLOG(WiFi, 2) << "WiFi debug level is currently "
1653 << current_level
1654 << "; assuming that it is being controlled elsewhere.";
1655 return;
1656 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001657 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1658 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001659
1660 if (new_level == current_level) {
1661 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1662 << current_level;
1663 return;
1664 }
1665
1666 try {
1667 supplicant_process_proxy_->SetDebugLevel(new_level);
1668 } catch (const DBus::Error &e) { // NOLINT
1669 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1670 }
1671}
1672
Paul Stewarta47c3c62012-12-18 12:14:29 -08001673void WiFi::SetConnectionDebugging(bool enabled) {
1674 if (is_debugging_connection_ == enabled) {
1675 return;
1676 }
1677 OnWiFiDebugScopeChanged(
1678 enabled ||
1679 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1680 is_debugging_connection_ = enabled;
1681}
1682
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001683void WiFi::ConnectToSupplicant() {
1684 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1685 << " supplicant: "
1686 << (supplicant_present_ ? "present" : "absent")
1687 << " proxy: "
1688 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1689 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1690 return;
1691 }
1692 supplicant_process_proxy_.reset(
1693 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001694 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001695 OnWiFiDebugScopeChanged(
1696 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001697 ::DBus::Path interface_path;
1698 try {
1699 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001700 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001701 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001702 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1703 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001704 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001705 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001706 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001707 interface_path =
1708 supplicant_process_proxy_->CreateInterface(create_interface_args);
1709 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001710 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001711 interface_path =
1712 supplicant_process_proxy_->GetInterface(link_name());
1713 // TODO(quiche): Is it okay to crash here, if device is missing?
1714 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001715 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1716 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001717 }
1718 }
1719
1720 supplicant_interface_proxy_.reset(
1721 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001722 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001723
1724 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1725 IFF_UP);
1726 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1727
1728 // Clear out any networks that might previously have been configured
1729 // for this interface.
1730 supplicant_interface_proxy_->RemoveAllNetworks();
1731
1732 // Flush interface's BSS cache, so that we get BSSAdded signals for
1733 // all BSSes (not just new ones since the last scan).
1734 supplicant_interface_proxy_->FlushBSS(0);
1735
1736 try {
1737 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1738 // with RADIUS servers that respond strangely to such requests.
1739 // crosbug.com/25630
1740 supplicant_interface_proxy_->SetFastReauth(false);
1741 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001742 LOG(ERROR) << "Failed to disable fast_reauth. "
1743 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001744 }
1745
1746 try {
1747 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1748 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1749 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001750 LOG(ERROR) << "Failed to set scan_interval. "
1751 << "May be running an older version of wpa_supplicant.";
1752 }
1753
1754 try {
1755 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1756 } catch (const DBus::Error &e) { // NOLINT
1757 LOG(ERROR) << "Failed to disable high bitrates. "
1758 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001759 }
1760
Wade Guthrie68d41092013-04-02 12:56:02 -07001761 Scan(kProgressiveScan, NULL);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001762 StartScanTimer();
1763}
1764
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001765void WiFi::EnableHighBitrates() {
1766 LOG(INFO) << "Enabling high bitrates.";
1767 try {
1768 supplicant_interface_proxy_->EnableHighBitrates();
1769 } catch (const DBus::Error &e) { // NOLINT
1770 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1771 }
1772}
1773
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001774void WiFi::Restart() {
1775 LOG(INFO) << link_name() << " restarting.";
1776 WiFiRefPtr me = this; // Make sure we don't get destructed.
1777 // Go through the manager rather than starting and stopping the device
1778 // directly so that the device can be configured with the profile.
1779 manager()->DeregisterDevice(me);
1780 manager()->RegisterDevice(me);
1781}
1782
Wade Guthrie92d06362013-04-25 15:41:30 -07001783void WiFi::ConfigureScanFrequencies() {
1784 GetWiphyMessage get_wiphy;
1785 get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
1786 interface_index());
Wade Guthrie7347bf22013-04-30 11:21:51 -07001787 netlink_manager_->SendNl80211Message(
1788 &get_wiphy,
1789 Bind(&WiFi::OnNewWiphy, weak_ptr_factory_.GetWeakPtr()),
1790 Bind(&NetlinkManager::OnNetlinkMessageError));
Wade Guthrie92d06362013-04-25 15:41:30 -07001791}
1792
Wade Guthrie7347bf22013-04-30 11:21:51 -07001793void WiFi::OnNewWiphy(const Nl80211Message &nl80211_message) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001794 // Verify NL80211_CMD_NEW_WIPHY
Wade Guthrie7347bf22013-04-30 11:21:51 -07001795 if (nl80211_message.command() != NewWiphyMessage::kCommand) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001796 LOG(ERROR) << "Received unexpected command:"
Wade Guthrie7347bf22013-04-30 11:21:51 -07001797 << nl80211_message.command();
Wade Guthrie92d06362013-04-25 15:41:30 -07001798 return;
1799 }
1800
1801 // The attributes, for this message, are complicated.
1802 // NL80211_ATTR_BANDS contains an array of bands...
1803 AttributeListConstRefPtr wiphy_bands;
Wade Guthrie7347bf22013-04-30 11:21:51 -07001804 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
Wade Guthrie92d06362013-04-25 15:41:30 -07001805 NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
1806 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
1807 return;
1808 }
1809
1810 AttributeIdIterator band_iter(*wiphy_bands);
1811 for (; !band_iter.AtEnd(); band_iter.Advance()) {
1812 AttributeListConstRefPtr wiphy_band;
1813 if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
1814 &wiphy_band)) {
1815 LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
1816 continue;
1817 }
1818
1819 // ...Each band has a FREQS attribute...
1820 AttributeListConstRefPtr frequencies;
1821 if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
1822 &frequencies)) {
1823 LOG(ERROR) << "BAND " << band_iter.GetId()
1824 << " had no 'frequencies' attribute";
1825 continue;
1826 }
1827
1828 // ...And each FREQS attribute contains an array of information about the
1829 // frequency...
1830 AttributeIdIterator freq_iter(*frequencies);
1831 for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
1832 AttributeListConstRefPtr frequency;
1833 if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
1834 &frequency)) {
1835 // ...Including the frequency, itself (the part we want).
1836 uint32_t frequency_value = 0;
1837 if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
1838 &frequency_value)) {
Wade Guthrie2edd58b2013-05-23 11:16:08 -07001839 SLOG(WiFi, 7) << "Found frequency[" << freq_iter.GetId()
Wade Guthrie92d06362013-04-25 15:41:30 -07001840 << "] = " << frequency_value;
1841 all_scan_frequencies_.insert(frequency_value);
1842 }
1843 }
1844 }
1845 }
1846}
1847
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001848bool WiFi::GetScanPending(Error */* error */) {
1849 return scan_state_ == kScanScanning;
1850}
1851
1852void WiFi::SetScanState(ScanState new_state, ScanMethod new_method) {
1853 if (new_state == kScanIdle)
1854 new_method = kScanMethodNone;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07001855 if (new_state == kScanConnected) {
1856 // The scan method shouldn't be changed by the connection process, so
1857 // we'll put a CHECK, here, to verify. NOTE: this assumption is also
1858 // enforced by the parameters to the call to |ReportScanResultToUma|.
1859 CHECK(new_method == scan_method_);
1860 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001861
1862 int log_level = 6;
1863 bool state_changed = true;
1864 bool is_terminal_state = false;
1865 if (new_state == scan_state_ && new_method == scan_method_) {
1866 log_level = 7;
1867 state_changed = false;
1868 } else if (new_state == kScanConnected || new_state == kScanFoundNothing) {
1869 // These 'terminal' states are slightly more interesting than the
1870 // intermediate states.
1871 log_level = 5;
1872 is_terminal_state = true;
1873 }
1874
1875 base::TimeDelta elapsed_time;
1876 if (new_state == kScanScanning) {
1877 if (!scan_timer_.Start()) {
1878 LOG(ERROR) << "Scan start unreliable";
1879 }
1880 } else {
1881 if (!scan_timer_.GetElapsedTime(&elapsed_time)) {
1882 LOG(ERROR) << "Scan time unreliable";
1883 }
1884 }
1885 SLOG(WiFi, log_level) << link_name()
1886 << ": Scan state: "
1887 << ScanStateString(scan_state_, scan_method_)
1888 << " -> " << ScanStateString(new_state, new_method)
1889 << " @ " << elapsed_time.InMillisecondsF()
1890 << " ms into scan.";
1891 if (!state_changed)
1892 return;
1893
1894 // Actually change the state.
1895 ScanState old_state = scan_state_;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07001896 ScanMethod old_method = scan_method_;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001897 scan_state_ = new_state;
1898 scan_method_ = new_method;
1899 if (new_state == kScanScanning || old_state == kScanScanning) {
1900 Error error;
1901 adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
1902 GetScanPending(&error));
1903 }
Wade Guthrie44f290d2013-05-28 10:16:25 -07001904 switch (new_state) {
1905 case kScanIdle:
1906 metrics()->ResetScanTimer(interface_index());
1907 break;
1908 case kScanConnecting:
1909 metrics()->NotifyDeviceScanFinished(interface_index());
1910 // TODO(wdg): Provide |is_auto_connecting| to this interface. For now,
1911 // I'll lie (because I don't care about the auto-connect metrics).
1912 metrics()->NotifyDeviceConnectStarted(interface_index(), false);
1913 break;
1914 case kScanConnected:
1915 metrics()->NotifyDeviceConnectFinished(interface_index());
1916 break;
1917 case kScanFoundNothing:
1918 // Note that finishing a scan that hasn't started (if, for example, we
1919 // get here when we fail to complete a connection) does nothing.
1920 metrics()->NotifyDeviceScanFinished(interface_index());
1921 metrics()->ResetConnectTimer(interface_index());
1922 break;
1923 default:
1924 break;
1925 }
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07001926 ReportScanResultToUma(new_state, old_method);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001927 if (is_terminal_state) {
1928 // Now that we've logged a terminal state, let's call ourselves to
1929 // transistion to the idle state.
1930 SetScanState(kScanIdle, kScanMethodNone);
1931 }
1932}
1933
1934// static
1935string WiFi::ScanStateString(ScanState state, ScanMethod method) {
1936 switch (state) {
1937 case kScanIdle:
1938 return "IDLE";
1939 case kScanScanning:
1940 DCHECK(method != kScanMethodNone) << "Scanning with no scan method.";
1941 switch (method) {
1942 case kScanMethodFull:
1943 return "FULL_START";
1944 case kScanMethodProgressive:
1945 return "PROGRESSIVE_START";
1946 case kScanMethodProgressiveErrorToFull:
1947 return "PROGRESSIVE_ERROR_FULL_START";
1948 case kScanMethodProgressiveFinishedToFull:
1949 return "PROGRESSIVE_FINISHED_FULL_START";
1950 default:
1951 NOTREACHED();
1952 }
1953 case kScanConnecting:
1954 switch (method) {
1955 case kScanMethodNone:
1956 return "CONNECTING (not scan related)";
1957 case kScanMethodFull:
1958 return "FULL_CONNECTING";
1959 case kScanMethodProgressive:
1960 return "PROGRESSIVE_CONNECTING";
1961 case kScanMethodProgressiveErrorToFull:
1962 return "PROGRESSIVE_ERROR_FULL_CONNECTING";
1963 case kScanMethodProgressiveFinishedToFull:
1964 return "PROGRESSIVE_FINISHED_FULL_CONNECTING";
1965 default:
1966 NOTREACHED();
1967 }
1968 case kScanConnected:
1969 switch (method) {
1970 case kScanMethodNone:
1971 return "CONNECTED (not scan related; e.g., from a supplicant roam)";
1972 case kScanMethodFull:
1973 return "FULL_CONNECTED";
1974 case kScanMethodProgressive:
1975 return "PROGRESSIVE_CONNECTED";
1976 case kScanMethodProgressiveErrorToFull:
1977 return "PROGRESSIVE_ERROR_FULL_CONNECTED";
1978 case kScanMethodProgressiveFinishedToFull:
1979 return "PROGRESSIVE_FINISHED_FULL_CONNECTED";
1980 default:
1981 NOTREACHED();
1982 }
1983 case kScanFoundNothing:
1984 switch (method) {
1985 case kScanMethodNone:
1986 return "CONNECT FAILED (not scan related)";
1987 case kScanMethodFull:
1988 return "FULL_NOCONNECTION";
1989 case kScanMethodProgressive:
1990 // This is possible if shill started to connect but timed out before
1991 // the connection was completed.
1992 return "PROGRESSIVE_FINISHED_NOCONNECTION";
1993 case kScanMethodProgressiveErrorToFull:
1994 return "PROGRESSIVE_ERROR_FULL_NOCONNECTION";
1995 case kScanMethodProgressiveFinishedToFull:
1996 return "PROGRESSIVE_FINISHED_FULL_NOCONNECTION";
1997 default:
1998 NOTREACHED();
1999 }
2000 default:
2001 NOTREACHED();
2002 }
2003 return ""; // To shut up the compiler (that doesn't understand NOTREACHED).
2004}
2005
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07002006void WiFi::ReportScanResultToUma(ScanState state, ScanMethod method) {
2007 Metrics::WiFiScanResult result = Metrics::kScanResultMax;
2008 if (state == kScanConnected) {
2009 switch (method) {
2010 case kScanMethodFull:
2011 result = Metrics::kScanResultFullScanConnected;
2012 break;
2013 case kScanMethodProgressive:
2014 result = Metrics::kScanResultProgressiveConnected;
2015 break;
2016 case kScanMethodProgressiveErrorToFull:
2017 result = Metrics::kScanResultProgressiveErrorButFullConnected;
2018 break;
2019 case kScanMethodProgressiveFinishedToFull:
2020 result = Metrics::kScanResultProgressiveAndFullConnected;
2021 break;
2022 default:
2023 // OK: Connect resulting from something other than scan.
2024 break;
2025 }
2026 } else if (state == kScanFoundNothing) {
2027 switch (method) {
2028 case kScanMethodFull:
2029 result = Metrics::kScanResultFullScanFoundNothing;
2030 break;
2031 case kScanMethodProgressiveErrorToFull:
2032 result = Metrics::kScanResultProgressiveErrorAndFullFoundNothing;
2033 break;
2034 case kScanMethodProgressiveFinishedToFull:
2035 result = Metrics::kScanResultProgressiveAndFullFoundNothing;
2036 break;
2037 default:
2038 // OK: Connect failed, not scan related.
2039 break;
2040 }
2041 }
2042
2043 if (result != Metrics::kScanResultMax) {
2044 metrics()->SendEnumToUMA(Metrics::kMetricScanResult,
2045 result,
2046 Metrics::kScanResultMax);
2047 }
2048}
2049
Paul Stewartb50f0b92011-05-16 16:31:42 -07002050} // namespace shill