blob: 08f9084873f5e93daf219ecfce4c26cc1f7a1ee2 [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),
Wade Guthrie04fd0482013-07-25 10:43:01 -0700120 scan_configuration_("Full scan"),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700121 netlink_manager_(NetlinkManager::GetInstance()),
122 min_frequencies_to_scan_(kMinumumFrequenciesToScan),
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700123 max_frequencies_to_scan_(std::numeric_limits<int>::max()),
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700124 fraction_per_scan_(kDefaultFractionPerScan),
125 scan_state_(kScanIdle),
Paul Stewartf6f96482013-07-12 12:49:15 -0700126 scan_method_(kScanMethodNone),
127 receive_byte_count_at_connect_(0) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700128 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200129 store->RegisterDerivedString(
130 flimflam::kBgscanMethodProperty,
131 StringAccessor(
132 // TODO(petkov): CustomMappedAccessor is used for convenience because
133 // it provides a way to define a custom clearer (unlike
134 // CustomAccessor). We need to implement a fully custom accessor with
135 // no extra argument.
136 new CustomMappedAccessor<WiFi, string, int>(this,
137 &WiFi::ClearBgscanMethod,
138 &WiFi::GetBgscanMethod,
139 &WiFi::SetBgscanMethod,
140 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800141 HelpRegisterDerivedUint16(store,
142 flimflam::kBgscanShortIntervalProperty,
143 &WiFi::GetBgscanShortInterval,
144 &WiFi::SetBgscanShortInterval);
145 HelpRegisterDerivedInt32(store,
146 flimflam::kBgscanSignalThresholdProperty,
147 &WiFi::GetBgscanSignalThreshold,
148 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700149
Chris Masoneb925cc82011-06-22 15:39:57 -0700150 // TODO(quiche): Decide if scan_pending_ is close enough to
151 // "currently scanning" that we don't care, or if we want to track
152 // scan pending/currently scanning/no scan scheduled as a tri-state
153 // kind of thing.
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700154 HelpRegisterConstDerivedBool(store,
155 flimflam::kScanningProperty,
156 &WiFi::GetScanPending);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800157 HelpRegisterDerivedUint16(store,
158 flimflam::kScanIntervalProperty,
159 &WiFi::GetScanInterval,
160 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800161 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
162 ScopeLogger::kWiFi,
163 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Wade Guthriebb9fca22013-04-10 17:21:42 -0700164 CHECK(netlink_manager_);
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700165 if (PathExists(FilePath(kProgressiveScanFlagFile))) {
166 // Setup manually-enrolled progressive scan.
167 progressive_scan_enabled_ = true;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700168 scan_configuration_ = "Progressive scan (manual: min=4/max=MAX, 33%)";
169 min_frequencies_to_scan_ = 4;
170 max_frequencies_to_scan_ = std::numeric_limits<int>::max();
171 fraction_per_scan_ = .34;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700172 } else {
173 // TODO(wdg): Remove after progressive scan field trial is over.
174 // Only do the field trial if the user hasn't already enabled progressive
175 // scan manually. crbug.com/250945
176 ParseFieldTrialFile(FilePath(kProgressiveScanFieldTrialFlagFile));
177 }
Ben Chanfad4a0b2012-04-18 15:49:59 -0700178 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700179}
180
mukesh agrawalaf571952011-07-14 14:31:12 -0700181WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700182
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700183void WiFi::ParseFieldTrialFile(const FilePath &info_file_path) {
184 FileReader file_reader;
185 if (!file_reader.Open(info_file_path)) {
186 SLOG(WiFi, 7) << "Not enrolled in progressive scan field trial.";
187 return;
188 }
189 string line;
190 file_reader.ReadLine(&line);
191 switch (line[0]) {
192 case '1':
193 min_frequencies_to_scan_ = 4;
194 max_frequencies_to_scan_ = 4;
195 fraction_per_scan_ = .34;
196 progressive_scan_enabled_ = true;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700197 scan_configuration_ = "Progressive scan (field trial 1: min/max=4, 33%)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700198 break;
199 case '2':
200 min_frequencies_to_scan_ = 4;
201 max_frequencies_to_scan_ = 4;
202 fraction_per_scan_ = .51;
203 progressive_scan_enabled_ = true;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700204 scan_configuration_ = "Progressive scan (field trial 2: min/max=4, 50%)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700205 break;
206 case '3':
207 min_frequencies_to_scan_ = 8;
208 max_frequencies_to_scan_ = 8;
209 fraction_per_scan_ = .51;
210 progressive_scan_enabled_ = true;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700211 scan_configuration_ = "Progressive scan (field trial 3: min/max=8, 50%)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700212 break;
213 case '4':
214 min_frequencies_to_scan_ = 8;
215 max_frequencies_to_scan_ = 8;
216 fraction_per_scan_ = 1.1;
217 progressive_scan_enabled_ = true;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700218 scan_configuration_ = "Progressive scan (field trial 4: min/max=8, 100%)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700219 break;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700220 case 'c':
221 progressive_scan_enabled_ = false;
222 scan_configuration_ = "Full scan (field trial c: control group)";
223 break;
224 case 'x':
225 progressive_scan_enabled_ = false;
226 scan_configuration_ = "Full scan (field trial x: default/disabled group)";
227 break;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700228 default:
Wade Guthrie04fd0482013-07-25 10:43:01 -0700229 scan_configuration_ = "Full scan (field trial unknown)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700230 progressive_scan_enabled_ = false;
231 break;
232 }
233 LOG(INFO) << "Progressive scan (via field_trial) "
234 << (progressive_scan_enabled_ ? "enabled" : "disabled");
235 if (progressive_scan_enabled_) {
236 LOG(INFO) << " min_frequencies_to_scan_ = " << min_frequencies_to_scan_;
237 LOG(INFO) << " max_frequencies_to_scan_ = " << max_frequencies_to_scan_;
238 LOG(INFO) << " fraction_per_scan_ = " << fraction_per_scan_;
239 }
240
241 file_reader.Close();
242}
243
mukesh agrawal6813e762013-07-10 19:05:08 -0700244void WiFi::Start(Error *error,
245 const EnabledStateChangedCallback &/*callback*/) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200246 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
247 if (enabled()) {
248 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700249 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500250 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200251 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500252 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200253 }
254 if (on_supplicant_appear_.IsCancelled()) {
255 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
256 // device instance.
257 on_supplicant_appear_.Reset(
258 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
259 on_supplicant_vanish_.Reset(
260 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700261 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200262 on_supplicant_appear_.callback(),
263 on_supplicant_vanish_.callback());
264 }
Wade Guthriebee87c22013-03-06 11:00:46 -0800265 // Subscribe to multicast events.
Wade Guthriebb9fca22013-04-10 17:21:42 -0700266 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
267 NetlinkManager::kEventTypeConfig);
268 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
269 NetlinkManager::kEventTypeScan);
270 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
271 NetlinkManager::kEventTypeRegulatory);
272 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
273 NetlinkManager::kEventTypeMlme);
Wade Guthrie92d06362013-04-25 15:41:30 -0700274 ConfigureScanFrequencies();
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700275 // Connect to WPA supplicant if it's already present. If not, we'll connect to
276 // it when it appears.
277 ConnectToSupplicant();
mukesh agrawalab87ea42011-05-18 11:44:49 -0700278}
279
mukesh agrawal6813e762013-07-10 19:05:08 -0700280void WiFi::Stop(Error *error, const EnabledStateChangedCallback &/*callback*/) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700281 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200282 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700283 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800284 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
285 it != endpoint_by_rpcid_.end(); ++it) {
286 provider_->OnEndpointRemoved(it->second);
287 }
mukesh agrawal15908392011-11-16 18:29:25 +0000288 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800289 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
290 it != rpcid_by_service_.end(); ++it) {
291 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700292 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700293 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700294 supplicant_interface_proxy_.reset(); // breaks a reference cycle
295 // TODO(quiche): Remove interface from supplicant.
296 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800297 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000298 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800299 is_debugging_connection_ = false;
Wade Guthrieb0def9f2013-07-12 13:49:18 -0700300 SetScanState(kScanIdle, kScanMethodNone, __func__);
Paul Stewart2b05e622012-07-13 20:38:44 -0700301 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700302 StopReconnectTimer();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700303
Eric Shienbrood9a245532012-03-07 14:20:39 -0500304 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
305 if (error)
306 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700307 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700308
Ben Chanfad4a0b2012-04-18 15:49:59 -0700309 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
310 << (supplicant_process_proxy_.get() ?
311 "is set." : "is not set.");
312 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
313 << (supplicant_interface_proxy_.get() ?
314 "is set." : "is not set.");
315 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
316 << (pending_service_.get() ? "is set." : "is not set.");
317 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
318 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800319}
320
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700321void WiFi::Scan(ScanType scan_type, Error */*error*/, const string &reason) {
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700322 if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700323 LOG(INFO) << __func__ << " [progressive] on " << link_name() << " from "
324 << reason;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700325 LOG(INFO) << scan_configuration_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700326 if (!scan_session_) {
327 // TODO(wdg): Perform in-depth testing to determine the best values for
328 // the different scans. chromium:235293
329 ScanSession::FractionList scan_fractions;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700330 float total_fraction = 0.0;
331 do {
332 total_fraction += fraction_per_scan_;
333 scan_fractions.push_back(fraction_per_scan_);
334 } while (total_fraction < 1.0);
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700335 DCHECK(scan_state_ == kScanIdle);
336 DCHECK(scan_method_ == kScanMethodNone);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700337 scan_session_.reset(
338 new ScanSession(netlink_manager_,
339 dispatcher(),
340 provider_->GetScanFrequencies(),
341 all_scan_frequencies_,
342 interface_index(),
343 scan_fractions,
344 min_frequencies_to_scan_,
345 max_frequencies_to_scan_,
346 Bind(&WiFi::OnFailedProgressiveScan,
Wade Guthrief22681f2013-05-31 11:46:31 -0700347 weak_ptr_factory_.GetWeakPtr()),
348 metrics()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700349 for (const auto &ssid : provider_->GetHiddenSSIDList()) {
350 scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
351 }
352 }
353 dispatcher()->PostTask(
354 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
355 } else {
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700356 LOG(INFO) << __func__ << " [full] on " << link_name()
357 << " (progressive scan "
358 << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED")
359 << ") from " << reason;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700360 LOG(INFO) << scan_configuration_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700361 // Needs to send a D-Bus message, but may be called from D-Bus
362 // signal handler context (via Manager::RequestScan). So defer work
363 // to event loop.
364 dispatcher()->PostTask(
365 Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
366 }
mukesh agrawal32399322011-09-01 10:53:43 -0700367}
368
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000369void WiFi::BSSAdded(const ::DBus::Path &path,
370 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500371 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000372 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500373 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
374 weak_ptr_factory_.GetWeakPtr(),
375 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000376}
377
378void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500379 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000380 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500381 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
382 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700383}
384
Paul Stewartbc6e7392012-05-24 07:07:48 -0700385void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
386 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
387 weak_ptr_factory_.GetWeakPtr(), properties));
388}
389
Paul Stewartdb0f9172012-11-30 16:48:09 -0800390void WiFi::EAPEvent(const string &status, const string &parameter) {
391 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
392 weak_ptr_factory_.GetWeakPtr(),
393 status,
394 parameter));
395}
396
mukesh agrawal7ec71312011-11-10 02:08:26 +0000397void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200398 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000399 // Called from D-Bus signal handler, but may need to send a D-Bus
400 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500401 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
402 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000403}
404
mukesh agrawalb54601c2011-06-07 17:39:22 -0700405void WiFi::ScanDone() {
406 LOG(INFO) << __func__;
407
mukesh agrawal7ec71312011-11-10 02:08:26 +0000408 // Defer handling of scan result processing, because that processing
409 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700410 // registration can't be done in the context of a D-Bus signal
411 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500412 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
413 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700414}
415
mukesh agrawal6e277772011-09-29 15:04:23 -0700416void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000417 map<string, DBus::Variant> service_params) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800418 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700419 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700420
mukesh agrawal7ec71312011-11-10 02:08:26 +0000421 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000422 if (pending_service_ && pending_service_ == service) {
Paul Stewartee6b3d72013-07-12 16:07:51 -0700423 // TODO(quiche): Return an error to the caller. crbug.com/206812
Darin Petkov457728b2013-01-09 09:49:08 +0100424 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
425 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000426 << ", which is already pending.";
427 return;
428 }
429
430 if (pending_service_ && pending_service_ != service) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700431 // This is a signal to SetPendingService(NULL) to not modify the scan
432 // state since the overall story arc isn't reflected by the disconnect.
433 // It is, instead, described by the transition to either kScanFoundNothing
434 // or kScanConnecting (made by |SetPendingService|, below).
435 if (scan_method_ != kScanMethodNone) {
436 SetScanState(kScanTransitionToConnecting, scan_method_, __func__);
437 }
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000438 DisconnectFrom(pending_service_);
439 }
mukesh agrawal32399322011-09-01 10:53:43 -0700440
Paul Stewart835934a2012-12-06 19:27:09 -0800441 Error unused_error;
442 network_path = FindNetworkRpcidForService(service, &unused_error);
443 if (network_path.empty()) {
444 try {
445 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700446 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800447 append_uint32(scan_ssid);
448 AppendBgscan(service, &service_params);
449 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
450 CHECK(!network_path.empty()); // No DBus path should be empty.
451 rpcid_by_service_[service] = network_path;
452 } catch (const DBus::Error &e) { // NOLINT
453 LOG(ERROR) << "exception while adding network: " << e.what();
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700454 SetScanState(kScanIdle, scan_method_, __func__);
Paul Stewart835934a2012-12-06 19:27:09 -0800455 return;
456 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700457 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700458
Paul Stewarta47c3c62012-12-18 12:14:29 -0800459 if (service->HasRecentConnectionIssues()) {
460 SetConnectionDebugging(true);
461 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700462 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700463 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000464 CHECK(current_service_.get() != pending_service_.get());
465
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700466 // SelectService here (instead of in LinkEvent, like Ethernet), so
467 // that, if we fail to bring up L2, we can attribute failure correctly.
468 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000469 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700470 // reconsider if this is the right place to change the selected service.
Paul Stewartee6b3d72013-07-12 16:07:51 -0700471 // see discussion in crbug.com/203282.
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700472 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000473}
474
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000475void WiFi::DisconnectFrom(WiFiService *service) {
476 if (service != current_service_ && service != pending_service_) {
477 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700478 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000479 LOG(WARNING) << "In " << __func__ << "(): "
480 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100481 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000482 << " which is neither current nor pending";
483 return;
484 }
485
486 if (pending_service_ && service != pending_service_) {
487 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700488 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000489 LOG(WARNING) << "In " << __func__ << "(): "
490 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100491 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000492 << " which is not the pending service.";
493 return;
494 }
495
496 if (!pending_service_ && service != current_service_) {
497 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700498 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000499 LOG(WARNING) << "In " << __func__ << "(): "
500 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100501 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000502 << " which is not the current service.";
503 return;
504 }
505
Paul Stewartff96a842012-08-13 15:59:10 -0700506 if (pending_service_) {
507 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
508 // on this to drive the service state back to idle. Do that here.
509 pending_service_->SetState(Service::kStateIdle);
510 }
511
Paul Stewart2b05e622012-07-13 20:38:44 -0700512 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700513 StopReconnectTimer();
Paul Stewart549d44c2012-07-03 12:40:25 -0700514
515 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700516 LOG(ERROR) << "In " << __func__ << "(): "
517 << "wpa_supplicant is not present; silently resetting "
518 << "current_service_.";
519 if (current_service_ == selected_service()) {
520 DropConnection();
521 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700522 current_service_ = NULL;
523 return;
524 }
525
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000526 try {
527 supplicant_interface_proxy_->Disconnect();
528 // We'll call RemoveNetwork and reset |current_service_| after
529 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700530 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000531 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700532 // So effect changes immediately. For instance, this can happen when
533 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800534 Error unused_error;
535 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700536 if (service == selected_service()) {
537 DropConnection();
538 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000539 current_service_ = NULL;
540 }
541
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800542 CHECK(current_service_ == NULL ||
543 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000544}
545
Paul Stewart835934a2012-12-06 19:27:09 -0800546bool WiFi::DisableNetwork(const ::DBus::Path &network) {
547 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
548 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700549 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800550 try {
551 supplicant_network_proxy->SetEnabled(false);
552 } catch (const DBus::Error &e) { // NOLINT
553 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
554 return false;
555 }
556 return true;
557}
558
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700559bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
560 try {
561 supplicant_interface_proxy_->RemoveNetwork(network);
562 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700563 // RemoveNetwork can fail with three different errors.
564 //
565 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
566 // already removed the network object, so return true as if
567 // RemoveNetwork removes the network object successfully.
568 //
569 // As shill always passes a valid network object path, RemoveNetwork
570 // should not fail with an InvalidArgs error. Return false in such case
571 // as something weird may have happened. Similarly, return false in case
572 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700573 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700574 return false;
575 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700576 }
577 return true;
578}
579
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000580bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800581 return !current_service_ && !pending_service_;
582}
583
Paul Stewart835934a2012-12-06 19:27:09 -0800584void WiFi::ClearCachedCredentials(const WiFiService *service) {
585 Error unused_error;
586 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800587}
588
Paul Stewart3c504012013-01-17 17:49:58 -0800589void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700590 provider_->OnEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800591}
592
Darin Petkov4a66cc52012-06-15 10:08:29 +0200593void WiFi::AppendBgscan(WiFiService *service,
594 map<string, DBus::Variant> *service_params) const {
595 int scan_interval = kBackgroundScanIntervalSeconds;
596 string method = bgscan_method_;
597 if (method.empty()) {
598 // If multiple APs are detected for this SSID, configure the default method.
599 // Otherwise, disable background scanning completely.
600 if (service->GetEndpointCount() > 1) {
601 method = kDefaultBgscanMethod;
602 } else {
603 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
604 return;
605 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700606 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700607 LOG(INFO) << "Background scan disabled -- chose None method.";
608 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200609 } else {
610 // If the background scan method was explicitly specified, honor the
611 // configured background scan interval.
612 scan_interval = scan_interval_seconds_;
613 }
614 DCHECK(!method.empty());
615 string config_string = StringPrintf("%s:%d:%d:%d",
616 method.c_str(),
617 bgscan_short_interval_seconds_,
618 bgscan_signal_threshold_dbm_,
619 scan_interval);
620 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700621 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200622 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800623}
624
Darin Petkov4a66cc52012-06-15 10:08:29 +0200625string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
626 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
627}
628
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700629bool WiFi::SetBgscanMethod(
Darin Petkov4a66cc52012-06-15 10:08:29 +0200630 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700631 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
632 method != WPASupplicant::kNetworkBgscanMethodLearn &&
633 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800634 const string error_message =
635 StringPrintf("Unrecognized bgscan method %s", method.c_str());
636 LOG(WARNING) << error_message;
637 error->Populate(Error::kInvalidArguments, error_message);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700638 return false;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800639 }
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700640 if (bgscan_method_ == method) {
641 return false;
642 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800643 bgscan_method_ = method;
644 // We do not update kNetworkPropertyBgscan for |pending_service_| or
645 // |current_service_|, because supplicant does not allow for
646 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700647 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800648}
649
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700650bool WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
651 if (bgscan_short_interval_seconds_ == seconds) {
652 return false;
653 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800654 bgscan_short_interval_seconds_ = seconds;
655 // We do not update kNetworkPropertyBgscan for |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
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700661bool WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
662 if (bgscan_signal_threshold_dbm_ == dbm) {
663 return false;
664 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800665 bgscan_signal_threshold_dbm_ = dbm;
666 // We do not update kNetworkPropertyBgscan for |pending_service_| or
667 // |current_service_|, because supplicant does not allow for
668 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700669 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800670}
671
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700672bool WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
673 if (scan_interval_seconds_ == seconds) {
674 return false;
675 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800676 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700677 if (running()) {
678 StartScanTimer();
679 }
680 // The scan interval affects both foreground scans (handled by
681 // |scan_timer_callback_|), and background scans (handled by
682 // supplicant). However, we do not update |pending_service_| or
683 // |current_service_|, because supplicant does not allow for
684 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700685 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800686}
687
Darin Petkov4a66cc52012-06-15 10:08:29 +0200688void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
689 bgscan_method_.clear();
690}
691
mukesh agrawal15908392011-11-16 18:29:25 +0000692void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700693 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
694 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000695 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700696 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700697
698 // Any change in CurrentBSS means supplicant is actively changing our
699 // connectivity. We no longer need to track any previously pending
700 // reconnect.
701 StopReconnectTimer();
702
Paul Stewart0654ece2013-03-26 15:21:26 -0700703 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000704 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800705 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700706 // Before disconnecting, wpa_supplicant probably scanned for
707 // APs. So, in the normal case, we defer to the timer for the next scan.
708 //
709 // However, in the case of hidden SSIDs, supplicant knows about
710 // at most one of them. (That would be the hidden SSID we were
711 // connected to, if applicable.)
712 //
713 // So, in this case, we initiate an immediate scan. This scan
714 // will include the hidden SSIDs we know about (up to the limit of
715 // kScanMAxSSIDsPerScan).
716 //
717 // We may want to reconsider this immediate scan, if/when shill
718 // takes greater responsibility for scanning (vs. letting
719 // supplicant handle most of it).
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700720 Scan(kProgressiveScan, NULL, __func__);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700721 }
mukesh agrawal15908392011-11-16 18:29:25 +0000722 } else {
723 HandleRoam(new_bss);
724 }
725
Paul Stewart735eab52013-03-29 09:19:23 -0700726 // Reset the EAP handler only after calling HandleDisconnect() above
727 // so our EAP state could be used to detect a failed authentication.
728 eap_state_handler_->Reset();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800729
Paul Stewart2b05e622012-07-13 20:38:44 -0700730 // If we are selecting a new service, or if we're clearing selection
731 // of a something other than the pending service, call SelectService.
732 // Otherwise skip SelectService, since this will cause the pending
733 // service to be marked as Idle.
734 if (current_service_ || selected_service() != pending_service_) {
735 SelectService(current_service_);
736 }
737
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000738 // Invariant check: a Service can either be current, or pending, but
739 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000740 CHECK(current_service_.get() != pending_service_.get() ||
741 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800742
743 // If we are no longer debugging a problematic WiFi connection, return
744 // to the debugging level indicated by the WiFi debugging scope.
745 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
746 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
747 SetConnectionDebugging(false);
748 }
mukesh agrawal15908392011-11-16 18:29:25 +0000749}
750
751void WiFi::HandleDisconnect() {
752 // Identify the affected service. We expect to get a disconnect
753 // event when we fall off a Service that we were connected
754 // to. However, we also allow for the case where we get a disconnect
755 // event while attempting to connect from a disconnected state.
756 WiFiService *affected_service =
757 current_service_.get() ? current_service_.get() : pending_service_.get();
758
759 current_service_ = NULL;
760 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700761 SLOG(WiFi, 2) << "WiFi " << link_name()
762 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000763 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700764 }
765 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700766 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700767 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000768 }
769
Paul Stewart835934a2012-12-06 19:27:09 -0800770 Error error;
771 if (!DisableNetworkForService(affected_service, &error)) {
772 if (error.type() == Error::kNotFound) {
773 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100774 << " (or failed to connect to) service "
775 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800776 << "but could not find supplicant network to disable.";
777 } else {
Wade Guthrie727e0202013-06-27 11:22:38 -0700778 LOG(FATAL) << "DisableNetwork failed on " << link_name()
779 << "for service " << affected_service->unique_name() << ".";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700780 }
mukesh agrawal15908392011-11-16 18:29:25 +0000781 }
782
Ben Chanfad4a0b2012-04-18 15:49:59 -0700783 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100784 << " (or failed to connect to) service "
785 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800786 Service::ConnectFailure failure;
Paul Stewartbca08f82013-07-09 16:32:37 -0700787 if (SuspectCredentials(affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700788 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700789 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800790 affected_service->SetFailure(failure);
791 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
792 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700793 } else {
794 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700795 }
mukesh agrawale1d90e92012-02-15 17:36:08 -0800796 affected_service->NotifyCurrentEndpoint(NULL);
Thieu Le67370f62012-02-14 23:01:42 +0000797 metrics()->NotifyServiceDisconnect(affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000798
799 if (affected_service == pending_service_.get()) {
800 // The attempt to connect to |pending_service_| failed. Clear
801 // |pending_service_|, to indicate we're no longer in the middle
802 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700803 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000804 } else if (pending_service_.get()) {
805 // We've attributed the disconnection to what was the
806 // |current_service_|, rather than the |pending_service_|.
807 //
808 // If we're wrong about that (i.e. supplicant reported this
809 // CurrentBSS change after attempting to connect to
810 // |pending_service_|), we're depending on supplicant to retry
811 // connecting to |pending_service_|, and delivering another
812 // CurrentBSS change signal in the future.
813 //
814 // Log this fact, to help us debug (in case our assumptions are
815 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100816 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
817 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700818 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000819 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700820
821 // If we disconnect, initially scan at a faster frequency, to make sure
822 // we've found all available APs.
823 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000824}
825
826// We use the term "Roam" loosely. In particular, we include the case
827// where we "Roam" to a BSS from the disconnected state.
828void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
829 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
830 if (endpoint_it == endpoint_by_rpcid_.end()) {
831 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
832 << new_bss;
833 return;
834 }
835
Paul Stewart3c504012013-01-17 17:49:58 -0800836 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
837 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000838 if (!service.get()) {
839 LOG(WARNING) << "WiFi " << link_name()
840 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800841 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000842 << " (service will be unchanged)";
843 return;
844 }
845
Ben Chanfad4a0b2012-04-18 15:49:59 -0700846 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800847 << " roamed to Endpoint " << endpoint->bssid_string()
848 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000849
Paul Stewart3c504012013-01-17 17:49:58 -0800850 service->NotifyCurrentEndpoint(endpoint);
Wade Guthrie60a37062013-04-02 11:39:09 -0700851 provider_->IncrementConnectCount(endpoint->frequency());
Thieu Lee41a72d2012-02-06 20:46:51 +0000852
mukesh agrawal15908392011-11-16 18:29:25 +0000853 if (pending_service_.get() &&
854 service.get() != pending_service_.get()) {
855 // The Service we've roamed on to is not the one we asked for.
856 // We assume that this is transient, and that wpa_supplicant
857 // is trying / will try to connect to |pending_service_|.
858 //
859 // If it succeeds, we'll end up back here, but with |service|
860 // pointing at the same service as |pending_service_|.
861 //
862 // If it fails, we'll process things in HandleDisconnect.
863 //
864 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700865 SLOG(WiFi, 2) << "WiFi " << link_name()
866 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800867 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700868 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100869 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000870
871 // Sanity check: if we didn't roam onto |pending_service_|, we
872 // should still be on |current_service_|.
873 if (service.get() != current_service_.get()) {
874 LOG(WARNING) << "WiFi " << link_name()
875 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800876 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000877 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100878 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000879 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100880 << (current_service_ ?
881 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000882 "(NULL)");
883 // Although we didn't expect to get here, we should keep
884 // |current_service_| in sync with what supplicant has done.
885 current_service_ = service;
886 }
887 return;
888 }
889
890 if (pending_service_.get()) {
891 // We assume service.get() == pending_service_.get() here, because
892 // of the return in the previous if clause.
893 //
894 // Boring case: we've connected to the service we asked
895 // for. Simply update |current_service_| and |pending_service_|.
896 current_service_ = service;
Wade Guthrieb0def9f2013-07-12 13:49:18 -0700897 SetScanState(kScanConnected, scan_method_, __func__);
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700898 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000899 return;
900 }
901
902 // |pending_service_| was NULL, so we weren't attempting to connect
903 // to a new Service. Sanity check that we're still on
904 // |current_service_|.
905 if (service.get() != current_service_.get()) {
906 LOG(WARNING)
907 << "WiFi " << link_name()
908 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800909 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000910 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000911 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100912 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000913 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000914 // We didn't expect to be here, but let's cope as well as we
915 // can. Update |current_service_| to keep it in sync with
916 // supplicant.
917 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700918
919 // If this service isn't already marked as actively connecting (likely,
920 // since this service is a bit of a surprise) set the service as
921 // associating.
922 if (!current_service_->IsConnecting()) {
923 current_service_->SetState(Service::kStateAssociating);
924 }
925
mukesh agrawal15908392011-11-16 18:29:25 +0000926 return;
927 }
928
929 // At this point, we know that |pending_service_| was NULL, and that
930 // we're still on |current_service_|. This is the most boring case
931 // of all, because there's no state to update here.
932 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700933}
934
Paul Stewart835934a2012-12-06 19:27:09 -0800935string WiFi::FindNetworkRpcidForService(
936 const WiFiService *service, Error *error) {
937 ReverseServiceMap::const_iterator rpcid_it =
938 rpcid_by_service_.find(service);
939 if (rpcid_it == rpcid_by_service_.end()) {
940 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100941 StringPrintf(
942 "WiFi %s cannot find supplicant network rpcid for service %s",
943 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -0800944 // There are contexts where this is not an error, such as when a service
945 // is clearing whatever cached credentials may not exist.
946 SLOG(WiFi, 2) << error_message;
947 if (error) {
948 error->Populate(Error::kNotFound, error_message);
949 }
950 return "";
951 }
952
953 return rpcid_it->second;
954}
955
956bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
957 string rpcid = FindNetworkRpcidForService(service, error);
958 if (rpcid.empty()) {
959 // Error is already populated.
960 return false;
961 }
962
963 if (!DisableNetwork(rpcid)) {
964 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100965 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800966 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100967 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -0800968 rpcid.c_str());
969 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
970
971 // Make sure that such errored networks are removed, so problems do not
972 // propogate to future connection attempts.
973 RemoveNetwork(rpcid);
974 rpcid_by_service_.erase(service);
975
976 return false;
977 }
978
979 return true;
980}
981
982bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
983 string rpcid = FindNetworkRpcidForService(service, error);
984 if (rpcid.empty()) {
985 // Error is already populated.
986 return false;
987 }
988
989 // Erase the rpcid from our tables regardless of failure below, since even
990 // if in failure, we never want to use this network again.
991 rpcid_by_service_.erase(service);
992
993 // TODO(quiche): Reconsider giving up immediately. Maybe give
994 // wpa_supplicant some time to retry, first.
995 if (!RemoveNetwork(rpcid)) {
996 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +0100997 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -0800998 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +0100999 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -08001000 rpcid.c_str());
1001 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
1002 return false;
1003 }
1004
1005 return true;
1006}
1007
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001008void WiFi::BSSAddedTask(
1009 const ::DBus::Path &path,
1010 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001011 // Note: we assume that BSSIDs are unique across endpoints. This
1012 // means that if an AP reuses the same BSSID for multiple SSIDs, we
1013 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001014 WiFiEndpointRefPtr endpoint(
1015 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -08001016 SLOG(WiFi, 1) << "Found endpoint. "
1017 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +01001018 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -08001019 << "bssid: " << endpoint->bssid_string() << ", "
1020 << "signal: " << endpoint->signal_strength() << ", "
1021 << "security: " << endpoint->security_mode() << ", "
1022 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001023
1024 if (endpoint->ssid_string().empty()) {
1025 // Don't bother trying to find or create a Service for an Endpoint
1026 // without an SSID. We wouldn't be able to connect to it anyway.
1027 return;
1028 }
1029
mukesh agrawalb3857612012-01-18 16:23:29 -08001030 if (endpoint->ssid()[0] == 0) {
1031 // Assume that an SSID starting with NULL is bogus/misconfigured,
1032 // and filter it out.
1033 return;
1034 }
1035
Paul Stewart3c504012013-01-17 17:49:58 -08001036 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001037
mukesh agrawale9adda12012-02-09 18:33:48 -08001038 // Do this last, to maintain the invariant that any Endpoint we
1039 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001040 //
1041 // TODO(quiche): Write test to verify correct behavior in the case
1042 // where we get multiple BSSAdded events for a single endpoint.
1043 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1044 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -08001045 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -08001046 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001047}
1048
1049void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
1050 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1051 if (i == endpoint_by_rpcid_.end()) {
1052 LOG(WARNING) << "WiFi " << link_name()
1053 << " could not find BSS " << path
1054 << " to remove.";
1055 return;
1056 }
1057
1058 WiFiEndpointRefPtr endpoint = i->second;
1059 CHECK(endpoint);
1060 endpoint_by_rpcid_.erase(i);
1061
Paul Stewart3c504012013-01-17 17:49:58 -08001062 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
1063 if (!service) {
1064 return;
1065 }
1066 Error unused_error;
1067 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001068
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001069 bool disconnect_service = !service->HasEndpoints() &&
1070 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001071
1072 if (disconnect_service) {
mukesh agrawaldc7b8442012-09-27 13:48:14 -07001073 LOG(INFO) << "Disconnecting from service " << service->unique_name()
1074 << ": BSSRemoved";
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001075 DisconnectFrom(service);
1076 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001077}
1078
Paul Stewartbc6e7392012-05-24 07:07:48 -07001079void WiFi::CertificationTask(
1080 const map<string, ::DBus::Variant> &properties) {
1081 if (!current_service_) {
1082 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1083 << " with no current service.";
1084 return;
1085 }
1086
Paul Stewart735eab52013-03-29 09:19:23 -07001087 string subject;
1088 uint32 depth;
1089 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
1090 current_service_->AddEAPCertification(subject, depth);
Paul Stewartbc6e7392012-05-24 07:07:48 -07001091 }
Paul Stewartbc6e7392012-05-24 07:07:48 -07001092}
1093
Paul Stewartdb0f9172012-11-30 16:48:09 -08001094void WiFi::EAPEventTask(const string &status, const string &parameter) {
Paul Stewartdb0f9172012-11-30 16:48:09 -08001095 if (!current_service_) {
1096 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1097 << " with no current service.";
1098 return;
1099 }
Paul Stewart735eab52013-03-29 09:19:23 -07001100 Service::ConnectFailure failure = Service::kFailureUnknown;
1101 eap_state_handler_->ParseStatus(status, parameter, &failure);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001102 if (failure != Service::kFailureUnknown) {
Paul Stewart735eab52013-03-29 09:19:23 -07001103 // Avoid a reporting failure twice by resetting EAP state handler early.
1104 eap_state_handler_->Reset();
mukesh agrawald4dc0832013-03-25 14:38:26 -07001105 Error unused_error;
mukesh agrawald4dc0832013-03-25 14:38:26 -07001106 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001107 }
1108}
1109
mukesh agrawal15908392011-11-16 18:29:25 +00001110void WiFi::PropertiesChangedTask(
1111 const map<string, ::DBus::Variant> &properties) {
1112 // TODO(quiche): Handle changes in other properties (e.g. signal
1113 // strength).
1114
1115 // Note that order matters here. In particular, we want to process
1116 // changes in the current BSS before changes in state. This is so
1117 // that we update the state of the correct Endpoint/Service.
1118
1119 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -07001120 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +00001121 if (properties_it != properties.end()) {
1122 CurrentBSSChanged(properties_it->second.reader().get_path());
1123 }
1124
Paul Stewart0654ece2013-03-26 15:21:26 -07001125 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +00001126 if (properties_it != properties.end()) {
1127 StateChanged(properties_it->second.reader().get_string());
1128 }
1129}
1130
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001131void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001132 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001133 if (scan_session_) {
1134 // Post |ProgressiveScanTask| so it runs after any |BSSAddedTask|s that have
1135 // been posted. This allows connections on new BSSes to be started before
1136 // we decide whether to abort the progressive scan or continue scanning.
1137 dispatcher()->PostTask(
1138 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
1139 } else {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001140 // Post |UpdateScanStateAfterScanDone| so it runs after any |BSSAddedTask|s
1141 // that have been posted. This allows connections on new BSSes to be
1142 // started before we decide whether the scan was fruitful.
1143 dispatcher()->PostTask(Bind(&WiFi::UpdateScanStateAfterScanDone,
1144 weak_ptr_factory_.GetWeakPtr()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001145 }
mukesh agrawal5c05b292012-03-07 10:12:52 -08001146 if (need_bss_flush_) {
1147 CHECK(supplicant_interface_proxy_ != NULL);
1148 // Compute |max_age| relative to |resumed_at_|, to account for the
1149 // time taken to scan.
1150 struct timeval now;
1151 uint32_t max_age;
1152 time_->GetTimeMonotonic(&now);
1153 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1154 supplicant_interface_proxy_->FlushBSS(max_age);
1155 need_bss_flush_ = false;
1156 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001157 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001158}
1159
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001160void WiFi::UpdateScanStateAfterScanDone() {
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001161 if (scan_state_ != kScanIdle && IsIdle()) {
1162 SetScanState(kScanFoundNothing, scan_method_, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001163 }
1164}
1165
mukesh agrawal32399322011-09-01 10:53:43 -07001166void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001167 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001168 if (!enabled()) {
1169 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001170 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
Paul Stewartfae4dae2012-09-13 07:43:32 -07001171 return;
1172 }
1173 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1174 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001175 SetScanState(kScanIdle, kScanMethodNone, __func__);
Paul Stewartfae4dae2012-09-13 07:43:32 -07001176 return;
1177 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001178 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1179 (current_service_.get() && current_service_->IsConnecting())) {
1180 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1181 return;
1182 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001183 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001184 scan_args[WPASupplicant::kPropertyScanType].writer().
1185 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001186
Paul Stewart3c504012013-01-17 17:49:58 -08001187 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -08001188 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -08001189 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1190 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -07001191 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001192 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001193 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001194 hidden_ssids.end());
1195 }
1196 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1197 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1198 // behavior of doing a broadcast probe.
1199 hidden_ssids.push_back(ByteArray());
1200
Paul Stewart0654ece2013-03-26 15:21:26 -07001201 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001202 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1203 }
1204
Gaurav Shah10109f22011-11-11 20:16:22 -08001205 try {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001206 // Only set the scan state/method if we are starting a full scan from
1207 // scratch. Keep the existing method if this is a failover from a
1208 // progressive scan.
1209 if (scan_state_ != kScanScanning) {
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001210 SetScanState(kScanScanning, kScanMethodFull, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001211 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001212 supplicant_interface_proxy_->Scan(scan_args);
Ben Chan80326f32012-05-04 17:51:32 -07001213 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001214 // A scan may fail if, for example, the wpa_supplicant vanishing
1215 // notification is posted after this task has already started running.
1216 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001217 }
mukesh agrawal32399322011-09-01 10:53:43 -07001218}
1219
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001220void WiFi::ProgressiveScanTask() {
1221 SLOG(WiFi, 2) << __func__ << " - scan requested for " << link_name();
1222 if (!enabled()) {
1223 LOG(INFO) << "Ignoring scan request while device is not enabled.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001224 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001225 return;
1226 }
1227 if (!scan_session_) {
1228 SLOG(WiFi, 2) << "No scan session -- returning";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001229 SetScanState(kScanIdle, kScanMethodNone, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001230 return;
1231 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001232 // TODO(wdg): We don't currently support progressive background scans. If
1233 // we did, we couldn't bail out, here, if we're connected. Progressive scan
1234 // state will have to be modified to include whether there was a connection
1235 // when the scan started. Then, this code would only bail out if we didn't
1236 // start with a connection but one exists at this point.
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001237 if (!IsIdle()) {
1238 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1239 scan_session_.reset();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001240 return;
1241 }
1242 if (scan_session_->HasMoreFrequencies()) {
1243 SLOG(WiFi, 2) << "Initiating a scan -- returning";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001244 SetScanState(kScanScanning, kScanMethodProgressive, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001245 // After us initiating a scan, supplicant will gather the scan results and
1246 // send us zero or more |BSSAdded| events followed by a |ScanDone|.
1247 scan_session_->InitiateScan();
1248 return;
1249 }
1250 LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
1251 << "do a regular scan";
1252 scan_session_.reset();
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001253 SetScanState(kScanScanning, kScanMethodProgressiveFinishedToFull, __func__);
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001254 Scan(kFullScan, NULL, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001255}
1256
1257void WiFi::OnFailedProgressiveScan() {
1258 LOG(ERROR) << "Couldn't issue a scan on " << link_name()
1259 << " -- doing a regular scan";
1260 scan_session_.reset();
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001261 SetScanState(kScanScanning, kScanMethodProgressiveErrorToFull, __func__);
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001262 Scan(kFullScan, NULL, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001263}
1264
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001265string WiFi::GetServiceLeaseName(const WiFiService &service) {
1266 return service.GetStorageIdentifier();
1267}
1268
1269void WiFi::DestroyServiceLease(const WiFiService &service) {
1270 DestroyIPConfigLease(GetServiceLeaseName(service));
1271}
1272
mukesh agrawal15908392011-11-16 18:29:25 +00001273void WiFi::StateChanged(const string &new_state) {
1274 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001275 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001276 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1277 << old_state << " -> " << new_state;
1278
1279 WiFiService *affected_service;
1280 // Identify the service to which the state change applies. If
1281 // |pending_service_| is non-NULL, then the state change applies to
1282 // |pending_service_|. Otherwise, it applies to |current_service_|.
1283 //
1284 // This policy is driven by the fact that the |pending_service_|
1285 // doesn't become the |current_service_| until wpa_supplicant
1286 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001287 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001288 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001289 affected_service =
1290 pending_service_.get() ? pending_service_.get() : current_service_.get();
1291 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001292 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1293 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001294 return;
1295 }
1296
Paul Stewart0654ece2013-03-26 15:21:26 -07001297 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001298 if (affected_service->IsConnected()) {
1299 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001300 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001301 } else if (has_already_completed_) {
1302 LOG(INFO) << link_name() << " L3 configuration already started.";
Paul Stewart44663922012-07-30 11:03:03 -07001303 } else if (AcquireIPConfigWithLeaseName(
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001304 GetServiceLeaseName(*affected_service))) {
Paul Stewartd408fdf2012-05-07 17:15:57 -07001305 LOG(INFO) << link_name() << " is up; started L3 configuration.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001306 affected_service->SetState(Service::kStateConfiguring);
Paul Stewartf6f96482013-07-12 12:49:15 -07001307 if (affected_service->IsSecurityMatch(flimflam::kSecurityWep)) {
1308 // With the overwhelming majority of WEP networks, we cannot assume
1309 // our credentials are correct just because we have successfully
1310 // connected. It is more useful to track received data as the L3
1311 // configuration proceeds to see if we can decrypt anything.
1312 receive_byte_count_at_connect_ = GetReceiveByteCount();
1313 } else {
1314 affected_service->ResetSuspectedCredentialFailures();
1315 }
mukesh agrawalc01f3982012-01-24 13:48:39 -08001316 } else {
1317 LOG(ERROR) << "Unable to acquire DHCP config.";
1318 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001319 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001320 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001321 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001322 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1323 new_state == WPASupplicant::kInterfaceStateAssociating ||
1324 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1325 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001326 // Ignore transitions into these states from Completed, to avoid
1327 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001328 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001329 affected_service->SetState(Service::kStateAssociating);
1330 // TOOD(quiche): On backwards transitions, we should probably set
1331 // a timeout for getting back into the completed state. At present,
1332 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001333 // has changed. But there may be cases where that signal is not sent.
Paul Stewartee6b3d72013-07-12 16:07:51 -07001334 // (crbug.com/206208)
Paul Stewart0654ece2013-03-26 15:21:26 -07001335 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001336 affected_service == current_service_ &&
1337 affected_service->IsConnected()) {
1338 // This means that wpa_supplicant failed in a re-connect attempt, but
1339 // may still be reconnecting. Give wpa_supplicant a limited amount of
1340 // time to transition out this condition by either connecting or changing
1341 // CurrentBSS.
1342 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001343 } else {
1344 // Other transitions do not affect Service state.
1345 //
1346 // Note in particular that we ignore a State change into
1347 // kInterfaceStateDisconnected, in favor of observing the corresponding
1348 // change in CurrentBSS.
1349 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001350}
1351
Paul Stewart1369c2b2013-01-11 05:41:26 -08001352bool WiFi::SuspectCredentials(
Paul Stewartbca08f82013-07-09 16:32:37 -07001353 WiFiServiceRefPtr service, Service::ConnectFailure *failure) const {
1354 if (service->IsSecurityMatch(flimflam::kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001355 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewartbca08f82013-07-09 16:32:37 -07001356 service->AddSuspectedCredentialFailure()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001357 if (failure) {
1358 *failure = Service::kFailureBadPassphrase;
1359 }
1360 return true;
1361 }
Paul Stewartbca08f82013-07-09 16:32:37 -07001362 } else if (service->IsSecurityMatch(flimflam::kSecurity8021x)) {
Paul Stewart735eab52013-03-29 09:19:23 -07001363 if (eap_state_handler_->is_eap_in_progress() &&
Paul Stewartbca08f82013-07-09 16:32:37 -07001364 service->AddSuspectedCredentialFailure()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001365 if (failure) {
1366 *failure = Service::kFailureEAPAuthentication;
1367 }
1368 return true;
1369 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001370 }
1371
Paul Stewart1369c2b2013-01-11 05:41:26 -08001372 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001373}
1374
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001375// static
1376bool WiFi::SanitizeSSID(string *ssid) {
1377 CHECK(ssid);
1378
1379 size_t ssid_len = ssid->length();
1380 size_t i;
1381 bool changed = false;
1382
Gary Morainac1bdb42012-02-16 17:42:29 -08001383 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001384 if (!g_ascii_isprint((*ssid)[i])) {
1385 (*ssid)[i] = '?';
1386 changed = true;
1387 }
1388 }
1389
1390 return changed;
1391}
1392
Darin Petkov50cb78a2013-02-06 16:17:49 +01001393// static
1394string WiFi::LogSSID(const string &ssid) {
1395 string out;
1396 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1397 // Replace '[' and ']' (in addition to non-printable characters) so that
1398 // it's easy to match the right substring through a non-greedy regex.
1399 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1400 base::StringAppendF(&out, "\\x%02x", *it);
1401 } else {
1402 out += *it;
1403 }
1404 }
1405 return StringPrintf("[SSID=%s]", out.c_str());
1406}
1407
Paul Stewart3c508e12012-08-09 11:40:06 -07001408void WiFi::OnLinkMonitorFailure() {
1409 // If we have never found the gateway, let's be conservative and not
1410 // do anything, in case this network topology does not have a gateway.
1411 if (!link_monitor()->IsGatewayFound()) {
1412 LOG(INFO) << "In " << __func__ << "(): "
1413 << "Skipping reassociate since gateway was never found.";
1414 return;
1415 }
1416
1417 if (!supplicant_present_) {
1418 LOG(ERROR) << "In " << __func__ << "(): "
1419 << "wpa_supplicant is not present. Cannot reassociate.";
1420 return;
1421 }
1422
1423 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001424 // This will force a transition out of connected, if we are actually
1425 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001426 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001427 // If we don't eventually get a transition back into a connected state,
1428 // there is something wrong.
1429 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001430 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1431 } catch (const DBus::Error &e) { // NOLINT
1432 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1433 return;
1434 }
1435}
1436
Arman Ugurayed8e6102012-11-29 14:47:20 -08001437bool WiFi::ShouldUseArpGateway() const {
1438 return true;
1439}
1440
Paul Stewart3c504012013-01-17 17:49:58 -08001441void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1442 DisconnectFrom(service);
1443 if (service == selected_service()) {
1444 DropConnection();
1445 }
1446 Error unused_error;
1447 RemoveNetworkForService(service, &unused_error);
1448}
1449
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001450vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1451 vector<GeolocationInfo> objects;
1452 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1453 it != endpoint_by_rpcid_.end();
1454 ++it) {
1455 GeolocationInfo geoinfo;
1456 WiFiEndpointRefPtr endpoint = it->second;
1457 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1458 geoinfo.AddField(kGeoSignalStrengthProperty,
1459 StringPrintf("%d", endpoint->signal_strength()));
1460 geoinfo.AddField(
1461 kGeoChannelProperty,
1462 StringPrintf("%d",
1463 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
Paul Stewartee6b3d72013-07-12 16:07:51 -07001464 // TODO(gauravsh): Include age field. crbug.com/217554
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001465 objects.push_back(geoinfo);
1466 }
1467 return objects;
1468}
1469
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001470void WiFi::HelpRegisterDerivedInt32(
1471 PropertyStore *store,
1472 const string &name,
1473 int32(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001474 bool(WiFi::*set)(const int32 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001475 store->RegisterDerivedInt32(
1476 name,
1477 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1478}
1479
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001480void WiFi::HelpRegisterDerivedUint16(
1481 PropertyStore *store,
1482 const string &name,
1483 uint16(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001484 bool(WiFi::*set)(const uint16 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001485 store->RegisterDerivedUint16(
1486 name,
1487 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1488}
1489
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001490void WiFi::HelpRegisterConstDerivedBool(
1491 PropertyStore *store,
1492 const string &name,
1493 bool(WiFi::*get)(Error *error)) {
1494 store->RegisterDerivedBool(
1495 name,
1496 BoolAccessor(new CustomAccessor<WiFi, bool>(this, get, NULL)));
1497}
1498
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001499void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001500 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001501 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001502
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001503 // We want to flush the BSS cache, but we don't want to conflict
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001504 // with an active connection attempt. So record the need to flush,
1505 // and take care of flushing when the next scan completes.
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001506 //
1507 // Note that supplicant will automatically expire old cache
1508 // entries (after, e.g., a BSS is not found in two consecutive
1509 // scans). However, our explicit flush accelerates re-association
1510 // in cases where a BSS disappeared while we were asleep. (See,
1511 // e.g. WiFiRoaming.005SuspendRoam.)
1512 time_->GetTimeMonotonic(&resumed_at_);
1513 need_bss_flush_ = true;
1514
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001515 // Abort any current scan (at the shill-level; let any request that's
1516 // already gone out finish) since we don't know when it started.
1517 AbortScan();
1518
1519 if (IsIdle()) {
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001520 // Not scanning/connecting/connected, so let's get things rolling.
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001521 Scan(kProgressiveScan, NULL, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001522 RestartFastScanAttempts();
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001523 } else {
1524 SLOG(WiFi, 1) << __func__
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001525 << " skipping scan, already connecting or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001526 }
1527}
1528
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001529void WiFi::AbortScan() {
1530 if (scan_session_) {
1531 scan_session_.reset();
1532 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001533 SetScanState(kScanIdle, kScanMethodNone, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001534}
1535
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001536void WiFi::OnConnected() {
1537 Device::OnConnected();
1538 EnableHighBitrates();
Paul Stewartf6f96482013-07-12 12:49:15 -07001539 if (current_service_ &&
1540 current_service_->IsSecurityMatch(flimflam::kSecurityWep)) {
1541 // With a WEP network, we are now reasonably certain the credentials are
1542 // correct, whereas with other network types we were able to determine
1543 // this earlier when the association process succeeded.
1544 current_service_->ResetSuspectedCredentialFailures();
1545 }
1546}
1547
1548void WiFi::OnIPConfigFailure() {
1549 if (!current_service_) {
1550 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1551 << " with no current service.";
1552 return;
1553 }
1554 if (current_service_->IsSecurityMatch(flimflam::kSecurityWep) &&
1555 GetReceiveByteCount() == receive_byte_count_at_connect_ &&
1556 current_service_->AddSuspectedCredentialFailure()) {
1557 // If we've connected to a WEP network and haven't successfully
1558 // decrypted any bytes at all during the configuration process,
1559 // it is fair to suspect that our credentials to this network
1560 // may not be correct.
1561 Error error;
1562 current_service_->DisconnectWithFailure(Service::kFailureBadPassphrase,
1563 &error);
1564 return;
1565 }
1566
1567 Device::OnIPConfigFailure();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001568}
1569
Paul Stewarte369ece2012-05-22 09:11:03 -07001570void WiFi::RestartFastScanAttempts() {
1571 fast_scans_remaining_ = kNumFastScanAttempts;
1572 StartScanTimer();
1573}
1574
mukesh agrawalb66c6462012-05-07 11:45:25 -07001575void WiFi::StartScanTimer() {
1576 if (scan_interval_seconds_ == 0) {
1577 StopScanTimer();
1578 return;
1579 }
1580 scan_timer_callback_.Reset(
1581 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001582 // Repeat the first few scans after disconnect relatively quickly so we
1583 // have reasonable trust that no APs we are looking for are present.
1584 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1585 fast_scans_remaining_ > 0 ?
1586 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001587}
1588
1589void WiFi::StopScanTimer() {
1590 scan_timer_callback_.Cancel();
1591}
1592
1593void WiFi::ScanTimerHandler() {
1594 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001595 if (scan_state_ == kScanIdle && IsIdle()) {
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001596 Scan(kProgressiveScan, NULL, __func__);
Paul Stewarte369ece2012-05-22 09:11:03 -07001597 if (fast_scans_remaining_ > 0) {
1598 --fast_scans_remaining_;
1599 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001600 }
1601 StartScanTimer();
1602}
1603
Paul Stewart2b05e622012-07-13 20:38:44 -07001604void WiFi::StartPendingTimer() {
1605 pending_timeout_callback_.Reset(
1606 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1607 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1608 kPendingTimeoutSeconds * 1000);
1609}
1610
1611void WiFi::StopPendingTimer() {
Paul Stewartbca08f82013-07-09 16:32:37 -07001612 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart2b05e622012-07-13 20:38:44 -07001613 pending_timeout_callback_.Cancel();
1614}
1615
1616void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001617 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001618 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001619 if (service) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001620 SetScanState(kScanConnecting, scan_method_, __func__);
Paul Stewart2b05e622012-07-13 20:38:44 -07001621 service->SetState(Service::kStateAssociating);
1622 StartPendingTimer();
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001623 } else {
1624 // SetPendingService(NULL) is called in the following cases:
1625 // a) |ConnectTo|->|DisconnectFrom|. Connecting to a service, disconnect
1626 // the old service (scan_state_ == kScanTransitionToConnecting). No
1627 // state transition is needed here.
1628 // b) |HandleRoam|. Connected to a service, it's no longer pending
1629 // (scan_state_ == kScanIdle). No state transition is needed here.
1630 // c) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
1631 // from a service not during a scan (scan_state_ == kScanIdle). No
1632 // state transition is needed here.
1633 // d) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
1634 // from a service during a scan (scan_state_ == kScanScanning or
1635 // kScanConnecting). This is an odd case -- let's discard any
1636 // statistics we're gathering by transitioning directly into kScanIdle.
1637 if (scan_state_ == kScanScanning || scan_state_ == kScanConnecting) {
1638 SetScanState(kScanIdle, kScanMethodNone, __func__);
1639 }
1640 if (pending_service_) {
1641 StopPendingTimer();
1642 }
Paul Stewart2b05e622012-07-13 20:38:44 -07001643 }
1644 pending_service_ = service;
1645}
1646
1647void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001648 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001649 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1650 CHECK(pending_service_);
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001651 SetScanState(kScanFoundNothing, scan_method_, __func__);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001652 pending_service_->DisconnectWithFailure(
1653 Service::kFailureOutOfRange, &unused_error);
Paul Stewart17d90652013-04-04 15:09:11 -07001654
1655 // A hidden service may have no endpoints, since wpa_supplicant
1656 // failed to attain a CurrentBSS. If so, the service has no
1657 // reference to |this| device and cannot call WiFi::DisconnectFrom()
1658 // to reset pending_service_. In this case, we must perform the
1659 // disconnect here ourselves.
1660 if (pending_service_) {
1661 CHECK(!pending_service_->HasEndpoints());
1662 LOG(INFO) << "Hidden service was not found.";
1663 DisconnectFrom(pending_service_);
1664 }
Paul Stewart2b05e622012-07-13 20:38:44 -07001665}
1666
Paul Stewart44663922012-07-30 11:03:03 -07001667void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001668 if (!reconnect_timeout_callback_.IsCancelled()) {
1669 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1670 << ": reconnect timer already running.";
1671 return;
1672 }
Paul Stewart44663922012-07-30 11:03:03 -07001673 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1674 reconnect_timeout_callback_.Reset(
1675 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1676 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1677 kReconnectTimeoutSeconds * 1000);
1678}
1679
1680void WiFi::StopReconnectTimer() {
1681 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1682 reconnect_timeout_callback_.Cancel();
1683}
1684
1685void WiFi::ReconnectTimeoutHandler() {
1686 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001687 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001688 CHECK(current_service_);
1689 current_service_->SetFailureSilent(Service::kFailureConnect);
1690 DisconnectFrom(current_service_);
1691}
1692
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001693void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1694 LOG(INFO) << "WPA supplicant appeared.";
1695 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001696 // Restart the WiFi device if it's started already. This will reset the
1697 // state and connect the device to the new WPA supplicant instance.
1698 if (enabled()) {
1699 Restart();
1700 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001701 return;
1702 }
1703 supplicant_present_ = true;
1704 ConnectToSupplicant();
1705}
1706
1707void WiFi::OnSupplicantVanish() {
1708 LOG(INFO) << "WPA supplicant vanished.";
1709 if (!supplicant_present_) {
1710 return;
1711 }
1712 supplicant_present_ = false;
1713 // Restart the WiFi device if it's started already. This will effectively
1714 // suspend the device until the WPA supplicant reappears.
1715 if (enabled()) {
1716 Restart();
1717 }
1718}
1719
Paul Stewart5581d072012-12-17 17:30:20 -08001720void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1721 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1722 if (!supplicant_process_proxy_.get()) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001723 SLOG(WiFi, 2) << "Supplicant process proxy not present.";
Paul Stewart5581d072012-12-17 17:30:20 -08001724 return;
1725 }
1726 string current_level;
1727 try {
1728 current_level = supplicant_process_proxy_->GetDebugLevel();
1729 } catch (const DBus::Error &e) { // NOLINT
1730 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1731 return;
1732 }
1733
Paul Stewart0654ece2013-03-26 15:21:26 -07001734 if (current_level != WPASupplicant::kDebugLevelInfo &&
1735 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001736 SLOG(WiFi, 2) << "WiFi debug level is currently "
1737 << current_level
1738 << "; assuming that it is being controlled elsewhere.";
1739 return;
1740 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001741 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1742 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001743
1744 if (new_level == current_level) {
1745 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1746 << current_level;
1747 return;
1748 }
1749
1750 try {
1751 supplicant_process_proxy_->SetDebugLevel(new_level);
1752 } catch (const DBus::Error &e) { // NOLINT
1753 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1754 }
1755}
1756
Paul Stewarta47c3c62012-12-18 12:14:29 -08001757void WiFi::SetConnectionDebugging(bool enabled) {
1758 if (is_debugging_connection_ == enabled) {
1759 return;
1760 }
1761 OnWiFiDebugScopeChanged(
1762 enabled ||
1763 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1764 is_debugging_connection_ = enabled;
1765}
1766
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001767void WiFi::ConnectToSupplicant() {
1768 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1769 << " supplicant: "
1770 << (supplicant_present_ ? "present" : "absent")
1771 << " proxy: "
1772 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1773 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1774 return;
1775 }
1776 supplicant_process_proxy_.reset(
1777 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001778 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001779 OnWiFiDebugScopeChanged(
1780 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001781 ::DBus::Path interface_path;
1782 try {
1783 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001784 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001785 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001786 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1787 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001788 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001789 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001790 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001791 interface_path =
1792 supplicant_process_proxy_->CreateInterface(create_interface_args);
1793 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001794 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001795 interface_path =
1796 supplicant_process_proxy_->GetInterface(link_name());
1797 // TODO(quiche): Is it okay to crash here, if device is missing?
1798 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001799 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1800 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001801 }
1802 }
1803
1804 supplicant_interface_proxy_.reset(
1805 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001806 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001807
1808 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1809 IFF_UP);
1810 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1811
1812 // Clear out any networks that might previously have been configured
1813 // for this interface.
1814 supplicant_interface_proxy_->RemoveAllNetworks();
1815
1816 // Flush interface's BSS cache, so that we get BSSAdded signals for
1817 // all BSSes (not just new ones since the last scan).
1818 supplicant_interface_proxy_->FlushBSS(0);
1819
1820 try {
1821 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1822 // with RADIUS servers that respond strangely to such requests.
Paul Stewartee6b3d72013-07-12 16:07:51 -07001823 // crbug.com/208561
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001824 supplicant_interface_proxy_->SetFastReauth(false);
1825 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001826 LOG(ERROR) << "Failed to disable fast_reauth. "
1827 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001828 }
1829
1830 try {
1831 // Helps with passing WiFiRomaing.001SSIDSwitchBack.
1832 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1833 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001834 LOG(ERROR) << "Failed to set scan_interval. "
1835 << "May be running an older version of wpa_supplicant.";
1836 }
1837
1838 try {
1839 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1840 } catch (const DBus::Error &e) { // NOLINT
1841 LOG(ERROR) << "Failed to disable high bitrates. "
1842 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001843 }
1844
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001845 Scan(kProgressiveScan, NULL, __func__);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001846 StartScanTimer();
1847}
1848
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001849void WiFi::EnableHighBitrates() {
1850 LOG(INFO) << "Enabling high bitrates.";
1851 try {
1852 supplicant_interface_proxy_->EnableHighBitrates();
1853 } catch (const DBus::Error &e) { // NOLINT
1854 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1855 }
1856}
1857
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001858void WiFi::Restart() {
1859 LOG(INFO) << link_name() << " restarting.";
1860 WiFiRefPtr me = this; // Make sure we don't get destructed.
1861 // Go through the manager rather than starting and stopping the device
1862 // directly so that the device can be configured with the profile.
1863 manager()->DeregisterDevice(me);
1864 manager()->RegisterDevice(me);
1865}
1866
Wade Guthrie92d06362013-04-25 15:41:30 -07001867void WiFi::ConfigureScanFrequencies() {
1868 GetWiphyMessage get_wiphy;
1869 get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
1870 interface_index());
Wade Guthrie7347bf22013-04-30 11:21:51 -07001871 netlink_manager_->SendNl80211Message(
1872 &get_wiphy,
1873 Bind(&WiFi::OnNewWiphy, weak_ptr_factory_.GetWeakPtr()),
1874 Bind(&NetlinkManager::OnNetlinkMessageError));
Wade Guthrie92d06362013-04-25 15:41:30 -07001875}
1876
Wade Guthrie7347bf22013-04-30 11:21:51 -07001877void WiFi::OnNewWiphy(const Nl80211Message &nl80211_message) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001878 // Verify NL80211_CMD_NEW_WIPHY
Wade Guthrie7347bf22013-04-30 11:21:51 -07001879 if (nl80211_message.command() != NewWiphyMessage::kCommand) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001880 LOG(ERROR) << "Received unexpected command:"
Wade Guthrie7347bf22013-04-30 11:21:51 -07001881 << nl80211_message.command();
Wade Guthrie92d06362013-04-25 15:41:30 -07001882 return;
1883 }
1884
1885 // The attributes, for this message, are complicated.
1886 // NL80211_ATTR_BANDS contains an array of bands...
1887 AttributeListConstRefPtr wiphy_bands;
Wade Guthrie7347bf22013-04-30 11:21:51 -07001888 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
Wade Guthrie92d06362013-04-25 15:41:30 -07001889 NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
1890 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
1891 return;
1892 }
1893
1894 AttributeIdIterator band_iter(*wiphy_bands);
1895 for (; !band_iter.AtEnd(); band_iter.Advance()) {
1896 AttributeListConstRefPtr wiphy_band;
1897 if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
1898 &wiphy_band)) {
1899 LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
1900 continue;
1901 }
1902
1903 // ...Each band has a FREQS attribute...
1904 AttributeListConstRefPtr frequencies;
1905 if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
1906 &frequencies)) {
1907 LOG(ERROR) << "BAND " << band_iter.GetId()
1908 << " had no 'frequencies' attribute";
1909 continue;
1910 }
1911
1912 // ...And each FREQS attribute contains an array of information about the
1913 // frequency...
1914 AttributeIdIterator freq_iter(*frequencies);
1915 for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
1916 AttributeListConstRefPtr frequency;
1917 if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
1918 &frequency)) {
1919 // ...Including the frequency, itself (the part we want).
1920 uint32_t frequency_value = 0;
1921 if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
1922 &frequency_value)) {
Wade Guthrie2edd58b2013-05-23 11:16:08 -07001923 SLOG(WiFi, 7) << "Found frequency[" << freq_iter.GetId()
Wade Guthrie92d06362013-04-25 15:41:30 -07001924 << "] = " << frequency_value;
1925 all_scan_frequencies_.insert(frequency_value);
1926 }
1927 }
1928 }
1929 }
1930}
1931
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001932bool WiFi::GetScanPending(Error */* error */) {
1933 return scan_state_ == kScanScanning;
1934}
1935
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001936void WiFi::SetScanState(ScanState new_state,
1937 ScanMethod new_method,
1938 const char *reason) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001939 if (new_state == kScanIdle)
1940 new_method = kScanMethodNone;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07001941 if (new_state == kScanConnected) {
1942 // The scan method shouldn't be changed by the connection process, so
1943 // we'll put a CHECK, here, to verify. NOTE: this assumption is also
1944 // enforced by the parameters to the call to |ReportScanResultToUma|.
1945 CHECK(new_method == scan_method_);
1946 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001947
1948 int log_level = 6;
1949 bool state_changed = true;
1950 bool is_terminal_state = false;
1951 if (new_state == scan_state_ && new_method == scan_method_) {
1952 log_level = 7;
1953 state_changed = false;
1954 } else if (new_state == kScanConnected || new_state == kScanFoundNothing) {
1955 // These 'terminal' states are slightly more interesting than the
1956 // intermediate states.
1957 log_level = 5;
1958 is_terminal_state = true;
1959 }
1960
1961 base::TimeDelta elapsed_time;
1962 if (new_state == kScanScanning) {
1963 if (!scan_timer_.Start()) {
1964 LOG(ERROR) << "Scan start unreliable";
1965 }
1966 } else {
1967 if (!scan_timer_.GetElapsedTime(&elapsed_time)) {
1968 LOG(ERROR) << "Scan time unreliable";
1969 }
1970 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001971 SLOG(WiFi, log_level) << (reason ? reason : "<unknown>")
1972 << " - " << link_name()
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001973 << ": Scan state: "
1974 << ScanStateString(scan_state_, scan_method_)
1975 << " -> " << ScanStateString(new_state, new_method)
1976 << " @ " << elapsed_time.InMillisecondsF()
1977 << " ms into scan.";
1978 if (!state_changed)
1979 return;
1980
1981 // Actually change the state.
1982 ScanState old_state = scan_state_;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07001983 ScanMethod old_method = scan_method_;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001984 scan_state_ = new_state;
1985 scan_method_ = new_method;
1986 if (new_state == kScanScanning || old_state == kScanScanning) {
1987 Error error;
1988 adaptor()->EmitBoolChanged(flimflam::kScanningProperty,
1989 GetScanPending(&error));
1990 }
Wade Guthrie44f290d2013-05-28 10:16:25 -07001991 switch (new_state) {
1992 case kScanIdle:
1993 metrics()->ResetScanTimer(interface_index());
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001994 metrics()->ResetConnectTimer(interface_index());
1995 if (scan_session_) {
1996 scan_session_.reset();
1997 }
Wade Guthrie44f290d2013-05-28 10:16:25 -07001998 break;
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001999 case kScanScanning:
2000 metrics()->NotifyDeviceScanStarted(interface_index());
2001 break;
Wade Guthrie44f290d2013-05-28 10:16:25 -07002002 case kScanConnecting:
2003 metrics()->NotifyDeviceScanFinished(interface_index());
2004 // TODO(wdg): Provide |is_auto_connecting| to this interface. For now,
2005 // I'll lie (because I don't care about the auto-connect metrics).
2006 metrics()->NotifyDeviceConnectStarted(interface_index(), false);
2007 break;
2008 case kScanConnected:
2009 metrics()->NotifyDeviceConnectFinished(interface_index());
2010 break;
2011 case kScanFoundNothing:
2012 // Note that finishing a scan that hasn't started (if, for example, we
2013 // get here when we fail to complete a connection) does nothing.
2014 metrics()->NotifyDeviceScanFinished(interface_index());
2015 metrics()->ResetConnectTimer(interface_index());
2016 break;
Wade Guthriedf6d61b2013-07-17 11:43:55 -07002017 case kScanTransitionToConnecting: // FALLTHROUGH
Wade Guthrie44f290d2013-05-28 10:16:25 -07002018 default:
2019 break;
2020 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002021 if (is_terminal_state) {
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002022 ReportScanResultToUma(new_state, old_method);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002023 // Now that we've logged a terminal state, let's call ourselves to
2024 // transistion to the idle state.
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002025 SetScanState(kScanIdle, kScanMethodNone, reason);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002026 }
2027}
2028
2029// static
2030string WiFi::ScanStateString(ScanState state, ScanMethod method) {
2031 switch (state) {
2032 case kScanIdle:
2033 return "IDLE";
2034 case kScanScanning:
2035 DCHECK(method != kScanMethodNone) << "Scanning with no scan method.";
2036 switch (method) {
2037 case kScanMethodFull:
2038 return "FULL_START";
2039 case kScanMethodProgressive:
2040 return "PROGRESSIVE_START";
2041 case kScanMethodProgressiveErrorToFull:
2042 return "PROGRESSIVE_ERROR_FULL_START";
2043 case kScanMethodProgressiveFinishedToFull:
2044 return "PROGRESSIVE_FINISHED_FULL_START";
2045 default:
2046 NOTREACHED();
2047 }
Wade Guthriedf6d61b2013-07-17 11:43:55 -07002048 case kScanTransitionToConnecting:
2049 return "TRANSITION_TO_CONNECTING";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002050 case kScanConnecting:
2051 switch (method) {
2052 case kScanMethodNone:
2053 return "CONNECTING (not scan related)";
2054 case kScanMethodFull:
2055 return "FULL_CONNECTING";
2056 case kScanMethodProgressive:
2057 return "PROGRESSIVE_CONNECTING";
2058 case kScanMethodProgressiveErrorToFull:
2059 return "PROGRESSIVE_ERROR_FULL_CONNECTING";
2060 case kScanMethodProgressiveFinishedToFull:
2061 return "PROGRESSIVE_FINISHED_FULL_CONNECTING";
2062 default:
2063 NOTREACHED();
2064 }
2065 case kScanConnected:
2066 switch (method) {
2067 case kScanMethodNone:
2068 return "CONNECTED (not scan related; e.g., from a supplicant roam)";
2069 case kScanMethodFull:
2070 return "FULL_CONNECTED";
2071 case kScanMethodProgressive:
2072 return "PROGRESSIVE_CONNECTED";
2073 case kScanMethodProgressiveErrorToFull:
2074 return "PROGRESSIVE_ERROR_FULL_CONNECTED";
2075 case kScanMethodProgressiveFinishedToFull:
2076 return "PROGRESSIVE_FINISHED_FULL_CONNECTED";
2077 default:
2078 NOTREACHED();
2079 }
2080 case kScanFoundNothing:
2081 switch (method) {
2082 case kScanMethodNone:
2083 return "CONNECT FAILED (not scan related)";
2084 case kScanMethodFull:
2085 return "FULL_NOCONNECTION";
2086 case kScanMethodProgressive:
2087 // This is possible if shill started to connect but timed out before
2088 // the connection was completed.
2089 return "PROGRESSIVE_FINISHED_NOCONNECTION";
2090 case kScanMethodProgressiveErrorToFull:
2091 return "PROGRESSIVE_ERROR_FULL_NOCONNECTION";
2092 case kScanMethodProgressiveFinishedToFull:
2093 return "PROGRESSIVE_FINISHED_FULL_NOCONNECTION";
2094 default:
2095 NOTREACHED();
2096 }
2097 default:
2098 NOTREACHED();
2099 }
2100 return ""; // To shut up the compiler (that doesn't understand NOTREACHED).
2101}
2102
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07002103void WiFi::ReportScanResultToUma(ScanState state, ScanMethod method) {
2104 Metrics::WiFiScanResult result = Metrics::kScanResultMax;
2105 if (state == kScanConnected) {
2106 switch (method) {
2107 case kScanMethodFull:
2108 result = Metrics::kScanResultFullScanConnected;
2109 break;
2110 case kScanMethodProgressive:
2111 result = Metrics::kScanResultProgressiveConnected;
2112 break;
2113 case kScanMethodProgressiveErrorToFull:
2114 result = Metrics::kScanResultProgressiveErrorButFullConnected;
2115 break;
2116 case kScanMethodProgressiveFinishedToFull:
2117 result = Metrics::kScanResultProgressiveAndFullConnected;
2118 break;
2119 default:
2120 // OK: Connect resulting from something other than scan.
2121 break;
2122 }
2123 } else if (state == kScanFoundNothing) {
2124 switch (method) {
2125 case kScanMethodFull:
2126 result = Metrics::kScanResultFullScanFoundNothing;
2127 break;
2128 case kScanMethodProgressiveErrorToFull:
2129 result = Metrics::kScanResultProgressiveErrorAndFullFoundNothing;
2130 break;
2131 case kScanMethodProgressiveFinishedToFull:
2132 result = Metrics::kScanResultProgressiveAndFullFoundNothing;
2133 break;
2134 default:
2135 // OK: Connect failed, not scan related.
2136 break;
2137 }
2138 }
2139
2140 if (result != Metrics::kScanResultMax) {
2141 metrics()->SendEnumToUMA(Metrics::kMetricScanResult,
2142 result,
2143 Metrics::kScanResultMax);
2144 }
2145}
2146
Paul Stewartb50f0b92011-05-16 16:31:42 -07002147} // namespace shill