blob: 7593cf841836cf0c666878e78fd4f5bd89c12c7e [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>
Wade Guthrieb86860c2013-09-18 15:42:56 -070015#include <set>
Paul Stewartb50f0b92011-05-16 16:31:42 -070016#include <string>
mukesh agrawalab87ea42011-05-18 11:44:49 -070017#include <vector>
Paul Stewartb50f0b92011-05-16 16:31:42 -070018
Eric Shienbrood3e20a232012-02-16 11:35:56 -050019#include <base/bind.h>
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070020#include <base/file_path.h>
21#include <base/file_util.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070022#include <base/string_util.h>
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070023#include <base/stringprintf.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070024#include <chromeos/dbus/service_constants.h>
mukesh agrawal16bc1b82012-02-09 18:38:26 -080025#include <glib.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070026
27#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080028#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070029#include "shill/device.h"
Paul Stewart11c224b2013-10-22 19:04:40 -070030#include "shill/eap_credentials.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070031#include "shill/error.h"
Wade Guthrie2edd58b2013-05-23 11:16:08 -070032#include "shill/file_reader.h"
Gaurav Shah6d2c72d2012-10-16 16:30:44 -070033#include "shill/geolocation_info.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070034#include "shill/ieee80211.h"
Paul Stewart3c508e12012-08-09 11:40:06 -070035#include "shill/link_monitor.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070036#include "shill/logging.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070037#include "shill/manager.h"
Thieu Le67370f62012-02-14 23:01:42 +000038#include "shill/metrics.h"
Wade Guthriebb9fca22013-04-10 17:21:42 -070039#include "shill/netlink_manager.h"
Wade Guthrie7347bf22013-04-30 11:21:51 -070040#include "shill/netlink_message.h"
Wade Guthriebee87c22013-03-06 11:00:46 -080041#include "shill/nl80211_message.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080042#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070043#include "shill/proxy_factory.h"
Eric Shienbrood9a245532012-03-07 14:20:39 -050044#include "shill/rtnl_handler.h"
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070045#include "shill/scan_session.h"
Paul Stewart5581d072012-12-17 17:30:20 -080046#include "shill/scope_logger.h"
mukesh agrawal5c05b292012-03-07 10:12:52 -080047#include "shill/shill_time.h"
Paul Stewart735eab52013-03-29 09:19:23 -070048#include "shill/supplicant_eap_state_handler.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070049#include "shill/supplicant_interface_proxy_interface.h"
Paul Stewart835934a2012-12-06 19:27:09 -080050#include "shill/supplicant_network_proxy_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070051#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080052#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070053#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080054#include "shill/wifi_provider.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070055#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070056#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070057
Eric Shienbrood3e20a232012-02-16 11:35:56 -050058using base::Bind;
mukesh agrawal15908392011-11-16 18:29:25 +000059using base::StringPrintf;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070060using file_util::PathExists;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070061using std::map;
Wade Guthrieb86860c2013-09-18 15:42:56 -070062using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070063using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070064using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070065
Paul Stewartb50f0b92011-05-16 16:31:42 -070066namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070067
68// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080069const char *WiFi::kDefaultBgscanMethod =
Paul Stewart0654ece2013-03-26 15:21:26 -070070 WPASupplicant::kNetworkBgscanMethodSimple;
mukesh agrawal4d0401c2012-01-06 16:05:31 -080071const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
72const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
73const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
Wade Guthrie227c7742013-10-10 11:06:33 -070074const uint16 WiFi::kDefaultRoamThresholdDb = 18; // Supplicant's default.
Darin Petkov4a66cc52012-06-15 10:08:29 +020075// Scan interval while connected.
76const uint16 WiFi::kBackgroundScanIntervalSeconds = 3601;
mukesh agrawal5c05b292012-03-07 10:12:52 -080077// Age (in seconds) beyond which a BSS cache entry will not be preserved,
78// across a suspend/resume.
79const time_t WiFi::kMaxBSSResumeAgeSeconds = 10;
mukesh agrawal7ec71312011-11-10 02:08:26 +000080const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalf2028172012-03-13 14:20:22 -070081const time_t WiFi::kRescanIntervalSeconds = 1;
Paul Stewarte369ece2012-05-22 09:11:03 -070082const int WiFi::kNumFastScanAttempts = 3;
83const int WiFi::kFastScanIntervalSeconds = 10;
Paul Stewart2b05e622012-07-13 20:38:44 -070084const int WiFi::kPendingTimeoutSeconds = 15;
Paul Stewart44663922012-07-30 11:03:03 -070085const int WiFi::kReconnectTimeoutSeconds = 10;
Paul Stewart7cd45722013-08-12 14:50:14 -070086const int WiFi::kRequestStationInfoPeriodSeconds = 20;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -070087const size_t WiFi::kMinumumFrequenciesToScan = 4; // Arbitrary but > 0.
Wade Guthrie2edd58b2013-05-23 11:16:08 -070088const float WiFi::kDefaultFractionPerScan = 0.34;
Wade Guthrie2edd58b2013-05-23 11:16:08 -070089const char WiFi::kProgressiveScanFieldTrialFlagFile[] =
90 "/home/chronos/.progressive_scan_variation";
mukesh agrawalb54601c2011-06-07 17:39:22 -070091
Paul Stewartb50f0b92011-05-16 16:31:42 -070092WiFi::WiFi(ControlInterface *control_interface,
93 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080094 Metrics *metrics,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070095 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070096 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080097 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070098 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070099 : Device(control_interface,
100 dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -0800101 metrics,
Chris Masonea82b7112011-05-25 15:16:29 -0700102 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -0700103 link,
Chris Masone626719f2011-08-18 16:58:48 -0700104 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -0800105 interface_index,
106 Technology::kWifi),
Paul Stewart3c504012013-01-17 17:49:58 -0800107 provider_(manager->wifi_provider()),
Eric Shienbrood9a245532012-03-07 14:20:39 -0500108 weak_ptr_factory_(this),
Darin Petkovab565bb2011-10-06 02:55:51 -0700109 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800110 time_(Time::GetInstance()),
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200111 supplicant_present_(false),
mukesh agrawal15908392011-11-16 18:29:25 +0000112 supplicant_state_(kInterfaceStateUnknown),
113 supplicant_bss_("(unknown)"),
mukesh agrawal5c05b292012-03-07 10:12:52 -0800114 need_bss_flush_(false),
Wade Guthriebb9fca22013-04-10 17:21:42 -0700115 resumed_at_((struct timeval) {0}),
Paul Stewarte369ece2012-05-22 09:11:03 -0700116 fast_scans_remaining_(kNumFastScanAttempts),
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700117 has_already_completed_(false),
Paul Stewarta47c3c62012-12-18 12:14:29 -0800118 is_debugging_connection_(false),
Paul Stewart735eab52013-03-29 09:19:23 -0700119 eap_state_handler_(new SupplicantEAPStateHandler()),
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800120 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
121 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Wade Guthrie227c7742013-10-10 11:06:33 -0700122 roam_threshold_db_(kDefaultRoamThresholdDb),
Wade Guthriebee87c22013-03-06 11:00:46 -0800123 scan_interval_seconds_(kDefaultScanIntervalSeconds),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700124 progressive_scan_enabled_(false),
Wade Guthrie04fd0482013-07-25 10:43:01 -0700125 scan_configuration_("Full scan"),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700126 netlink_manager_(NetlinkManager::GetInstance()),
127 min_frequencies_to_scan_(kMinumumFrequenciesToScan),
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700128 max_frequencies_to_scan_(std::numeric_limits<int>::max()),
Wade Guthrieb86860c2013-09-18 15:42:56 -0700129 scan_all_frequencies_(true),
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700130 fraction_per_scan_(kDefaultFractionPerScan),
131 scan_state_(kScanIdle),
Paul Stewartf6f96482013-07-12 12:49:15 -0700132 scan_method_(kScanMethodNone),
133 receive_byte_count_at_connect_(0) {
mukesh agrawalde29fa82011-09-16 16:16:36 -0700134 PropertyStore *store = this->mutable_store();
Darin Petkov4a66cc52012-06-15 10:08:29 +0200135 store->RegisterDerivedString(
Ben Chanf024ef42013-09-20 14:21:38 -0700136 kBgscanMethodProperty,
Darin Petkov4a66cc52012-06-15 10:08:29 +0200137 StringAccessor(
138 // TODO(petkov): CustomMappedAccessor is used for convenience because
139 // it provides a way to define a custom clearer (unlike
140 // CustomAccessor). We need to implement a fully custom accessor with
141 // no extra argument.
142 new CustomMappedAccessor<WiFi, string, int>(this,
143 &WiFi::ClearBgscanMethod,
144 &WiFi::GetBgscanMethod,
145 &WiFi::SetBgscanMethod,
146 0))); // Unused.
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800147 HelpRegisterDerivedUint16(store,
Ben Chanf024ef42013-09-20 14:21:38 -0700148 kBgscanShortIntervalProperty,
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800149 &WiFi::GetBgscanShortInterval,
150 &WiFi::SetBgscanShortInterval);
151 HelpRegisterDerivedInt32(store,
Ben Chanf024ef42013-09-20 14:21:38 -0700152 kBgscanSignalThresholdProperty,
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800153 &WiFi::GetBgscanSignalThreshold,
154 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700155
Paul Stewartbaf87072013-10-04 17:03:37 -0700156 store->RegisterDerivedKeyValueStore(
157 kLinkStatisticsProperty,
158 KeyValueStoreAccessor(
159 new CustomAccessor<WiFi, KeyValueStore>(
160 this, &WiFi::GetLinkStatistics, NULL)));
161
Chris Masoneb925cc82011-06-22 15:39:57 -0700162 // TODO(quiche): Decide if scan_pending_ is close enough to
163 // "currently scanning" that we don't care, or if we want to track
164 // scan pending/currently scanning/no scan scheduled as a tri-state
165 // kind of thing.
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700166 HelpRegisterConstDerivedBool(store,
Ben Chanf024ef42013-09-20 14:21:38 -0700167 kScanningProperty,
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700168 &WiFi::GetScanPending);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800169 HelpRegisterDerivedUint16(store,
Wade Guthrie227c7742013-10-10 11:06:33 -0700170 kRoamThresholdProperty,
171 &WiFi::GetRoamThreshold,
172 &WiFi::SetRoamThreshold);
173 HelpRegisterDerivedUint16(store,
Ben Chanf024ef42013-09-20 14:21:38 -0700174 kScanIntervalProperty,
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800175 &WiFi::GetScanInterval,
176 &WiFi::SetScanInterval);
Paul Stewart5581d072012-12-17 17:30:20 -0800177 ScopeLogger::GetInstance()->RegisterScopeEnableChangedCallback(
178 ScopeLogger::kWiFi,
179 Bind(&WiFi::OnWiFiDebugScopeChanged, weak_ptr_factory_.GetWeakPtr()));
Wade Guthriebb9fca22013-04-10 17:21:42 -0700180 CHECK(netlink_manager_);
Wade Guthrieb86860c2013-09-18 15:42:56 -0700181 // TODO(wdg): Remove after progressive scan field trial is over.
182 // Only do the field trial if the user hasn't already enabled progressive
183 // scan manually. crbug.com/250945
184 ParseFieldTrialFile(FilePath(kProgressiveScanFieldTrialFlagFile));
Ben Chanfad4a0b2012-04-18 15:49:59 -0700185 SLOG(WiFi, 2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700186}
187
mukesh agrawalaf571952011-07-14 14:31:12 -0700188WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700189
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700190void WiFi::ParseFieldTrialFile(const FilePath &info_file_path) {
191 FileReader file_reader;
192 if (!file_reader.Open(info_file_path)) {
193 SLOG(WiFi, 7) << "Not enrolled in progressive scan field trial.";
194 return;
195 }
196 string line;
197 file_reader.ReadLine(&line);
198 switch (line[0]) {
199 case '1':
Wade Guthrie58742e92013-10-03 11:24:04 -0700200 case '2':
Wade Guthriefe16d542013-09-19 10:30:52 -0700201 // The minimum and maximum are the same (which makes the fraction
202 // irrelevant). Every scan batch (except, possibly, the last) contains
203 // exactly 4 frequencies. These cases is optimized for users that use
204 // connect to a few frequencies or that heavily prefer the top 4.
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700205 min_frequencies_to_scan_ = 4;
206 max_frequencies_to_scan_ = 4;
207 fraction_per_scan_ = .34;
208 progressive_scan_enabled_ = true;
Wade Guthrie58742e92013-10-03 11:24:04 -0700209 scan_configuration_ = "Progressive scan (field trial 1/2: min/max=4)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700210 break;
211 case '3':
Wade Guthrie58742e92013-10-03 11:24:04 -0700212 case '4':
Wade Guthriefe16d542013-09-19 10:30:52 -0700213 // The minimum and maximum are the same (which makes the fraction
214 // irrelevant). Every scan batch (except, possibly, the last) contains
215 // exactly 8 frequencies. These cases is optimized for users that use
216 // several frequencies, each with similar likelihood.
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700217 min_frequencies_to_scan_ = 8;
218 max_frequencies_to_scan_ = 8;
219 fraction_per_scan_ = .51;
220 progressive_scan_enabled_ = true;
Wade Guthrie58742e92013-10-03 11:24:04 -0700221 scan_configuration_ = "Progressive scan (field trial 3/4: min/max=8)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700222 break;
Wade Guthriefe16d542013-09-19 10:30:52 -0700223 case '5':
224 case '6':
225 // Does a single scan, only of previously-seen frequencies. The idea is
226 // that, in nearly all cases, we'll find a good BSS in a scan of all
227 // previously seen frequencies and that, since about 75% of the users
228 // (based on preliminary field trial data) have seen less than 6 or 7
229 // frequencies and 50% (based on the same data) have less than 4, 'all
230 // frequencies' is not too large of a group in the worst case and is a
231 // pretty small group in more then half the cases. Note that if we don't
232 // find a BSS in a scan, the code falls back to a complete scan. This
233 // algorithm is represented by two identical groups to help determine
234 // whether the size of the field trial groups are large enough to make the
235 // results statistically significant.
236 min_frequencies_to_scan_ = 1;
237 max_frequencies_to_scan_ = std::numeric_limits<int>::max();
238 fraction_per_scan_ = 1.1;
239 scan_all_frequencies_ = false;
240 progressive_scan_enabled_ = true;
241 scan_configuration_ = (line[0] == '5') ?
242 "Progressive scan (field trial 5: min=1/max=all, 100%, only-seen)" :
243 "Progressive scan (field trial 6: min=1/max=all, 100%, only-seen)";
244 break;
245 case '7':
246 // Uses different min/max values. This allows machines that have a very
247 // small set of previously-seen frequencies to have very short scan
248 // times, machines that have a large set of previously-seen frequencies
249 // to have their scans broken up to try to find a BSS without searching
250 // all of those frequencies, and scans that don't find anything in the
251 // previously-seen list to scan just the frequencies that haven't just
252 // been scanned.
253 min_frequencies_to_scan_ = 1;
254 max_frequencies_to_scan_ = 4;
255 // This is 1.0 rather than 1.1 so that we only get previously seen
256 // frequencies until they are exhausted.
257 fraction_per_scan_ = 1.0;
258 progressive_scan_enabled_ = true;
259 scan_configuration_ =
260 "Progressive scan (field trial 7: min=1/max=4, 100%)";
261 break;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700262 case 'c':
Wade Guthriefe16d542013-09-19 10:30:52 -0700263 // This is the control group; it uses traditional, full, scan. It's the
264 // same size as the other test groups.
Wade Guthrie04fd0482013-07-25 10:43:01 -0700265 progressive_scan_enabled_ = false;
266 scan_configuration_ = "Full scan (field trial c: control group)";
267 break;
268 case 'x':
Wade Guthriefe16d542013-09-19 10:30:52 -0700269 // This is the non-test group; it uses traditional, full, scan. It
270 // contains all users that aren't in one of the test groups.
Wade Guthrie04fd0482013-07-25 10:43:01 -0700271 progressive_scan_enabled_ = false;
272 scan_configuration_ = "Full scan (field trial x: default/disabled group)";
273 break;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700274 default:
275 progressive_scan_enabled_ = false;
Wade Guthriefe16d542013-09-19 10:30:52 -0700276 scan_configuration_ = "Full scan (field trial unknown)";
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700277 break;
278 }
279 LOG(INFO) << "Progressive scan (via field_trial) "
280 << (progressive_scan_enabled_ ? "enabled" : "disabled");
281 if (progressive_scan_enabled_) {
282 LOG(INFO) << " min_frequencies_to_scan_ = " << min_frequencies_to_scan_;
283 LOG(INFO) << " max_frequencies_to_scan_ = " << max_frequencies_to_scan_;
284 LOG(INFO) << " fraction_per_scan_ = " << fraction_per_scan_;
285 }
286
287 file_reader.Close();
288}
289
mukesh agrawal6813e762013-07-10 19:05:08 -0700290void WiFi::Start(Error *error,
291 const EnabledStateChangedCallback &/*callback*/) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200292 SLOG(WiFi, 2) << "WiFi " << link_name() << " starting.";
293 if (enabled()) {
294 return;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700295 }
Eric Shienbrood9a245532012-03-07 14:20:39 -0500296 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200297 if (error) {
Eric Shienbrood9a245532012-03-07 14:20:39 -0500298 error->Reset(); // indicate immediate completion
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200299 }
300 if (on_supplicant_appear_.IsCancelled()) {
301 // Registers the WPA supplicant appear/vanish callbacks only once per WiFi
302 // device instance.
303 on_supplicant_appear_.Reset(
304 Bind(&WiFi::OnSupplicantAppear, Unretained(this)));
305 on_supplicant_vanish_.Reset(
306 Bind(&WiFi::OnSupplicantVanish, Unretained(this)));
Paul Stewart0654ece2013-03-26 15:21:26 -0700307 manager()->dbus_manager()->WatchName(WPASupplicant::kDBusAddr,
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200308 on_supplicant_appear_.callback(),
309 on_supplicant_vanish_.callback());
310 }
Wade Guthriebee87c22013-03-06 11:00:46 -0800311 // Subscribe to multicast events.
Wade Guthriebb9fca22013-04-10 17:21:42 -0700312 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
313 NetlinkManager::kEventTypeConfig);
314 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
315 NetlinkManager::kEventTypeScan);
316 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
317 NetlinkManager::kEventTypeRegulatory);
318 netlink_manager_->SubscribeToEvents(Nl80211Message::kMessageTypeString,
319 NetlinkManager::kEventTypeMlme);
Wade Guthrie92d06362013-04-25 15:41:30 -0700320 ConfigureScanFrequencies();
Wade Guthrie0cf3c982013-05-29 09:11:35 -0700321 // Connect to WPA supplicant if it's already present. If not, we'll connect to
322 // it when it appears.
323 ConnectToSupplicant();
mukesh agrawalab87ea42011-05-18 11:44:49 -0700324}
325
mukesh agrawal6813e762013-07-10 19:05:08 -0700326void WiFi::Stop(Error *error, const EnabledStateChangedCallback &/*callback*/) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700327 SLOG(WiFi, 2) << "WiFi " << link_name() << " stopping.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +0200328 DropConnection();
mukesh agrawalb66c6462012-05-07 11:45:25 -0700329 StopScanTimer();
Paul Stewart3c504012013-01-17 17:49:58 -0800330 for (EndpointMap::iterator it = endpoint_by_rpcid_.begin();
331 it != endpoint_by_rpcid_.end(); ++it) {
332 provider_->OnEndpointRemoved(it->second);
333 }
mukesh agrawal15908392011-11-16 18:29:25 +0000334 endpoint_by_rpcid_.clear();
Paul Stewart3c504012013-01-17 17:49:58 -0800335 for (ReverseServiceMap::const_iterator it = rpcid_by_service_.begin();
336 it != rpcid_by_service_.end(); ++it) {
337 RemoveNetwork(it->second);
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700338 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700339 rpcid_by_service_.clear();
Paul Stewart549d44c2012-07-03 12:40:25 -0700340 supplicant_interface_proxy_.reset(); // breaks a reference cycle
341 // TODO(quiche): Remove interface from supplicant.
342 supplicant_process_proxy_.reset();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800343 current_service_ = NULL; // breaks a reference cycle
mukesh agrawal7ec71312011-11-10 02:08:26 +0000344 pending_service_ = NULL; // breaks a reference cycle
Paul Stewarta47c3c62012-12-18 12:14:29 -0800345 is_debugging_connection_ = false;
Wade Guthrieb0def9f2013-07-12 13:49:18 -0700346 SetScanState(kScanIdle, kScanMethodNone, __func__);
Paul Stewart2b05e622012-07-13 20:38:44 -0700347 StopPendingTimer();
Paul Stewart44663922012-07-30 11:03:03 -0700348 StopReconnectTimer();
Paul Stewart7cd45722013-08-12 14:50:14 -0700349 StopRequestingStationInfo();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700350
Eric Shienbrood9a245532012-03-07 14:20:39 -0500351 OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
352 if (error)
353 error->Reset(); // indicate immediate completion
mukesh agrawalc4f368f2012-06-04 19:45:52 -0700354 weak_ptr_factory_.InvalidateWeakPtrs();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700355
Ben Chanfad4a0b2012-04-18 15:49:59 -0700356 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
357 << (supplicant_process_proxy_.get() ?
358 "is set." : "is not set.");
359 SLOG(WiFi, 3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
360 << (supplicant_interface_proxy_.get() ?
361 "is set." : "is not set.");
362 SLOG(WiFi, 3) << "WiFi " << link_name() << " pending_service_ "
363 << (pending_service_.get() ? "is set." : "is not set.");
364 SLOG(WiFi, 3) << "WiFi " << link_name() << " has "
365 << endpoint_by_rpcid_.size() << " EndpointMap entries.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800366}
367
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700368void WiFi::Scan(ScanType scan_type, Error */*error*/, const string &reason) {
Wade Guthrie2ef88ad2013-07-29 15:14:18 -0700369 if ((scan_state_ != kScanIdle) ||
370 (current_service_.get() && current_service_->IsConnecting())) {
371 SLOG(WiFi, 2) << "Ignoring scan request while scanning or connecting.";
372 return;
373 }
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700374 if (progressive_scan_enabled_ && scan_type == kProgressiveScan) {
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700375 LOG(INFO) << __func__ << " [progressive] on " << link_name() << " from "
376 << reason;
Wade Guthrie04fd0482013-07-25 10:43:01 -0700377 LOG(INFO) << scan_configuration_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700378 if (!scan_session_) {
379 // TODO(wdg): Perform in-depth testing to determine the best values for
380 // the different scans. chromium:235293
381 ScanSession::FractionList scan_fractions;
Wade Guthrie2edd58b2013-05-23 11:16:08 -0700382 float total_fraction = 0.0;
383 do {
384 total_fraction += fraction_per_scan_;
385 scan_fractions.push_back(fraction_per_scan_);
386 } while (total_fraction < 1.0);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700387 scan_session_.reset(
388 new ScanSession(netlink_manager_,
389 dispatcher(),
390 provider_->GetScanFrequencies(),
Wade Guthrieb86860c2013-09-18 15:42:56 -0700391 (scan_all_frequencies_ ? all_scan_frequencies_ :
392 set<uint16_t>()),
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700393 interface_index(),
394 scan_fractions,
395 min_frequencies_to_scan_,
396 max_frequencies_to_scan_,
397 Bind(&WiFi::OnFailedProgressiveScan,
Wade Guthrief22681f2013-05-31 11:46:31 -0700398 weak_ptr_factory_.GetWeakPtr()),
399 metrics()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700400 for (const auto &ssid : provider_->GetHiddenSSIDList()) {
401 scan_session_->AddSsid(ByteString(&ssid.front(), ssid.size()));
402 }
403 }
404 dispatcher()->PostTask(
405 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
406 } else {
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700407 LOG(INFO) << __func__ << " [full] on " << link_name()
408 << " (progressive scan "
409 << (progressive_scan_enabled_ ? "ENABLED" : "DISABLED")
410 << ") from " << reason;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -0700411 // Needs to send a D-Bus message, but may be called from D-Bus
412 // signal handler context (via Manager::RequestScan). So defer work
413 // to event loop.
414 dispatcher()->PostTask(
415 Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
416 }
mukesh agrawal32399322011-09-01 10:53:43 -0700417}
418
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000419void WiFi::BSSAdded(const ::DBus::Path &path,
420 const map<string, ::DBus::Variant> &properties) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500421 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000422 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500423 dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
424 weak_ptr_factory_.GetWeakPtr(),
425 path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000426}
427
428void WiFi::BSSRemoved(const ::DBus::Path &path) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500429 // Called from a D-Bus signal handler, and may need to send a D-Bus
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000430 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500431 dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
432 weak_ptr_factory_.GetWeakPtr(), path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700433}
434
Paul Stewartbc6e7392012-05-24 07:07:48 -0700435void WiFi::Certification(const map<string, ::DBus::Variant> &properties) {
436 dispatcher()->PostTask(Bind(&WiFi::CertificationTask,
437 weak_ptr_factory_.GetWeakPtr(), properties));
438}
439
Paul Stewartdb0f9172012-11-30 16:48:09 -0800440void WiFi::EAPEvent(const string &status, const string &parameter) {
441 dispatcher()->PostTask(Bind(&WiFi::EAPEventTask,
442 weak_ptr_factory_.GetWeakPtr(),
443 status,
444 parameter));
445}
446
mukesh agrawal7ec71312011-11-10 02:08:26 +0000447void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200448 SLOG(WiFi, 2) << __func__;
mukesh agrawal15908392011-11-16 18:29:25 +0000449 // Called from D-Bus signal handler, but may need to send a D-Bus
450 // message. So defer work to event loop.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500451 dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
452 weak_ptr_factory_.GetWeakPtr(), properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000453}
454
mukesh agrawalb54601c2011-06-07 17:39:22 -0700455void WiFi::ScanDone() {
456 LOG(INFO) << __func__;
457
mukesh agrawal7ec71312011-11-10 02:08:26 +0000458 // Defer handling of scan result processing, because that processing
459 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700460 // registration can't be done in the context of a D-Bus signal
461 // handler.
Eric Shienbrood9a245532012-03-07 14:20:39 -0500462 dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
463 weak_ptr_factory_.GetWeakPtr()));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700464}
465
Paul Stewarta283e4e2013-10-22 20:50:14 -0700466void WiFi::ConnectTo(WiFiService *service) {
mukesh agrawale9adda12012-02-09 18:33:48 -0800467 CHECK(service) << "Can't connect to NULL service.";
mukesh agrawal445e72c2011-06-22 11:13:50 -0700468 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700469
mukesh agrawal7ec71312011-11-10 02:08:26 +0000470 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000471 if (pending_service_ && pending_service_ == service) {
Paul Stewartee6b3d72013-07-12 16:07:51 -0700472 // TODO(quiche): Return an error to the caller. crbug.com/206812
Darin Petkov457728b2013-01-09 09:49:08 +0100473 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo service "
474 << service->unique_name()
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000475 << ", which is already pending.";
476 return;
477 }
478
479 if (pending_service_ && pending_service_ != service) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700480 // This is a signal to SetPendingService(NULL) to not modify the scan
481 // state since the overall story arc isn't reflected by the disconnect.
482 // It is, instead, described by the transition to either kScanFoundNothing
483 // or kScanConnecting (made by |SetPendingService|, below).
484 if (scan_method_ != kScanMethodNone) {
485 SetScanState(kScanTransitionToConnecting, scan_method_, __func__);
486 }
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000487 DisconnectFrom(pending_service_);
488 }
mukesh agrawal32399322011-09-01 10:53:43 -0700489
Paul Stewart835934a2012-12-06 19:27:09 -0800490 Error unused_error;
491 network_path = FindNetworkRpcidForService(service, &unused_error);
492 if (network_path.empty()) {
493 try {
Paul Stewarta283e4e2013-10-22 20:50:14 -0700494 DBusPropertiesMap service_params =
495 service->GetSupplicantConfigurationParameters();
Paul Stewart835934a2012-12-06 19:27:09 -0800496 const uint32_t scan_ssid = 1; // "True": Use directed probe.
Paul Stewart0654ece2013-03-26 15:21:26 -0700497 service_params[WPASupplicant::kNetworkPropertyScanSSID].writer().
Paul Stewart835934a2012-12-06 19:27:09 -0800498 append_uint32(scan_ssid);
499 AppendBgscan(service, &service_params);
500 network_path = supplicant_interface_proxy_->AddNetwork(service_params);
501 CHECK(!network_path.empty()); // No DBus path should be empty.
502 rpcid_by_service_[service] = network_path;
503 } catch (const DBus::Error &e) { // NOLINT
504 LOG(ERROR) << "exception while adding network: " << e.what();
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700505 SetScanState(kScanIdle, scan_method_, __func__);
Paul Stewart835934a2012-12-06 19:27:09 -0800506 return;
507 }
mukesh agrawal6e277772011-09-29 15:04:23 -0700508 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700509
Paul Stewarta47c3c62012-12-18 12:14:29 -0800510 if (service->HasRecentConnectionIssues()) {
511 SetConnectionDebugging(true);
512 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700513 supplicant_interface_proxy_->SelectNetwork(network_path);
Paul Stewart2b05e622012-07-13 20:38:44 -0700514 SetPendingService(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000515 CHECK(current_service_.get() != pending_service_.get());
516
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700517 // SelectService here (instead of in LinkEvent, like Ethernet), so
518 // that, if we fail to bring up L2, we can attribute failure correctly.
519 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000520 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700521 // reconsider if this is the right place to change the selected service.
Paul Stewartee6b3d72013-07-12 16:07:51 -0700522 // see discussion in crbug.com/203282.
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700523 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000524}
525
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000526void WiFi::DisconnectFrom(WiFiService *service) {
527 if (service != current_service_ && service != pending_service_) {
528 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700529 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000530 LOG(WARNING) << "In " << __func__ << "(): "
531 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100532 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000533 << " which is neither current nor pending";
534 return;
535 }
536
537 if (pending_service_ && service != pending_service_) {
538 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700539 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000540 LOG(WARNING) << "In " << __func__ << "(): "
541 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100542 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000543 << " which is not the pending service.";
544 return;
545 }
546
547 if (!pending_service_ && service != current_service_) {
548 // TODO(quiche): Once we have asynchronous reply support, we should
Paul Stewartee6b3d72013-07-12 16:07:51 -0700549 // generate a D-Bus error here. (crbug.com/206812)
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000550 LOG(WARNING) << "In " << __func__ << "(): "
551 << " ignoring request to disconnect from service "
Darin Petkov457728b2013-01-09 09:49:08 +0100552 << service->unique_name()
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000553 << " which is not the current service.";
554 return;
555 }
556
Paul Stewartff96a842012-08-13 15:59:10 -0700557 if (pending_service_) {
558 // Since wpa_supplicant has not yet set CurrentBSS, we can't depend
559 // on this to drive the service state back to idle. Do that here.
560 pending_service_->SetState(Service::kStateIdle);
561 }
562
Paul Stewart2b05e622012-07-13 20:38:44 -0700563 SetPendingService(NULL);
Paul Stewart44663922012-07-30 11:03:03 -0700564 StopReconnectTimer();
Paul Stewart7cd45722013-08-12 14:50:14 -0700565 StopRequestingStationInfo();
Paul Stewart549d44c2012-07-03 12:40:25 -0700566
567 if (!supplicant_present_) {
Christopher Wileyc6184482012-10-24 15:31:56 -0700568 LOG(ERROR) << "In " << __func__ << "(): "
569 << "wpa_supplicant is not present; silently resetting "
570 << "current_service_.";
571 if (current_service_ == selected_service()) {
572 DropConnection();
573 }
Paul Stewart549d44c2012-07-03 12:40:25 -0700574 current_service_ = NULL;
575 return;
576 }
577
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000578 try {
579 supplicant_interface_proxy_->Disconnect();
580 // We'll call RemoveNetwork and reset |current_service_| after
581 // supplicant notifies us that the CurrentBSS has changed.
Ben Chan80326f32012-05-04 17:51:32 -0700582 } catch (const DBus::Error &e) { // NOLINT
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000583 // Can't depend on getting a notification of CurrentBSS change.
Christopher Wileyc6184482012-10-24 15:31:56 -0700584 // So effect changes immediately. For instance, this can happen when
585 // a disconnect is triggered by a BSS going away.
Paul Stewart835934a2012-12-06 19:27:09 -0800586 Error unused_error;
587 RemoveNetworkForService(service, &unused_error);
Christopher Wileyc6184482012-10-24 15:31:56 -0700588 if (service == selected_service()) {
589 DropConnection();
590 }
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000591 current_service_ = NULL;
592 }
593
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800594 CHECK(current_service_ == NULL ||
595 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000596}
597
Paul Stewart835934a2012-12-06 19:27:09 -0800598bool WiFi::DisableNetwork(const ::DBus::Path &network) {
599 scoped_ptr<SupplicantNetworkProxyInterface> supplicant_network_proxy(
600 proxy_factory_->CreateSupplicantNetworkProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -0700601 network, WPASupplicant::kDBusAddr));
Paul Stewart835934a2012-12-06 19:27:09 -0800602 try {
603 supplicant_network_proxy->SetEnabled(false);
604 } catch (const DBus::Error &e) { // NOLINT
605 LOG(ERROR) << "DisableNetwork for " << network << " failed.";
606 return false;
607 }
608 return true;
609}
610
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700611bool WiFi::RemoveNetwork(const ::DBus::Path &network) {
612 try {
613 supplicant_interface_proxy_->RemoveNetwork(network);
614 } catch (const DBus::Error &e) { // NOLINT
Ben Chan381fdcc2012-10-14 21:10:36 -0700615 // RemoveNetwork can fail with three different errors.
616 //
617 // If RemoveNetwork fails with a NetworkUnknown error, supplicant has
618 // already removed the network object, so return true as if
619 // RemoveNetwork removes the network object successfully.
620 //
621 // As shill always passes a valid network object path, RemoveNetwork
622 // should not fail with an InvalidArgs error. Return false in such case
623 // as something weird may have happened. Similarly, return false in case
624 // of an UnknownError.
Paul Stewart0654ece2013-03-26 15:21:26 -0700625 if (strcmp(e.name(), WPASupplicant::kErrorNetworkUnknown) != 0) {
Ben Chan381fdcc2012-10-14 21:10:36 -0700626 return false;
627 }
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700628 }
629 return true;
630}
631
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000632bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800633 return !current_service_ && !pending_service_;
634}
635
Paul Stewart835934a2012-12-06 19:27:09 -0800636void WiFi::ClearCachedCredentials(const WiFiService *service) {
637 Error unused_error;
638 RemoveNetworkForService(service, &unused_error);
Paul Stewart66c86002012-01-30 18:00:52 -0800639}
640
Paul Stewart3c504012013-01-17 17:49:58 -0800641void WiFi::NotifyEndpointChanged(const WiFiEndpointConstRefPtr &endpoint) {
Paul Stewart0427cc12013-03-25 13:50:39 -0700642 provider_->OnEndpointUpdated(endpoint);
mukesh agrawalb20776f2012-02-10 16:00:36 -0800643}
644
Darin Petkov4a66cc52012-06-15 10:08:29 +0200645void WiFi::AppendBgscan(WiFiService *service,
646 map<string, DBus::Variant> *service_params) const {
647 int scan_interval = kBackgroundScanIntervalSeconds;
648 string method = bgscan_method_;
649 if (method.empty()) {
650 // If multiple APs are detected for this SSID, configure the default method.
651 // Otherwise, disable background scanning completely.
652 if (service->GetEndpointCount() > 1) {
653 method = kDefaultBgscanMethod;
654 } else {
655 LOG(INFO) << "Background scan disabled -- single Endpoint for Service.";
656 return;
657 }
Paul Stewart0654ece2013-03-26 15:21:26 -0700658 } else if (method.compare(WPASupplicant::kNetworkBgscanMethodNone) == 0) {
Christopher Wileya998df22012-07-11 15:14:55 -0700659 LOG(INFO) << "Background scan disabled -- chose None method.";
660 return;
Darin Petkov4a66cc52012-06-15 10:08:29 +0200661 } else {
662 // If the background scan method was explicitly specified, honor the
663 // configured background scan interval.
664 scan_interval = scan_interval_seconds_;
665 }
666 DCHECK(!method.empty());
667 string config_string = StringPrintf("%s:%d:%d:%d",
668 method.c_str(),
669 bgscan_short_interval_seconds_,
670 bgscan_signal_threshold_dbm_,
671 scan_interval);
672 LOG(INFO) << "Background scan: " << config_string;
Paul Stewart0654ece2013-03-26 15:21:26 -0700673 (*service_params)[WPASupplicant::kNetworkPropertyBgscan].writer()
Darin Petkov4a66cc52012-06-15 10:08:29 +0200674 .append_string(config_string.c_str());
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800675}
676
Darin Petkov4a66cc52012-06-15 10:08:29 +0200677string WiFi::GetBgscanMethod(const int &/*argument*/, Error */* error */) {
678 return bgscan_method_.empty() ? kDefaultBgscanMethod : bgscan_method_;
679}
680
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700681bool WiFi::SetBgscanMethod(
Darin Petkov4a66cc52012-06-15 10:08:29 +0200682 const int &/*argument*/, const string &method, Error *error) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700683 if (method != WPASupplicant::kNetworkBgscanMethodSimple &&
684 method != WPASupplicant::kNetworkBgscanMethodLearn &&
685 method != WPASupplicant::kNetworkBgscanMethodNone) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800686 const string error_message =
687 StringPrintf("Unrecognized bgscan method %s", method.c_str());
688 LOG(WARNING) << error_message;
689 error->Populate(Error::kInvalidArguments, error_message);
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700690 return false;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800691 }
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700692 if (bgscan_method_ == method) {
693 return false;
694 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800695 bgscan_method_ = method;
696 // We do not update kNetworkPropertyBgscan for |pending_service_| or
697 // |current_service_|, because supplicant does not allow for
698 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700699 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800700}
701
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700702bool WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
703 if (bgscan_short_interval_seconds_ == seconds) {
704 return false;
705 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800706 bgscan_short_interval_seconds_ = seconds;
707 // We do not update kNetworkPropertyBgscan for |pending_service_| or
708 // |current_service_|, because supplicant does not allow for
709 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700710 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800711}
712
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700713bool WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
714 if (bgscan_signal_threshold_dbm_ == dbm) {
715 return false;
716 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800717 bgscan_signal_threshold_dbm_ = dbm;
718 // We do not update kNetworkPropertyBgscan for |pending_service_| or
719 // |current_service_|, because supplicant does not allow for
720 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700721 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800722}
723
Wade Guthrie227c7742013-10-10 11:06:33 -0700724bool WiFi::SetRoamThreshold(const uint16 &threshold, Error */*error*/) {
725 roam_threshold_db_ = threshold;
726 supplicant_interface_proxy_->SetRoamThreshold(threshold);
727 return true;
728}
729
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700730bool WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
731 if (scan_interval_seconds_ == seconds) {
732 return false;
733 }
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800734 scan_interval_seconds_ = seconds;
mukesh agrawalb66c6462012-05-07 11:45:25 -0700735 if (running()) {
736 StartScanTimer();
737 }
738 // The scan interval affects both foreground scans (handled by
739 // |scan_timer_callback_|), and background scans (handled by
740 // supplicant). However, we do not update |pending_service_| or
741 // |current_service_|, because supplicant does not allow for
742 // reconfiguration without disconnect and reconnect.
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700743 return true;
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800744}
745
Darin Petkov4a66cc52012-06-15 10:08:29 +0200746void WiFi::ClearBgscanMethod(const int &/*argument*/, Error */*error*/) {
747 bgscan_method_.clear();
748}
749
mukesh agrawal15908392011-11-16 18:29:25 +0000750void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700751 SLOG(WiFi, 3) << "WiFi " << link_name() << " CurrentBSS "
752 << supplicant_bss_ << " -> " << new_bss;
mukesh agrawal15908392011-11-16 18:29:25 +0000753 supplicant_bss_ = new_bss;
Christopher Wiley8f81e2a2012-10-17 16:51:32 -0700754 has_already_completed_ = false;
Paul Stewart44663922012-07-30 11:03:03 -0700755
756 // Any change in CurrentBSS means supplicant is actively changing our
757 // connectivity. We no longer need to track any previously pending
758 // reconnect.
759 StopReconnectTimer();
Paul Stewart7cd45722013-08-12 14:50:14 -0700760 StopRequestingStationInfo();
Paul Stewart44663922012-07-30 11:03:03 -0700761
Paul Stewart0654ece2013-03-26 15:21:26 -0700762 if (new_bss == WPASupplicant::kCurrentBSSNull) {
mukesh agrawal15908392011-11-16 18:29:25 +0000763 HandleDisconnect();
Paul Stewart3c504012013-01-17 17:49:58 -0800764 if (!provider_->GetHiddenSSIDList().empty()) {
mukesh agrawalb66c6462012-05-07 11:45:25 -0700765 // Before disconnecting, wpa_supplicant probably scanned for
766 // APs. So, in the normal case, we defer to the timer for the next scan.
767 //
768 // However, in the case of hidden SSIDs, supplicant knows about
769 // at most one of them. (That would be the hidden SSID we were
770 // connected to, if applicable.)
771 //
772 // So, in this case, we initiate an immediate scan. This scan
773 // will include the hidden SSIDs we know about (up to the limit of
774 // kScanMAxSSIDsPerScan).
775 //
776 // We may want to reconsider this immediate scan, if/when shill
777 // takes greater responsibility for scanning (vs. letting
778 // supplicant handle most of it).
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700779 Scan(kProgressiveScan, NULL, __func__);
mukesh agrawalb66c6462012-05-07 11:45:25 -0700780 }
mukesh agrawal15908392011-11-16 18:29:25 +0000781 } else {
782 HandleRoam(new_bss);
783 }
784
Paul Stewart735eab52013-03-29 09:19:23 -0700785 // Reset the EAP handler only after calling HandleDisconnect() above
786 // so our EAP state could be used to detect a failed authentication.
787 eap_state_handler_->Reset();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800788
Paul Stewart2b05e622012-07-13 20:38:44 -0700789 // If we are selecting a new service, or if we're clearing selection
790 // of a something other than the pending service, call SelectService.
791 // Otherwise skip SelectService, since this will cause the pending
792 // service to be marked as Idle.
793 if (current_service_ || selected_service() != pending_service_) {
794 SelectService(current_service_);
795 }
796
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000797 // Invariant check: a Service can either be current, or pending, but
798 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000799 CHECK(current_service_.get() != pending_service_.get() ||
800 current_service_.get() == NULL);
Paul Stewarta47c3c62012-12-18 12:14:29 -0800801
802 // If we are no longer debugging a problematic WiFi connection, return
803 // to the debugging level indicated by the WiFi debugging scope.
804 if ((!current_service_ || !current_service_->HasRecentConnectionIssues()) &&
805 (!pending_service_ || !pending_service_->HasRecentConnectionIssues())) {
806 SetConnectionDebugging(false);
807 }
mukesh agrawal15908392011-11-16 18:29:25 +0000808}
809
810void WiFi::HandleDisconnect() {
811 // Identify the affected service. We expect to get a disconnect
812 // event when we fall off a Service that we were connected
813 // to. However, we also allow for the case where we get a disconnect
814 // event while attempting to connect from a disconnected state.
815 WiFiService *affected_service =
816 current_service_.get() ? current_service_.get() : pending_service_.get();
817
818 current_service_ = NULL;
819 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700820 SLOG(WiFi, 2) << "WiFi " << link_name()
821 << " disconnected while not connected or connecting";
mukesh agrawal15908392011-11-16 18:29:25 +0000822 return;
Christopher Wileyc6184482012-10-24 15:31:56 -0700823 }
824 if (affected_service == selected_service()) {
Paul Stewart20b0a092012-05-22 20:39:57 -0700825 // If our selected service has disconnected, destroy IP configuration state.
Christopher Wileyc6184482012-10-24 15:31:56 -0700826 DropConnection();
mukesh agrawal15908392011-11-16 18:29:25 +0000827 }
828
Paul Stewart835934a2012-12-06 19:27:09 -0800829 Error error;
830 if (!DisableNetworkForService(affected_service, &error)) {
831 if (error.type() == Error::kNotFound) {
832 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100833 << " (or failed to connect to) service "
834 << affected_service->unique_name() << ", "
Paul Stewart835934a2012-12-06 19:27:09 -0800835 << "but could not find supplicant network to disable.";
836 } else {
Wade Guthrie727e0202013-06-27 11:22:38 -0700837 LOG(FATAL) << "DisableNetwork failed on " << link_name()
838 << "for service " << affected_service->unique_name() << ".";
Paul Stewart71f6ecd2012-09-13 14:52:18 -0700839 }
mukesh agrawal15908392011-11-16 18:29:25 +0000840 }
841
Ben Chanfad4a0b2012-04-18 15:49:59 -0700842 SLOG(WiFi, 2) << "WiFi " << link_name() << " disconnected from "
Darin Petkov457728b2013-01-09 09:49:08 +0100843 << " (or failed to connect to) service "
844 << affected_service->unique_name();
Paul Stewart1369c2b2013-01-11 05:41:26 -0800845 Service::ConnectFailure failure;
Paul Stewartbca08f82013-07-09 16:32:37 -0700846 if (SuspectCredentials(affected_service, &failure)) {
mukesh agrawalcf24a242012-05-21 16:46:11 -0700847 // If we suspect bad credentials, set failure, to trigger an error
mukesh agrawal56e32202012-07-26 16:32:11 -0700848 // mole in Chrome.
Paul Stewart1369c2b2013-01-11 05:41:26 -0800849 affected_service->SetFailure(failure);
850 LOG(ERROR) << "Connection failure is due to suspect credentials: returning "
851 << Service::ConnectFailureToString(failure);
Paul Stewartf2d60912012-07-15 08:37:30 -0700852 } else {
853 affected_service->SetFailureSilent(Service::kFailureUnknown);
mukesh agrawalcf24a242012-05-21 16:46:11 -0700854 }
Wade Guthrie9ec08062013-09-25 15:22:24 -0700855 metrics()->NotifySignalAtDisconnect(*affected_service,
856 affected_service->SignalLevel());
mukesh agrawale1d90e92012-02-15 17:36:08 -0800857 affected_service->NotifyCurrentEndpoint(NULL);
Wade Guthrie7ac610b2013-10-01 17:48:14 -0700858 metrics()->NotifyServiceDisconnect(*affected_service);
mukesh agrawal15908392011-11-16 18:29:25 +0000859
860 if (affected_service == pending_service_.get()) {
861 // The attempt to connect to |pending_service_| failed. Clear
862 // |pending_service_|, to indicate we're no longer in the middle
863 // of a connect request.
Paul Stewart2b05e622012-07-13 20:38:44 -0700864 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000865 } else if (pending_service_.get()) {
866 // We've attributed the disconnection to what was the
867 // |current_service_|, rather than the |pending_service_|.
868 //
869 // If we're wrong about that (i.e. supplicant reported this
870 // CurrentBSS change after attempting to connect to
871 // |pending_service_|), we're depending on supplicant to retry
872 // connecting to |pending_service_|, and delivering another
873 // CurrentBSS change signal in the future.
874 //
875 // Log this fact, to help us debug (in case our assumptions are
876 // wrong).
Darin Petkov457728b2013-01-09 09:49:08 +0100877 SLOG(WiFi, 2) << "WiFi " << link_name() << " pending connection to service "
878 << pending_service_->unique_name()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700879 << " after disconnect";
mukesh agrawal15908392011-11-16 18:29:25 +0000880 }
Paul Stewarte369ece2012-05-22 09:11:03 -0700881
882 // If we disconnect, initially scan at a faster frequency, to make sure
883 // we've found all available APs.
884 RestartFastScanAttempts();
mukesh agrawal15908392011-11-16 18:29:25 +0000885}
886
887// We use the term "Roam" loosely. In particular, we include the case
888// where we "Roam" to a BSS from the disconnected state.
889void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
890 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
891 if (endpoint_it == endpoint_by_rpcid_.end()) {
892 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
893 << new_bss;
894 return;
895 }
896
Paul Stewart3c504012013-01-17 17:49:58 -0800897 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
898 WiFiServiceRefPtr service = provider_->FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000899 if (!service.get()) {
900 LOG(WARNING) << "WiFi " << link_name()
901 << " could not find Service for Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800902 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000903 << " (service will be unchanged)";
904 return;
905 }
906
Ben Chanfad4a0b2012-04-18 15:49:59 -0700907 SLOG(WiFi, 2) << "WiFi " << link_name()
Paul Stewart3c504012013-01-17 17:49:58 -0800908 << " roamed to Endpoint " << endpoint->bssid_string()
909 << " " << LogSSID(endpoint->ssid_string());
mukesh agrawal15908392011-11-16 18:29:25 +0000910
Paul Stewart3c504012013-01-17 17:49:58 -0800911 service->NotifyCurrentEndpoint(endpoint);
Thieu Lee41a72d2012-02-06 20:46:51 +0000912
mukesh agrawal15908392011-11-16 18:29:25 +0000913 if (pending_service_.get() &&
914 service.get() != pending_service_.get()) {
915 // The Service we've roamed on to is not the one we asked for.
916 // We assume that this is transient, and that wpa_supplicant
917 // is trying / will try to connect to |pending_service_|.
918 //
919 // If it succeeds, we'll end up back here, but with |service|
920 // pointing at the same service as |pending_service_|.
921 //
922 // If it fails, we'll process things in HandleDisconnect.
923 //
924 // So we leave |pending_service_| untouched.
Ben Chanfad4a0b2012-04-18 15:49:59 -0700925 SLOG(WiFi, 2) << "WiFi " << link_name()
926 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800927 << endpoint->bssid_string()
Ben Chanfad4a0b2012-04-18 15:49:59 -0700928 << " is not part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100929 << pending_service_->unique_name();
mukesh agrawal15908392011-11-16 18:29:25 +0000930
931 // Sanity check: if we didn't roam onto |pending_service_|, we
932 // should still be on |current_service_|.
933 if (service.get() != current_service_.get()) {
934 LOG(WARNING) << "WiFi " << link_name()
935 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800936 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000937 << " is neither part of pending service "
Darin Petkov457728b2013-01-09 09:49:08 +0100938 << pending_service_->unique_name()
mukesh agrawal15908392011-11-16 18:29:25 +0000939 << " nor part of current service "
Darin Petkov457728b2013-01-09 09:49:08 +0100940 << (current_service_ ?
941 current_service_->unique_name() :
mukesh agrawal15908392011-11-16 18:29:25 +0000942 "(NULL)");
943 // Although we didn't expect to get here, we should keep
944 // |current_service_| in sync with what supplicant has done.
945 current_service_ = service;
946 }
947 return;
948 }
949
950 if (pending_service_.get()) {
951 // We assume service.get() == pending_service_.get() here, because
952 // of the return in the previous if clause.
953 //
954 // Boring case: we've connected to the service we asked
955 // for. Simply update |current_service_| and |pending_service_|.
956 current_service_ = service;
Wade Guthrieb0def9f2013-07-12 13:49:18 -0700957 SetScanState(kScanConnected, scan_method_, __func__);
Wade Guthriedf6d61b2013-07-17 11:43:55 -0700958 SetPendingService(NULL);
mukesh agrawal15908392011-11-16 18:29:25 +0000959 return;
960 }
961
962 // |pending_service_| was NULL, so we weren't attempting to connect
963 // to a new Service. Sanity check that we're still on
964 // |current_service_|.
965 if (service.get() != current_service_.get()) {
966 LOG(WARNING)
967 << "WiFi " << link_name()
968 << " new current Endpoint "
Paul Stewart3c504012013-01-17 17:49:58 -0800969 << endpoint->bssid_string()
mukesh agrawal15908392011-11-16 18:29:25 +0000970 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000971 StringPrintf(" is not part of current service %s",
Darin Petkov457728b2013-01-09 09:49:08 +0100972 current_service_->unique_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000973 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000974 // We didn't expect to be here, but let's cope as well as we
975 // can. Update |current_service_| to keep it in sync with
976 // supplicant.
977 current_service_ = service;
Paul Stewartabbe2792012-07-15 07:50:35 -0700978
979 // If this service isn't already marked as actively connecting (likely,
980 // since this service is a bit of a surprise) set the service as
981 // associating.
982 if (!current_service_->IsConnecting()) {
983 current_service_->SetState(Service::kStateAssociating);
984 }
985
mukesh agrawal15908392011-11-16 18:29:25 +0000986 return;
987 }
988
989 // At this point, we know that |pending_service_| was NULL, and that
990 // we're still on |current_service_|. This is the most boring case
991 // of all, because there's no state to update here.
992 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700993}
994
Paul Stewart835934a2012-12-06 19:27:09 -0800995string WiFi::FindNetworkRpcidForService(
996 const WiFiService *service, Error *error) {
997 ReverseServiceMap::const_iterator rpcid_it =
998 rpcid_by_service_.find(service);
999 if (rpcid_it == rpcid_by_service_.end()) {
1000 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +01001001 StringPrintf(
1002 "WiFi %s cannot find supplicant network rpcid for service %s",
1003 link_name().c_str(), service->unique_name().c_str());
Paul Stewart835934a2012-12-06 19:27:09 -08001004 // There are contexts where this is not an error, such as when a service
1005 // is clearing whatever cached credentials may not exist.
1006 SLOG(WiFi, 2) << error_message;
1007 if (error) {
1008 error->Populate(Error::kNotFound, error_message);
1009 }
1010 return "";
1011 }
1012
1013 return rpcid_it->second;
1014}
1015
1016bool WiFi::DisableNetworkForService(const WiFiService *service, Error *error) {
1017 string rpcid = FindNetworkRpcidForService(service, error);
1018 if (rpcid.empty()) {
1019 // Error is already populated.
1020 return false;
1021 }
1022
1023 if (!DisableNetwork(rpcid)) {
1024 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +01001025 StringPrintf("WiFi %s cannot disable network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -08001026 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +01001027 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -08001028 rpcid.c_str());
1029 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
1030
1031 // Make sure that such errored networks are removed, so problems do not
Wade Guthrie9ec08062013-09-25 15:22:24 -07001032 // propagate to future connection attempts.
Paul Stewart835934a2012-12-06 19:27:09 -08001033 RemoveNetwork(rpcid);
1034 rpcid_by_service_.erase(service);
1035
1036 return false;
1037 }
1038
1039 return true;
1040}
1041
1042bool WiFi::RemoveNetworkForService(const WiFiService *service, Error *error) {
1043 string rpcid = FindNetworkRpcidForService(service, error);
1044 if (rpcid.empty()) {
1045 // Error is already populated.
1046 return false;
1047 }
1048
1049 // Erase the rpcid from our tables regardless of failure below, since even
1050 // if in failure, we never want to use this network again.
1051 rpcid_by_service_.erase(service);
1052
1053 // TODO(quiche): Reconsider giving up immediately. Maybe give
1054 // wpa_supplicant some time to retry, first.
1055 if (!RemoveNetwork(rpcid)) {
1056 const string error_message =
Darin Petkov457728b2013-01-09 09:49:08 +01001057 StringPrintf("WiFi %s cannot remove network for service %s: "
Paul Stewart835934a2012-12-06 19:27:09 -08001058 "DBus operation failed for rpcid %s.",
Darin Petkov457728b2013-01-09 09:49:08 +01001059 link_name().c_str(), service->unique_name().c_str(),
Paul Stewart835934a2012-12-06 19:27:09 -08001060 rpcid.c_str());
1061 Error::PopulateAndLog(error, Error::kOperationFailed, error_message);
1062 return false;
1063 }
1064
1065 return true;
1066}
1067
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001068void WiFi::BSSAddedTask(
1069 const ::DBus::Path &path,
1070 const map<string, ::DBus::Variant> &properties) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001071 // Note: we assume that BSSIDs are unique across endpoints. This
1072 // means that if an AP reuses the same BSSID for multiple SSIDs, we
1073 // lose.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001074 WiFiEndpointRefPtr endpoint(
1075 new WiFiEndpoint(proxy_factory_, this, path, properties));
Wade Guthrie592ecd52012-11-12 13:12:30 -08001076 SLOG(WiFi, 1) << "Found endpoint. "
1077 << "RPC path: " << path << ", "
Darin Petkov50cb78a2013-02-06 16:17:49 +01001078 << LogSSID(endpoint->ssid_string()) << ", "
Wade Guthrie592ecd52012-11-12 13:12:30 -08001079 << "bssid: " << endpoint->bssid_string() << ", "
1080 << "signal: " << endpoint->signal_strength() << ", "
1081 << "security: " << endpoint->security_mode() << ", "
1082 << "frequency: " << endpoint->frequency();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001083
1084 if (endpoint->ssid_string().empty()) {
1085 // Don't bother trying to find or create a Service for an Endpoint
1086 // without an SSID. We wouldn't be able to connect to it anyway.
1087 return;
1088 }
1089
mukesh agrawalb3857612012-01-18 16:23:29 -08001090 if (endpoint->ssid()[0] == 0) {
1091 // Assume that an SSID starting with NULL is bogus/misconfigured,
1092 // and filter it out.
1093 return;
1094 }
1095
Paul Stewart3c504012013-01-17 17:49:58 -08001096 provider_->OnEndpointAdded(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001097
mukesh agrawale9adda12012-02-09 18:33:48 -08001098 // Do this last, to maintain the invariant that any Endpoint we
1099 // know about has a corresponding Service.
mukesh agrawalb20776f2012-02-10 16:00:36 -08001100 //
1101 // TODO(quiche): Write test to verify correct behavior in the case
1102 // where we get multiple BSSAdded events for a single endpoint.
1103 // (Old Endpoint's refcount should fall to zero, and old Endpoint
1104 // should be destroyed.)
mukesh agrawale9adda12012-02-09 18:33:48 -08001105 endpoint_by_rpcid_[path] = endpoint;
mukesh agrawalb20776f2012-02-10 16:00:36 -08001106 endpoint->Start();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001107}
1108
1109void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
1110 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
1111 if (i == endpoint_by_rpcid_.end()) {
1112 LOG(WARNING) << "WiFi " << link_name()
1113 << " could not find BSS " << path
1114 << " to remove.";
1115 return;
1116 }
1117
1118 WiFiEndpointRefPtr endpoint = i->second;
1119 CHECK(endpoint);
1120 endpoint_by_rpcid_.erase(i);
1121
Paul Stewart3c504012013-01-17 17:49:58 -08001122 WiFiServiceRefPtr service = provider_->OnEndpointRemoved(endpoint);
1123 if (!service) {
1124 return;
1125 }
1126 Error unused_error;
1127 RemoveNetworkForService(service, &unused_error);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001128
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001129 bool disconnect_service = !service->HasEndpoints() &&
1130 (service->IsConnecting() || service->IsConnected());
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001131
1132 if (disconnect_service) {
mukesh agrawaldc7b8442012-09-27 13:48:14 -07001133 LOG(INFO) << "Disconnecting from service " << service->unique_name()
1134 << ": BSSRemoved";
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001135 DisconnectFrom(service);
1136 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +00001137}
1138
Paul Stewartbc6e7392012-05-24 07:07:48 -07001139void WiFi::CertificationTask(
1140 const map<string, ::DBus::Variant> &properties) {
1141 if (!current_service_) {
1142 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1143 << " with no current service.";
1144 return;
1145 }
1146
Paul Stewart735eab52013-03-29 09:19:23 -07001147 string subject;
1148 uint32 depth;
1149 if (WPASupplicant::ExtractRemoteCertification(properties, &subject, &depth)) {
1150 current_service_->AddEAPCertification(subject, depth);
Paul Stewartbc6e7392012-05-24 07:07:48 -07001151 }
Paul Stewartbc6e7392012-05-24 07:07:48 -07001152}
1153
Paul Stewartdb0f9172012-11-30 16:48:09 -08001154void WiFi::EAPEventTask(const string &status, const string &parameter) {
Paul Stewartdb0f9172012-11-30 16:48:09 -08001155 if (!current_service_) {
1156 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1157 << " with no current service.";
1158 return;
1159 }
Paul Stewart735eab52013-03-29 09:19:23 -07001160 Service::ConnectFailure failure = Service::kFailureUnknown;
1161 eap_state_handler_->ParseStatus(status, parameter, &failure);
Paul Stewart11c224b2013-10-22 19:04:40 -07001162 if (failure == Service::kFailurePinMissing) {
1163 // wpa_supplicant can sometimes forget the PIN on disconnect from the AP.
1164 const string &pin = current_service_->eap()->pin();
1165 Error unused_error;
1166 string rpcid = FindNetworkRpcidForService(current_service_, &unused_error);
1167 if (!pin.empty() && !rpcid.empty()) {
1168 // We have a PIN configured, so we can provide it back to wpa_supplicant.
1169 LOG(INFO) << "Re-supplying PIN parameter to wpa_supplicant.";
1170 supplicant_interface_proxy_->NetworkReply(
1171 rpcid, WPASupplicant::kEAPRequestedParameterPIN, pin);
1172 failure = Service::kFailureUnknown;
1173 }
1174 }
Paul Stewartdb0f9172012-11-30 16:48:09 -08001175 if (failure != Service::kFailureUnknown) {
Paul Stewart735eab52013-03-29 09:19:23 -07001176 // Avoid a reporting failure twice by resetting EAP state handler early.
1177 eap_state_handler_->Reset();
mukesh agrawald4dc0832013-03-25 14:38:26 -07001178 Error unused_error;
mukesh agrawald4dc0832013-03-25 14:38:26 -07001179 current_service_->DisconnectWithFailure(failure, &unused_error);
Paul Stewartdb0f9172012-11-30 16:48:09 -08001180 }
1181}
1182
mukesh agrawal15908392011-11-16 18:29:25 +00001183void WiFi::PropertiesChangedTask(
1184 const map<string, ::DBus::Variant> &properties) {
1185 // TODO(quiche): Handle changes in other properties (e.g. signal
1186 // strength).
1187
1188 // Note that order matters here. In particular, we want to process
1189 // changes in the current BSS before changes in state. This is so
1190 // that we update the state of the correct Endpoint/Service.
1191
1192 map<string, ::DBus::Variant>::const_iterator properties_it =
Paul Stewart0654ece2013-03-26 15:21:26 -07001193 properties.find(WPASupplicant::kInterfacePropertyCurrentBSS);
mukesh agrawal15908392011-11-16 18:29:25 +00001194 if (properties_it != properties.end()) {
1195 CurrentBSSChanged(properties_it->second.reader().get_path());
1196 }
1197
Paul Stewart0654ece2013-03-26 15:21:26 -07001198 properties_it = properties.find(WPASupplicant::kInterfacePropertyState);
mukesh agrawal15908392011-11-16 18:29:25 +00001199 if (properties_it != properties.end()) {
1200 StateChanged(properties_it->second.reader().get_string());
1201 }
1202}
1203
mukesh agrawaldc42bb32011-07-28 10:40:26 -07001204void WiFi::ScanDoneTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001205 SLOG(WiFi, 2) << __func__ << " need_bss_flush_ " << need_bss_flush_;
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001206 if (scan_session_) {
1207 // Post |ProgressiveScanTask| so it runs after any |BSSAddedTask|s that have
1208 // been posted. This allows connections on new BSSes to be started before
1209 // we decide whether to abort the progressive scan or continue scanning.
1210 dispatcher()->PostTask(
1211 Bind(&WiFi::ProgressiveScanTask, weak_ptr_factory_.GetWeakPtr()));
1212 } else {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001213 // Post |UpdateScanStateAfterScanDone| so it runs after any |BSSAddedTask|s
1214 // that have been posted. This allows connections on new BSSes to be
1215 // started before we decide whether the scan was fruitful.
1216 dispatcher()->PostTask(Bind(&WiFi::UpdateScanStateAfterScanDone,
1217 weak_ptr_factory_.GetWeakPtr()));
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001218 }
mukesh agrawal5c05b292012-03-07 10:12:52 -08001219 if (need_bss_flush_) {
1220 CHECK(supplicant_interface_proxy_ != NULL);
1221 // Compute |max_age| relative to |resumed_at_|, to account for the
1222 // time taken to scan.
1223 struct timeval now;
1224 uint32_t max_age;
1225 time_->GetTimeMonotonic(&now);
1226 max_age = kMaxBSSResumeAgeSeconds + (now.tv_sec - resumed_at_.tv_sec);
1227 supplicant_interface_proxy_->FlushBSS(max_age);
1228 need_bss_flush_ = false;
1229 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001230 StartScanTimer();
mukesh agrawalb54601c2011-06-07 17:39:22 -07001231}
1232
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001233void WiFi::UpdateScanStateAfterScanDone() {
Paul Stewart7de7e022013-08-28 09:42:50 -07001234 if (scan_method_ == kScanMethodFull) {
1235 // Only notify the Manager on completion of full scans, since the manager
1236 // will replace any cached geolocation info with the BSSes we have right
1237 // now.
1238 manager()->OnDeviceGeolocationInfoUpdated(this);
1239 }
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07001240 if (scan_state_ == kScanBackgroundScanning) {
Wade Guthriee71aa492013-09-23 13:44:19 -07001241 // Going directly to kScanIdle (instead of to kScanFoundNothing) inhibits
1242 // some UMA reporting in SetScanState. That's desired -- we don't want
1243 // to report background scan results to UMA since the drivers may play
1244 // background scans over a longer period in order to not interfere with
1245 // traffic.
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07001246 SetScanState(kScanIdle, kScanMethodNone, __func__);
1247 } else if (scan_state_ != kScanIdle && IsIdle()) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001248 SetScanState(kScanFoundNothing, scan_method_, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001249 }
1250}
1251
mukesh agrawal32399322011-09-01 10:53:43 -07001252void WiFi::ScanTask() {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001253 SLOG(WiFi, 2) << "WiFi " << link_name() << " scan requested.";
Paul Stewartfae4dae2012-09-13 07:43:32 -07001254 if (!enabled()) {
1255 SLOG(WiFi, 2) << "Ignoring scan request while device is not enabled.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001256 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
Paul Stewartfae4dae2012-09-13 07:43:32 -07001257 return;
1258 }
1259 if (!supplicant_present_ || !supplicant_interface_proxy_.get()) {
1260 SLOG(WiFi, 2) << "Ignoring scan request while supplicant is not present.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001261 SetScanState(kScanIdle, kScanMethodNone, __func__);
Paul Stewartfae4dae2012-09-13 07:43:32 -07001262 return;
1263 }
Christopher Wileyc68c8672012-11-20 16:52:21 -08001264 if ((pending_service_.get() && pending_service_->IsConnecting()) ||
1265 (current_service_.get() && current_service_->IsConnecting())) {
1266 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1267 return;
1268 }
Paul Stewarta41e38d2011-11-11 07:47:29 -08001269 map<string, DBus::Variant> scan_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001270 scan_args[WPASupplicant::kPropertyScanType].writer().
1271 append_string(WPASupplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -08001272
Paul Stewart3c504012013-01-17 17:49:58 -08001273 ByteArrays hidden_ssids = provider_->GetHiddenSSIDList();
Paul Stewartced6a0b2011-11-08 15:32:04 -08001274 if (!hidden_ssids.empty()) {
Paul Stewart3c504012013-01-17 17:49:58 -08001275 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
1276 // not fit in.
Paul Stewart0654ece2013-03-26 15:21:26 -07001277 if (hidden_ssids.size() >= WPASupplicant::kScanMaxSSIDsPerScan) {
Paul Stewart3c504012013-01-17 17:49:58 -08001278 hidden_ssids.erase(
Paul Stewart0654ece2013-03-26 15:21:26 -07001279 hidden_ssids.begin() + WPASupplicant::kScanMaxSSIDsPerScan - 1,
Paul Stewart3c504012013-01-17 17:49:58 -08001280 hidden_ssids.end());
1281 }
1282 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
1283 // SSIDs to wpa_supplicant, we need to explicitly specify the default
1284 // behavior of doing a broadcast probe.
1285 hidden_ssids.push_back(ByteArray());
1286
Paul Stewart0654ece2013-03-26 15:21:26 -07001287 scan_args[WPASupplicant::kPropertyScanSSIDs] =
Paul Stewartced6a0b2011-11-08 15:32:04 -08001288 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
1289 }
1290
Gaurav Shah10109f22011-11-11 20:16:22 -08001291 try {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001292 // Only set the scan state/method if we are starting a full scan from
1293 // scratch. Keep the existing method if this is a failover from a
1294 // progressive scan.
1295 if (scan_state_ != kScanScanning) {
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07001296 SetScanState(IsIdle() ? kScanScanning : kScanBackgroundScanning,
1297 kScanMethodFull, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001298 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001299 supplicant_interface_proxy_->Scan(scan_args);
Ben Chan80326f32012-05-04 17:51:32 -07001300 } catch (const DBus::Error &e) { // NOLINT
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001301 // A scan may fail if, for example, the wpa_supplicant vanishing
1302 // notification is posted after this task has already started running.
1303 LOG(WARNING) << "Scan failed: " << e.what();
Gaurav Shah10109f22011-11-11 20:16:22 -08001304 }
mukesh agrawal32399322011-09-01 10:53:43 -07001305}
1306
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001307void WiFi::ProgressiveScanTask() {
1308 SLOG(WiFi, 2) << __func__ << " - scan requested for " << link_name();
1309 if (!enabled()) {
1310 LOG(INFO) << "Ignoring scan request while device is not enabled.";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001311 SetScanState(kScanIdle, kScanMethodNone, __func__); // Probably redundant.
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001312 return;
1313 }
1314 if (!scan_session_) {
1315 SLOG(WiFi, 2) << "No scan session -- returning";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001316 SetScanState(kScanIdle, kScanMethodNone, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001317 return;
1318 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001319 // TODO(wdg): We don't currently support progressive background scans. If
1320 // we did, we couldn't bail out, here, if we're connected. Progressive scan
1321 // state will have to be modified to include whether there was a connection
1322 // when the scan started. Then, this code would only bail out if we didn't
1323 // start with a connection but one exists at this point.
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001324 if (!IsIdle()) {
1325 SLOG(WiFi, 2) << "Ignoring scan request while connecting to an AP.";
1326 scan_session_.reset();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001327 return;
1328 }
1329 if (scan_session_->HasMoreFrequencies()) {
1330 SLOG(WiFi, 2) << "Initiating a scan -- returning";
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001331 SetScanState(kScanScanning, kScanMethodProgressive, __func__);
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001332 // After us initiating a scan, supplicant will gather the scan results and
1333 // send us zero or more |BSSAdded| events followed by a |ScanDone|.
1334 scan_session_->InitiateScan();
1335 return;
1336 }
1337 LOG(ERROR) << "A complete progressive scan turned-up nothing -- "
1338 << "do a regular scan";
1339 scan_session_.reset();
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001340 SetScanState(kScanScanning, kScanMethodProgressiveFinishedToFull, __func__);
Wade Guthrie2ef88ad2013-07-29 15:14:18 -07001341 LOG(INFO) << "Scan [full] on " << link_name()
1342 << " (connected to nothing on progressive scan) from " << __func__;
1343 ScanTask();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001344}
1345
1346void WiFi::OnFailedProgressiveScan() {
1347 LOG(ERROR) << "Couldn't issue a scan on " << link_name()
1348 << " -- doing a regular scan";
1349 scan_session_.reset();
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001350 SetScanState(kScanScanning, kScanMethodProgressiveErrorToFull, __func__);
Wade Guthrie2ef88ad2013-07-29 15:14:18 -07001351 LOG(INFO) << "Scan [full] on " << link_name()
1352 << " (failover from progressive scan) from " << __func__;
1353 ScanTask();
Wade Guthrie5a4e2ef2013-04-30 12:51:39 -07001354}
1355
Albert Chaulk0e1cdea2013-02-27 15:32:55 -08001356string WiFi::GetServiceLeaseName(const WiFiService &service) {
1357 return service.GetStorageIdentifier();
1358}
1359
1360void WiFi::DestroyServiceLease(const WiFiService &service) {
1361 DestroyIPConfigLease(GetServiceLeaseName(service));
1362}
1363
mukesh agrawal15908392011-11-16 18:29:25 +00001364void WiFi::StateChanged(const string &new_state) {
1365 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +00001366 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +00001367 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
1368 << old_state << " -> " << new_state;
1369
1370 WiFiService *affected_service;
1371 // Identify the service to which the state change applies. If
1372 // |pending_service_| is non-NULL, then the state change applies to
1373 // |pending_service_|. Otherwise, it applies to |current_service_|.
1374 //
1375 // This policy is driven by the fact that the |pending_service_|
1376 // doesn't become the |current_service_| until wpa_supplicant
1377 // reports a CurrentBSS change to the |pending_service_|. And the
mukesh agrawalc01f3982012-01-24 13:48:39 -08001378 // CurrentBSS change won't be reported until the |pending_service_|
Paul Stewart0654ece2013-03-26 15:21:26 -07001379 // reaches the WPASupplicant::kInterfaceStateCompleted state.
mukesh agrawal15908392011-11-16 18:29:25 +00001380 affected_service =
1381 pending_service_.get() ? pending_service_.get() : current_service_.get();
1382 if (!affected_service) {
Ben Chanfad4a0b2012-04-18 15:49:59 -07001383 SLOG(WiFi, 2) << "WiFi " << link_name() << " " << __func__
1384 << " with no service";
mukesh agrawal15908392011-11-16 18:29:25 +00001385 return;
1386 }
1387
Paul Stewart0654ece2013-03-26 15:21:26 -07001388 if (new_state == WPASupplicant::kInterfaceStateCompleted) {
Paul Stewart44663922012-07-30 11:03:03 -07001389 if (affected_service->IsConnected()) {
1390 StopReconnectTimer();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001391 EnableHighBitrates();
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001392 } else if (has_already_completed_) {
1393 LOG(INFO) << link_name() << " L3 configuration already started.";
mukesh agrawalc01f3982012-01-24 13:48:39 -08001394 } else {
mukesh agrawala5dda0e2013-08-16 11:53:10 -07001395 provider_->IncrementConnectCount(affected_service->frequency());
1396 if (AcquireIPConfigWithLeaseName(
1397 GetServiceLeaseName(*affected_service))) {
1398 LOG(INFO) << link_name() << " is up; started L3 configuration.";
1399 affected_service->SetState(Service::kStateConfiguring);
Ben Chanf024ef42013-09-20 14:21:38 -07001400 if (affected_service->IsSecurityMatch(kSecurityWep)) {
mukesh agrawala5dda0e2013-08-16 11:53:10 -07001401 // With the overwhelming majority of WEP networks, we cannot assume
1402 // our credentials are correct just because we have successfully
1403 // connected. It is more useful to track received data as the L3
1404 // configuration proceeds to see if we can decrypt anything.
1405 receive_byte_count_at_connect_ = GetReceiveByteCount();
1406 } else {
1407 affected_service->ResetSuspectedCredentialFailures();
1408 }
1409 } else {
1410 LOG(ERROR) << "Unable to acquire DHCP config.";
1411 }
mukesh agrawalc01f3982012-01-24 13:48:39 -08001412 }
Christopher Wiley8f81e2a2012-10-17 16:51:32 -07001413 has_already_completed_ = true;
Paul Stewart0654ece2013-03-26 15:21:26 -07001414 } else if (new_state == WPASupplicant::kInterfaceStateAssociated) {
mukesh agrawal15908392011-11-16 18:29:25 +00001415 affected_service->SetState(Service::kStateAssociating);
Paul Stewart0654ece2013-03-26 15:21:26 -07001416 } else if (new_state == WPASupplicant::kInterfaceStateAuthenticating ||
1417 new_state == WPASupplicant::kInterfaceStateAssociating ||
1418 new_state == WPASupplicant::kInterfaceState4WayHandshake ||
1419 new_state == WPASupplicant::kInterfaceStateGroupHandshake) {
mukesh agrawal15908392011-11-16 18:29:25 +00001420 // Ignore transitions into these states from Completed, to avoid
1421 // bothering the user when roaming, or re-keying.
Paul Stewart0654ece2013-03-26 15:21:26 -07001422 if (old_state != WPASupplicant::kInterfaceStateCompleted)
mukesh agrawal15908392011-11-16 18:29:25 +00001423 affected_service->SetState(Service::kStateAssociating);
Wade Guthrie9ec08062013-09-25 15:22:24 -07001424 // TODO(quiche): On backwards transitions, we should probably set
mukesh agrawal15908392011-11-16 18:29:25 +00001425 // a timeout for getting back into the completed state. At present,
1426 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001427 // has changed. But there may be cases where that signal is not sent.
Paul Stewartee6b3d72013-07-12 16:07:51 -07001428 // (crbug.com/206208)
Paul Stewart0654ece2013-03-26 15:21:26 -07001429 } else if (new_state == WPASupplicant::kInterfaceStateDisconnected &&
Paul Stewart44663922012-07-30 11:03:03 -07001430 affected_service == current_service_ &&
1431 affected_service->IsConnected()) {
1432 // This means that wpa_supplicant failed in a re-connect attempt, but
1433 // may still be reconnecting. Give wpa_supplicant a limited amount of
1434 // time to transition out this condition by either connecting or changing
1435 // CurrentBSS.
1436 StartReconnectTimer();
mukesh agrawal15908392011-11-16 18:29:25 +00001437 } else {
1438 // Other transitions do not affect Service state.
1439 //
1440 // Note in particular that we ignore a State change into
1441 // kInterfaceStateDisconnected, in favor of observing the corresponding
1442 // change in CurrentBSS.
1443 }
mukesh agrawal7ec71312011-11-10 02:08:26 +00001444}
1445
Paul Stewart1369c2b2013-01-11 05:41:26 -08001446bool WiFi::SuspectCredentials(
Paul Stewartbca08f82013-07-09 16:32:37 -07001447 WiFiServiceRefPtr service, Service::ConnectFailure *failure) const {
Ben Chanf024ef42013-09-20 14:21:38 -07001448 if (service->IsSecurityMatch(kSecurityPsk)) {
Paul Stewart0654ece2013-03-26 15:21:26 -07001449 if (supplicant_state_ == WPASupplicant::kInterfaceState4WayHandshake &&
Paul Stewartbca08f82013-07-09 16:32:37 -07001450 service->AddSuspectedCredentialFailure()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001451 if (failure) {
1452 *failure = Service::kFailureBadPassphrase;
1453 }
1454 return true;
1455 }
Ben Chanf024ef42013-09-20 14:21:38 -07001456 } else if (service->IsSecurityMatch(kSecurity8021x)) {
Paul Stewart735eab52013-03-29 09:19:23 -07001457 if (eap_state_handler_->is_eap_in_progress() &&
Paul Stewartbca08f82013-07-09 16:32:37 -07001458 service->AddSuspectedCredentialFailure()) {
Paul Stewart1369c2b2013-01-11 05:41:26 -08001459 if (failure) {
1460 *failure = Service::kFailureEAPAuthentication;
1461 }
1462 return true;
1463 }
mukesh agrawalcf24a242012-05-21 16:46:11 -07001464 }
1465
Paul Stewart1369c2b2013-01-11 05:41:26 -08001466 return false;
mukesh agrawalcf24a242012-05-21 16:46:11 -07001467}
1468
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001469// static
1470bool WiFi::SanitizeSSID(string *ssid) {
1471 CHECK(ssid);
1472
1473 size_t ssid_len = ssid->length();
1474 size_t i;
1475 bool changed = false;
1476
Gary Morainac1bdb42012-02-16 17:42:29 -08001477 for (i = 0; i < ssid_len; ++i) {
mukesh agrawal16bc1b82012-02-09 18:38:26 -08001478 if (!g_ascii_isprint((*ssid)[i])) {
1479 (*ssid)[i] = '?';
1480 changed = true;
1481 }
1482 }
1483
1484 return changed;
1485}
1486
Darin Petkov50cb78a2013-02-06 16:17:49 +01001487// static
1488string WiFi::LogSSID(const string &ssid) {
1489 string out;
1490 for (string::const_iterator it = ssid.begin(); it != ssid.end(); ++it) {
1491 // Replace '[' and ']' (in addition to non-printable characters) so that
1492 // it's easy to match the right substring through a non-greedy regex.
1493 if (*it == '[' || *it == ']' || !g_ascii_isprint(*it)) {
1494 base::StringAppendF(&out, "\\x%02x", *it);
1495 } else {
1496 out += *it;
1497 }
1498 }
1499 return StringPrintf("[SSID=%s]", out.c_str());
1500}
1501
Paul Stewart3c508e12012-08-09 11:40:06 -07001502void WiFi::OnLinkMonitorFailure() {
1503 // If we have never found the gateway, let's be conservative and not
1504 // do anything, in case this network topology does not have a gateway.
1505 if (!link_monitor()->IsGatewayFound()) {
1506 LOG(INFO) << "In " << __func__ << "(): "
1507 << "Skipping reassociate since gateway was never found.";
1508 return;
1509 }
1510
1511 if (!supplicant_present_) {
1512 LOG(ERROR) << "In " << __func__ << "(): "
1513 << "wpa_supplicant is not present. Cannot reassociate.";
1514 return;
1515 }
1516
1517 try {
Christopher Wileye0b2a012012-10-31 13:11:27 -07001518 // This will force a transition out of connected, if we are actually
1519 // connected.
Paul Stewart3c508e12012-08-09 11:40:06 -07001520 supplicant_interface_proxy_->Reassociate();
Christopher Wileye0b2a012012-10-31 13:11:27 -07001521 // If we don't eventually get a transition back into a connected state,
1522 // there is something wrong.
1523 StartReconnectTimer();
Paul Stewart3c508e12012-08-09 11:40:06 -07001524 LOG(INFO) << "In " << __func__ << "(): Called Reassociate().";
1525 } catch (const DBus::Error &e) { // NOLINT
1526 LOG(ERROR) << "In " << __func__ << "(): failed to call Reassociate().";
1527 return;
1528 }
1529}
1530
Arman Ugurayed8e6102012-11-29 14:47:20 -08001531bool WiFi::ShouldUseArpGateway() const {
1532 return true;
1533}
1534
Paul Stewart3c504012013-01-17 17:49:58 -08001535void WiFi::DisassociateFromService(const WiFiServiceRefPtr &service) {
1536 DisconnectFrom(service);
1537 if (service == selected_service()) {
1538 DropConnection();
1539 }
1540 Error unused_error;
1541 RemoveNetworkForService(service, &unused_error);
1542}
1543
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001544vector<GeolocationInfo> WiFi::GetGeolocationObjects() const {
1545 vector<GeolocationInfo> objects;
1546 for (EndpointMap::const_iterator it = endpoint_by_rpcid_.begin();
1547 it != endpoint_by_rpcid_.end();
1548 ++it) {
1549 GeolocationInfo geoinfo;
1550 WiFiEndpointRefPtr endpoint = it->second;
1551 geoinfo.AddField(kGeoMacAddressProperty, endpoint->bssid_string());
1552 geoinfo.AddField(kGeoSignalStrengthProperty,
1553 StringPrintf("%d", endpoint->signal_strength()));
1554 geoinfo.AddField(
1555 kGeoChannelProperty,
1556 StringPrintf("%d",
1557 Metrics::WiFiFrequencyToChannel(endpoint->frequency())));
Paul Stewartee6b3d72013-07-12 16:07:51 -07001558 // TODO(gauravsh): Include age field. crbug.com/217554
Gaurav Shah6d2c72d2012-10-16 16:30:44 -07001559 objects.push_back(geoinfo);
1560 }
1561 return objects;
1562}
1563
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001564void WiFi::HelpRegisterDerivedInt32(
1565 PropertyStore *store,
1566 const string &name,
1567 int32(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001568 bool(WiFi::*set)(const int32 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001569 store->RegisterDerivedInt32(
1570 name,
1571 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1572}
1573
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001574void WiFi::HelpRegisterDerivedUint16(
1575 PropertyStore *store,
1576 const string &name,
1577 uint16(WiFi::*get)(Error *error),
mukesh agrawalbebf1b82013-04-23 15:06:33 -07001578 bool(WiFi::*set)(const uint16 &value, Error *error)) {
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001579 store->RegisterDerivedUint16(
1580 name,
1581 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1582}
1583
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001584void WiFi::HelpRegisterConstDerivedBool(
1585 PropertyStore *store,
1586 const string &name,
1587 bool(WiFi::*get)(Error *error)) {
1588 store->RegisterDerivedBool(
1589 name,
1590 BoolAccessor(new CustomAccessor<WiFi, bool>(this, get, NULL)));
1591}
1592
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001593void WiFi::OnAfterResume() {
mukesh agrawal5c05b292012-03-07 10:12:52 -08001594 LOG(INFO) << __func__;
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001595 Device::OnAfterResume(); // May refresh ipconfig_
mukesh agrawal5c05b292012-03-07 10:12:52 -08001596
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001597 // We want to flush the BSS cache, but we don't want to conflict
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001598 // with an active connection attempt. So record the need to flush,
1599 // and take care of flushing when the next scan completes.
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001600 //
1601 // Note that supplicant will automatically expire old cache
1602 // entries (after, e.g., a BSS is not found in two consecutive
1603 // scans). However, our explicit flush accelerates re-association
1604 // in cases where a BSS disappeared while we were asleep. (See,
1605 // e.g. WiFiRoaming.005SuspendRoam.)
1606 time_->GetTimeMonotonic(&resumed_at_);
1607 need_bss_flush_ = true;
1608
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001609 // Abort any current scan (at the shill-level; let any request that's
1610 // already gone out finish) since we don't know when it started.
1611 AbortScan();
1612
1613 if (IsIdle()) {
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001614 // Not scanning/connecting/connected, so let's get things rolling.
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001615 Scan(kProgressiveScan, NULL, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001616 RestartFastScanAttempts();
mukesh agrawal2f9df4e2012-08-08 12:29:20 -07001617 } else {
1618 SLOG(WiFi, 1) << __func__
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001619 << " skipping scan, already connecting or connected.";
Gary Morainac1bdb42012-02-16 17:42:29 -08001620 }
1621}
1622
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001623void WiFi::AbortScan() {
1624 if (scan_session_) {
1625 scan_session_.reset();
1626 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07001627 SetScanState(kScanIdle, kScanMethodNone, __func__);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001628}
1629
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001630void WiFi::OnConnected() {
1631 Device::OnConnected();
1632 EnableHighBitrates();
Paul Stewartf6f96482013-07-12 12:49:15 -07001633 if (current_service_ &&
Ben Chanf024ef42013-09-20 14:21:38 -07001634 current_service_->IsSecurityMatch(kSecurityWep)) {
Paul Stewartf6f96482013-07-12 12:49:15 -07001635 // With a WEP network, we are now reasonably certain the credentials are
1636 // correct, whereas with other network types we were able to determine
1637 // this earlier when the association process succeeded.
1638 current_service_->ResetSuspectedCredentialFailures();
1639 }
Paul Stewart7cd45722013-08-12 14:50:14 -07001640 RequestStationInfo();
Paul Stewartf6f96482013-07-12 12:49:15 -07001641}
1642
1643void WiFi::OnIPConfigFailure() {
1644 if (!current_service_) {
1645 LOG(ERROR) << "WiFi " << link_name() << " " << __func__
1646 << " with no current service.";
1647 return;
1648 }
Ben Chanf024ef42013-09-20 14:21:38 -07001649 if (current_service_->IsSecurityMatch(kSecurityWep) &&
Paul Stewartf6f96482013-07-12 12:49:15 -07001650 GetReceiveByteCount() == receive_byte_count_at_connect_ &&
1651 current_service_->AddSuspectedCredentialFailure()) {
1652 // If we've connected to a WEP network and haven't successfully
1653 // decrypted any bytes at all during the configuration process,
1654 // it is fair to suspect that our credentials to this network
1655 // may not be correct.
1656 Error error;
1657 current_service_->DisconnectWithFailure(Service::kFailureBadPassphrase,
1658 &error);
1659 return;
1660 }
1661
1662 Device::OnIPConfigFailure();
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001663}
1664
Paul Stewarte369ece2012-05-22 09:11:03 -07001665void WiFi::RestartFastScanAttempts() {
1666 fast_scans_remaining_ = kNumFastScanAttempts;
1667 StartScanTimer();
1668}
1669
mukesh agrawalb66c6462012-05-07 11:45:25 -07001670void WiFi::StartScanTimer() {
1671 if (scan_interval_seconds_ == 0) {
1672 StopScanTimer();
1673 return;
1674 }
1675 scan_timer_callback_.Reset(
1676 Bind(&WiFi::ScanTimerHandler, weak_ptr_factory_.GetWeakPtr()));
Paul Stewarte369ece2012-05-22 09:11:03 -07001677 // Repeat the first few scans after disconnect relatively quickly so we
1678 // have reasonable trust that no APs we are looking for are present.
1679 dispatcher()->PostDelayedTask(scan_timer_callback_.callback(),
1680 fast_scans_remaining_ > 0 ?
1681 kFastScanIntervalSeconds * 1000 : scan_interval_seconds_ * 1000);
mukesh agrawalb66c6462012-05-07 11:45:25 -07001682}
1683
1684void WiFi::StopScanTimer() {
1685 scan_timer_callback_.Cancel();
1686}
1687
1688void WiFi::ScanTimerHandler() {
1689 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001690 if (scan_state_ == kScanIdle && IsIdle()) {
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001691 Scan(kProgressiveScan, NULL, __func__);
Paul Stewarte369ece2012-05-22 09:11:03 -07001692 if (fast_scans_remaining_ > 0) {
1693 --fast_scans_remaining_;
1694 }
mukesh agrawalb66c6462012-05-07 11:45:25 -07001695 }
1696 StartScanTimer();
1697}
1698
Paul Stewart2b05e622012-07-13 20:38:44 -07001699void WiFi::StartPendingTimer() {
1700 pending_timeout_callback_.Reset(
1701 Bind(&WiFi::PendingTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1702 dispatcher()->PostDelayedTask(pending_timeout_callback_.callback(),
1703 kPendingTimeoutSeconds * 1000);
1704}
1705
1706void WiFi::StopPendingTimer() {
Paul Stewartbca08f82013-07-09 16:32:37 -07001707 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart2b05e622012-07-13 20:38:44 -07001708 pending_timeout_callback_.Cancel();
1709}
1710
1711void WiFi::SetPendingService(const WiFiServiceRefPtr &service) {
Paul Stewartff96a842012-08-13 15:59:10 -07001712 SLOG(WiFi, 2) << "WiFi " << link_name() << " setting pending service to "
Darin Petkov457728b2013-01-09 09:49:08 +01001713 << (service ? service->unique_name(): "NULL");
Paul Stewart2b05e622012-07-13 20:38:44 -07001714 if (service) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001715 SetScanState(kScanConnecting, scan_method_, __func__);
Paul Stewart2b05e622012-07-13 20:38:44 -07001716 service->SetState(Service::kStateAssociating);
1717 StartPendingTimer();
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001718 } else {
1719 // SetPendingService(NULL) is called in the following cases:
1720 // a) |ConnectTo|->|DisconnectFrom|. Connecting to a service, disconnect
1721 // the old service (scan_state_ == kScanTransitionToConnecting). No
1722 // state transition is needed here.
1723 // b) |HandleRoam|. Connected to a service, it's no longer pending
1724 // (scan_state_ == kScanIdle). No state transition is needed here.
1725 // c) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
1726 // from a service not during a scan (scan_state_ == kScanIdle). No
1727 // state transition is needed here.
1728 // d) |DisconnectFrom| and |HandleDisconnect|. Disconnected/disconnecting
1729 // from a service during a scan (scan_state_ == kScanScanning or
1730 // kScanConnecting). This is an odd case -- let's discard any
1731 // statistics we're gathering by transitioning directly into kScanIdle.
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07001732 if (scan_state_ == kScanScanning ||
1733 scan_state_ == kScanBackgroundScanning ||
1734 scan_state_ == kScanConnecting) {
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001735 SetScanState(kScanIdle, kScanMethodNone, __func__);
1736 }
1737 if (pending_service_) {
1738 StopPendingTimer();
1739 }
Paul Stewart2b05e622012-07-13 20:38:44 -07001740 }
1741 pending_service_ = service;
1742}
1743
1744void WiFi::PendingTimeoutHandler() {
mukesh agrawald4dc0832013-03-25 14:38:26 -07001745 Error unused_error;
Paul Stewart2b05e622012-07-13 20:38:44 -07001746 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1747 CHECK(pending_service_);
Wade Guthriedf6d61b2013-07-17 11:43:55 -07001748 SetScanState(kScanFoundNothing, scan_method_, __func__);
mukesh agrawald4dc0832013-03-25 14:38:26 -07001749 pending_service_->DisconnectWithFailure(
1750 Service::kFailureOutOfRange, &unused_error);
Paul Stewart17d90652013-04-04 15:09:11 -07001751
1752 // A hidden service may have no endpoints, since wpa_supplicant
1753 // failed to attain a CurrentBSS. If so, the service has no
1754 // reference to |this| device and cannot call WiFi::DisconnectFrom()
1755 // to reset pending_service_. In this case, we must perform the
1756 // disconnect here ourselves.
1757 if (pending_service_) {
1758 CHECK(!pending_service_->HasEndpoints());
1759 LOG(INFO) << "Hidden service was not found.";
1760 DisconnectFrom(pending_service_);
1761 }
Paul Stewart2b05e622012-07-13 20:38:44 -07001762}
1763
Paul Stewart44663922012-07-30 11:03:03 -07001764void WiFi::StartReconnectTimer() {
Paul Stewart1aff7302012-08-04 20:04:47 -07001765 if (!reconnect_timeout_callback_.IsCancelled()) {
1766 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__
1767 << ": reconnect timer already running.";
1768 return;
1769 }
Paul Stewart44663922012-07-30 11:03:03 -07001770 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
1771 reconnect_timeout_callback_.Reset(
1772 Bind(&WiFi::ReconnectTimeoutHandler, weak_ptr_factory_.GetWeakPtr()));
1773 dispatcher()->PostDelayedTask(reconnect_timeout_callback_.callback(),
1774 kReconnectTimeoutSeconds * 1000);
1775}
1776
1777void WiFi::StopReconnectTimer() {
1778 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
1779 reconnect_timeout_callback_.Cancel();
1780}
1781
1782void WiFi::ReconnectTimeoutHandler() {
1783 LOG(INFO) << "WiFi Device " << link_name() << ": " << __func__;
Paul Stewart1aff7302012-08-04 20:04:47 -07001784 reconnect_timeout_callback_.Cancel();
Paul Stewart44663922012-07-30 11:03:03 -07001785 CHECK(current_service_);
1786 current_service_->SetFailureSilent(Service::kFailureConnect);
1787 DisconnectFrom(current_service_);
1788}
1789
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001790void WiFi::OnSupplicantAppear(const string &/*owner*/) {
1791 LOG(INFO) << "WPA supplicant appeared.";
1792 if (supplicant_present_) {
Darin Petkov9cd7ca12012-07-03 11:06:40 +02001793 // Restart the WiFi device if it's started already. This will reset the
1794 // state and connect the device to the new WPA supplicant instance.
1795 if (enabled()) {
1796 Restart();
1797 }
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001798 return;
1799 }
1800 supplicant_present_ = true;
1801 ConnectToSupplicant();
1802}
1803
1804void WiFi::OnSupplicantVanish() {
1805 LOG(INFO) << "WPA supplicant vanished.";
1806 if (!supplicant_present_) {
1807 return;
1808 }
1809 supplicant_present_ = false;
1810 // Restart the WiFi device if it's started already. This will effectively
1811 // suspend the device until the WPA supplicant reappears.
1812 if (enabled()) {
1813 Restart();
1814 }
1815}
1816
Paul Stewart5581d072012-12-17 17:30:20 -08001817void WiFi::OnWiFiDebugScopeChanged(bool enabled) {
1818 SLOG(WiFi, 2) << "WiFi debug scope changed; enable is now " << enabled;
1819 if (!supplicant_process_proxy_.get()) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07001820 SLOG(WiFi, 2) << "Supplicant process proxy not present.";
Paul Stewart5581d072012-12-17 17:30:20 -08001821 return;
1822 }
1823 string current_level;
1824 try {
1825 current_level = supplicant_process_proxy_->GetDebugLevel();
1826 } catch (const DBus::Error &e) { // NOLINT
1827 LOG(ERROR) << __func__ << ": Failed to get wpa_supplicant debug level.";
1828 return;
1829 }
1830
Paul Stewart0654ece2013-03-26 15:21:26 -07001831 if (current_level != WPASupplicant::kDebugLevelInfo &&
1832 current_level != WPASupplicant::kDebugLevelDebug) {
Paul Stewart5581d072012-12-17 17:30:20 -08001833 SLOG(WiFi, 2) << "WiFi debug level is currently "
1834 << current_level
1835 << "; assuming that it is being controlled elsewhere.";
1836 return;
1837 }
Paul Stewart0654ece2013-03-26 15:21:26 -07001838 string new_level = enabled ? WPASupplicant::kDebugLevelDebug :
1839 WPASupplicant::kDebugLevelInfo;
Paul Stewart5581d072012-12-17 17:30:20 -08001840
1841 if (new_level == current_level) {
1842 SLOG(WiFi, 2) << "WiFi debug level is already the desired level "
1843 << current_level;
1844 return;
1845 }
1846
1847 try {
1848 supplicant_process_proxy_->SetDebugLevel(new_level);
1849 } catch (const DBus::Error &e) { // NOLINT
1850 LOG(ERROR) << __func__ << ": Failed to set wpa_supplicant debug level.";
1851 }
1852}
1853
Paul Stewarta47c3c62012-12-18 12:14:29 -08001854void WiFi::SetConnectionDebugging(bool enabled) {
1855 if (is_debugging_connection_ == enabled) {
1856 return;
1857 }
1858 OnWiFiDebugScopeChanged(
1859 enabled ||
1860 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
1861 is_debugging_connection_ = enabled;
1862}
1863
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001864void WiFi::ConnectToSupplicant() {
1865 LOG(INFO) << link_name() << ": " << (enabled() ? "enabled" : "disabled")
1866 << " supplicant: "
1867 << (supplicant_present_ ? "present" : "absent")
1868 << " proxy: "
1869 << (supplicant_process_proxy_.get() ? "non-null" : "null");
1870 if (!enabled() || !supplicant_present_ || supplicant_process_proxy_.get()) {
1871 return;
1872 }
1873 supplicant_process_proxy_.reset(
1874 proxy_factory_->CreateSupplicantProcessProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001875 WPASupplicant::kDBusPath, WPASupplicant::kDBusAddr));
Paul Stewart5581d072012-12-17 17:30:20 -08001876 OnWiFiDebugScopeChanged(
1877 ScopeLogger::GetInstance()->IsScopeEnabled(ScopeLogger::kWiFi));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001878 ::DBus::Path interface_path;
1879 try {
1880 map<string, DBus::Variant> create_interface_args;
Paul Stewart0654ece2013-03-26 15:21:26 -07001881 create_interface_args[WPASupplicant::kInterfacePropertyName].writer().
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001882 append_string(link_name().c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -07001883 create_interface_args[WPASupplicant::kInterfacePropertyDriver].writer().
1884 append_string(WPASupplicant::kDriverNL80211);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001885 create_interface_args[
Paul Stewart0654ece2013-03-26 15:21:26 -07001886 WPASupplicant::kInterfacePropertyConfigFile].writer().
Paul Stewart196f50f2013-03-27 18:02:11 -07001887 append_string(WPASupplicant::kSupplicantConfPath);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001888 interface_path =
1889 supplicant_process_proxy_->CreateInterface(create_interface_args);
1890 } catch (const DBus::Error &e) { // NOLINT
Paul Stewart0654ece2013-03-26 15:21:26 -07001891 if (!strcmp(e.name(), WPASupplicant::kErrorInterfaceExists)) {
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001892 interface_path =
1893 supplicant_process_proxy_->GetInterface(link_name());
1894 // TODO(quiche): Is it okay to crash here, if device is missing?
1895 } else {
Paul Stewartb80c81c2012-06-28 13:05:45 -07001896 LOG(ERROR) << __func__ << ": Failed to create interface with supplicant.";
1897 return;
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001898 }
1899 }
1900
1901 supplicant_interface_proxy_.reset(
1902 proxy_factory_->CreateSupplicantInterfaceProxy(
Paul Stewart0654ece2013-03-26 15:21:26 -07001903 this, interface_path, WPASupplicant::kDBusAddr));
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001904
1905 RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
1906 IFF_UP);
1907 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
1908
1909 // Clear out any networks that might previously have been configured
1910 // for this interface.
1911 supplicant_interface_proxy_->RemoveAllNetworks();
1912
1913 // Flush interface's BSS cache, so that we get BSSAdded signals for
1914 // all BSSes (not just new ones since the last scan).
1915 supplicant_interface_proxy_->FlushBSS(0);
1916
1917 try {
1918 // TODO(pstew): Disable fast_reauth until supplicant can properly deal
1919 // with RADIUS servers that respond strangely to such requests.
Paul Stewartee6b3d72013-07-12 16:07:51 -07001920 // crbug.com/208561
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001921 supplicant_interface_proxy_->SetFastReauth(false);
1922 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001923 LOG(ERROR) << "Failed to disable fast_reauth. "
1924 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001925 }
1926
1927 try {
Wade Guthrie227c7742013-10-10 11:06:33 -07001928 supplicant_interface_proxy_->SetRoamThreshold(roam_threshold_db_);
1929 } catch (const DBus::Error &e) { // NOLINT
1930 LOG(ERROR) << "Failed to set roam_threshold. "
1931 << "May be running an older version of wpa_supplicant.";
1932 }
1933
1934 try {
1935 // Helps with passing WiFiRoaming.001SSIDSwitchBack.
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001936 supplicant_interface_proxy_->SetScanInterval(kRescanIntervalSeconds);
1937 } catch (const DBus::Error &e) { // NOLINT
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001938 LOG(ERROR) << "Failed to set scan_interval. "
1939 << "May be running an older version of wpa_supplicant.";
1940 }
1941
1942 try {
1943 supplicant_interface_proxy_->SetDisableHighBitrates(true);
1944 } catch (const DBus::Error &e) { // NOLINT
1945 LOG(ERROR) << "Failed to disable high bitrates. "
1946 << "May be running an older version of wpa_supplicant.";
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001947 }
1948
Wade Guthrie4823f4f2013-07-25 10:03:03 -07001949 Scan(kProgressiveScan, NULL, __func__);
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001950 StartScanTimer();
1951}
1952
Christopher Wiley5519e9e2013-01-08 16:55:56 -08001953void WiFi::EnableHighBitrates() {
1954 LOG(INFO) << "Enabling high bitrates.";
1955 try {
1956 supplicant_interface_proxy_->EnableHighBitrates();
1957 } catch (const DBus::Error &e) { // NOLINT
1958 LOG(ERROR) << "exception while enabling high rates: " << e.what();
1959 }
1960}
1961
Darin Petkov2b8e44e2012-06-25 15:13:26 +02001962void WiFi::Restart() {
1963 LOG(INFO) << link_name() << " restarting.";
1964 WiFiRefPtr me = this; // Make sure we don't get destructed.
1965 // Go through the manager rather than starting and stopping the device
1966 // directly so that the device can be configured with the profile.
1967 manager()->DeregisterDevice(me);
1968 manager()->RegisterDevice(me);
1969}
1970
Wade Guthrie92d06362013-04-25 15:41:30 -07001971void WiFi::ConfigureScanFrequencies() {
1972 GetWiphyMessage get_wiphy;
1973 get_wiphy.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
1974 interface_index());
Wade Guthrie7347bf22013-04-30 11:21:51 -07001975 netlink_manager_->SendNl80211Message(
1976 &get_wiphy,
1977 Bind(&WiFi::OnNewWiphy, weak_ptr_factory_.GetWeakPtr()),
1978 Bind(&NetlinkManager::OnNetlinkMessageError));
Wade Guthrie92d06362013-04-25 15:41:30 -07001979}
1980
Wade Guthrie7347bf22013-04-30 11:21:51 -07001981void WiFi::OnNewWiphy(const Nl80211Message &nl80211_message) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001982 // Verify NL80211_CMD_NEW_WIPHY
Wade Guthrie7347bf22013-04-30 11:21:51 -07001983 if (nl80211_message.command() != NewWiphyMessage::kCommand) {
Wade Guthrie92d06362013-04-25 15:41:30 -07001984 LOG(ERROR) << "Received unexpected command:"
Wade Guthrie7347bf22013-04-30 11:21:51 -07001985 << nl80211_message.command();
Wade Guthrie92d06362013-04-25 15:41:30 -07001986 return;
1987 }
1988
1989 // The attributes, for this message, are complicated.
1990 // NL80211_ATTR_BANDS contains an array of bands...
1991 AttributeListConstRefPtr wiphy_bands;
Wade Guthrie7347bf22013-04-30 11:21:51 -07001992 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
Wade Guthrie92d06362013-04-25 15:41:30 -07001993 NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) {
1994 LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS";
1995 return;
1996 }
1997
1998 AttributeIdIterator band_iter(*wiphy_bands);
1999 for (; !band_iter.AtEnd(); band_iter.Advance()) {
2000 AttributeListConstRefPtr wiphy_band;
2001 if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(),
2002 &wiphy_band)) {
2003 LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found";
2004 continue;
2005 }
2006
2007 // ...Each band has a FREQS attribute...
2008 AttributeListConstRefPtr frequencies;
2009 if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS,
2010 &frequencies)) {
2011 LOG(ERROR) << "BAND " << band_iter.GetId()
2012 << " had no 'frequencies' attribute";
2013 continue;
2014 }
2015
2016 // ...And each FREQS attribute contains an array of information about the
2017 // frequency...
2018 AttributeIdIterator freq_iter(*frequencies);
2019 for (; !freq_iter.AtEnd(); freq_iter.Advance()) {
2020 AttributeListConstRefPtr frequency;
2021 if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(),
2022 &frequency)) {
2023 // ...Including the frequency, itself (the part we want).
2024 uint32_t frequency_value = 0;
2025 if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ,
2026 &frequency_value)) {
Wade Guthrie2edd58b2013-05-23 11:16:08 -07002027 SLOG(WiFi, 7) << "Found frequency[" << freq_iter.GetId()
Wade Guthrie92d06362013-04-25 15:41:30 -07002028 << "] = " << frequency_value;
2029 all_scan_frequencies_.insert(frequency_value);
2030 }
2031 }
2032 }
2033 }
2034}
2035
Paul Stewartbaf87072013-10-04 17:03:37 -07002036KeyValueStore WiFi::GetLinkStatistics(Error */*error*/) {
2037 return link_statistics_;
2038}
2039
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002040bool WiFi::GetScanPending(Error */* error */) {
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07002041 return scan_state_ == kScanScanning || scan_state_ == kScanBackgroundScanning;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002042}
2043
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002044void WiFi::SetScanState(ScanState new_state,
2045 ScanMethod new_method,
2046 const char *reason) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002047 if (new_state == kScanIdle)
2048 new_method = kScanMethodNone;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07002049 if (new_state == kScanConnected) {
2050 // The scan method shouldn't be changed by the connection process, so
2051 // we'll put a CHECK, here, to verify. NOTE: this assumption is also
2052 // enforced by the parameters to the call to |ReportScanResultToUma|.
2053 CHECK(new_method == scan_method_);
2054 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002055
2056 int log_level = 6;
Wade Guthrie9f4aa152013-07-29 16:03:14 -07002057 bool state_or_method_changed = true;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002058 bool is_terminal_state = false;
2059 if (new_state == scan_state_ && new_method == scan_method_) {
2060 log_level = 7;
Wade Guthrie9f4aa152013-07-29 16:03:14 -07002061 state_or_method_changed = false;
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002062 } else if (new_state == kScanConnected || new_state == kScanFoundNothing) {
2063 // These 'terminal' states are slightly more interesting than the
2064 // intermediate states.
Wade Guthriee71aa492013-09-23 13:44:19 -07002065 // NOTE: Since background scan goes directly to kScanIdle (skipping over
2066 // the states required to set |is_terminal_state|), ReportScanResultToUma,
2067 // below, doesn't get called. That's intentional.
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002068 log_level = 5;
2069 is_terminal_state = true;
2070 }
2071
2072 base::TimeDelta elapsed_time;
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07002073 if (new_state == kScanScanning || new_state == kScanBackgroundScanning) {
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002074 if (!scan_timer_.Start()) {
2075 LOG(ERROR) << "Scan start unreliable";
2076 }
2077 } else {
2078 if (!scan_timer_.GetElapsedTime(&elapsed_time)) {
2079 LOG(ERROR) << "Scan time unreliable";
2080 }
2081 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002082 SLOG(WiFi, log_level) << (reason ? reason : "<unknown>")
2083 << " - " << link_name()
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002084 << ": Scan state: "
2085 << ScanStateString(scan_state_, scan_method_)
2086 << " -> " << ScanStateString(new_state, new_method)
2087 << " @ " << elapsed_time.InMillisecondsF()
2088 << " ms into scan.";
Wade Guthrie9f4aa152013-07-29 16:03:14 -07002089 if (!state_or_method_changed)
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002090 return;
2091
2092 // Actually change the state.
Wade Guthrie2187efe2013-08-27 09:13:26 -07002093 ScanState old_state = scan_state_;
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07002094 ScanMethod old_method = scan_method_;
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07002095 bool old_scan_pending = GetScanPending(NULL);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002096 scan_state_ = new_state;
2097 scan_method_ = new_method;
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07002098 bool new_scan_pending = GetScanPending(NULL);
2099 if (old_scan_pending != new_scan_pending) {
Ben Chanf024ef42013-09-20 14:21:38 -07002100 adaptor()->EmitBoolChanged(kScanningProperty, new_scan_pending);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002101 }
Wade Guthrie44f290d2013-05-28 10:16:25 -07002102 switch (new_state) {
2103 case kScanIdle:
2104 metrics()->ResetScanTimer(interface_index());
Wade Guthriedf6d61b2013-07-17 11:43:55 -07002105 metrics()->ResetConnectTimer(interface_index());
2106 if (scan_session_) {
2107 scan_session_.reset();
2108 }
Wade Guthrie44f290d2013-05-28 10:16:25 -07002109 break;
Wade Guthrie2187efe2013-08-27 09:13:26 -07002110 case kScanScanning: // FALLTHROUGH
2111 case kScanBackgroundScanning:
2112 if (new_state != old_state) {
2113 metrics()->NotifyDeviceScanStarted(interface_index());
2114 }
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002115 break;
Wade Guthrie44f290d2013-05-28 10:16:25 -07002116 case kScanConnecting:
2117 metrics()->NotifyDeviceScanFinished(interface_index());
2118 // TODO(wdg): Provide |is_auto_connecting| to this interface. For now,
2119 // I'll lie (because I don't care about the auto-connect metrics).
2120 metrics()->NotifyDeviceConnectStarted(interface_index(), false);
2121 break;
2122 case kScanConnected:
2123 metrics()->NotifyDeviceConnectFinished(interface_index());
2124 break;
2125 case kScanFoundNothing:
2126 // Note that finishing a scan that hasn't started (if, for example, we
2127 // get here when we fail to complete a connection) does nothing.
2128 metrics()->NotifyDeviceScanFinished(interface_index());
2129 metrics()->ResetConnectTimer(interface_index());
2130 break;
Wade Guthriedf6d61b2013-07-17 11:43:55 -07002131 case kScanTransitionToConnecting: // FALLTHROUGH
Wade Guthrie44f290d2013-05-28 10:16:25 -07002132 default:
2133 break;
2134 }
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002135 if (is_terminal_state) {
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002136 ReportScanResultToUma(new_state, old_method);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002137 // Now that we've logged a terminal state, let's call ourselves to
Wade Guthrie9ec08062013-09-25 15:22:24 -07002138 // transition to the idle state.
Wade Guthrieb0def9f2013-07-12 13:49:18 -07002139 SetScanState(kScanIdle, kScanMethodNone, reason);
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002140 }
2141}
2142
2143// static
2144string WiFi::ScanStateString(ScanState state, ScanMethod method) {
2145 switch (state) {
2146 case kScanIdle:
2147 return "IDLE";
2148 case kScanScanning:
2149 DCHECK(method != kScanMethodNone) << "Scanning with no scan method.";
2150 switch (method) {
2151 case kScanMethodFull:
2152 return "FULL_START";
2153 case kScanMethodProgressive:
2154 return "PROGRESSIVE_START";
2155 case kScanMethodProgressiveErrorToFull:
2156 return "PROGRESSIVE_ERROR_FULL_START";
2157 case kScanMethodProgressiveFinishedToFull:
2158 return "PROGRESSIVE_FINISHED_FULL_START";
2159 default:
2160 NOTREACHED();
2161 }
Paul Stewart3bdd3aa2013-08-20 13:20:54 -07002162 case kScanBackgroundScanning:
2163 return "BACKGROUND_START";
Wade Guthriedf6d61b2013-07-17 11:43:55 -07002164 case kScanTransitionToConnecting:
2165 return "TRANSITION_TO_CONNECTING";
Wade Guthrie0cf3c982013-05-29 09:11:35 -07002166 case kScanConnecting:
2167 switch (method) {
2168 case kScanMethodNone:
2169 return "CONNECTING (not scan related)";
2170 case kScanMethodFull:
2171 return "FULL_CONNECTING";
2172 case kScanMethodProgressive:
2173 return "PROGRESSIVE_CONNECTING";
2174 case kScanMethodProgressiveErrorToFull:
2175 return "PROGRESSIVE_ERROR_FULL_CONNECTING";
2176 case kScanMethodProgressiveFinishedToFull:
2177 return "PROGRESSIVE_FINISHED_FULL_CONNECTING";
2178 default:
2179 NOTREACHED();
2180 }
2181 case kScanConnected:
2182 switch (method) {
2183 case kScanMethodNone:
2184 return "CONNECTED (not scan related; e.g., from a supplicant roam)";
2185 case kScanMethodFull:
2186 return "FULL_CONNECTED";
2187 case kScanMethodProgressive:
2188 return "PROGRESSIVE_CONNECTED";
2189 case kScanMethodProgressiveErrorToFull:
2190 return "PROGRESSIVE_ERROR_FULL_CONNECTED";
2191 case kScanMethodProgressiveFinishedToFull:
2192 return "PROGRESSIVE_FINISHED_FULL_CONNECTED";
2193 default:
2194 NOTREACHED();
2195 }
2196 case kScanFoundNothing:
2197 switch (method) {
2198 case kScanMethodNone:
2199 return "CONNECT FAILED (not scan related)";
2200 case kScanMethodFull:
2201 return "FULL_NOCONNECTION";
2202 case kScanMethodProgressive:
2203 // This is possible if shill started to connect but timed out before
2204 // the connection was completed.
2205 return "PROGRESSIVE_FINISHED_NOCONNECTION";
2206 case kScanMethodProgressiveErrorToFull:
2207 return "PROGRESSIVE_ERROR_FULL_NOCONNECTION";
2208 case kScanMethodProgressiveFinishedToFull:
2209 return "PROGRESSIVE_FINISHED_FULL_NOCONNECTION";
2210 default:
2211 NOTREACHED();
2212 }
2213 default:
2214 NOTREACHED();
2215 }
2216 return ""; // To shut up the compiler (that doesn't understand NOTREACHED).
2217}
2218
Wade Guthrieb9e0ee72013-05-31 09:23:30 -07002219void WiFi::ReportScanResultToUma(ScanState state, ScanMethod method) {
2220 Metrics::WiFiScanResult result = Metrics::kScanResultMax;
2221 if (state == kScanConnected) {
2222 switch (method) {
2223 case kScanMethodFull:
2224 result = Metrics::kScanResultFullScanConnected;
2225 break;
2226 case kScanMethodProgressive:
2227 result = Metrics::kScanResultProgressiveConnected;
2228 break;
2229 case kScanMethodProgressiveErrorToFull:
2230 result = Metrics::kScanResultProgressiveErrorButFullConnected;
2231 break;
2232 case kScanMethodProgressiveFinishedToFull:
2233 result = Metrics::kScanResultProgressiveAndFullConnected;
2234 break;
2235 default:
2236 // OK: Connect resulting from something other than scan.
2237 break;
2238 }
2239 } else if (state == kScanFoundNothing) {
2240 switch (method) {
2241 case kScanMethodFull:
2242 result = Metrics::kScanResultFullScanFoundNothing;
2243 break;
2244 case kScanMethodProgressiveErrorToFull:
2245 result = Metrics::kScanResultProgressiveErrorAndFullFoundNothing;
2246 break;
2247 case kScanMethodProgressiveFinishedToFull:
2248 result = Metrics::kScanResultProgressiveAndFullFoundNothing;
2249 break;
2250 default:
2251 // OK: Connect failed, not scan related.
2252 break;
2253 }
2254 }
2255
2256 if (result != Metrics::kScanResultMax) {
2257 metrics()->SendEnumToUMA(Metrics::kMetricScanResult,
2258 result,
2259 Metrics::kScanResultMax);
2260 }
2261}
2262
Paul Stewart7cd45722013-08-12 14:50:14 -07002263void WiFi::RequestStationInfo() {
2264 if (!current_service_ || !current_service_->IsConnected()) {
2265 LOG(ERROR) << "Not collecting station info because we are not connected.";
2266 return;
2267 }
2268
2269 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(supplicant_bss_);
2270 if (endpoint_it == endpoint_by_rpcid_.end()) {
2271 LOG(ERROR) << "Can't get endpoint for current supplicant BSS "
2272 << supplicant_bss_;
2273 return;
2274 }
2275
2276 GetStationMessage get_station;
2277 if (!get_station.attributes()->SetU32AttributeValue(NL80211_ATTR_IFINDEX,
2278 interface_index())) {
2279 LOG(ERROR) << "Could not add IFINDEX attribute for GetStation message.";
2280 return;
2281 }
2282
2283 const WiFiEndpointConstRefPtr endpoint(endpoint_it->second);
2284 if (!get_station.attributes()->SetRawAttributeValue(
2285 NL80211_ATTR_MAC,
2286 ByteString::CreateFromHexString(endpoint->bssid_hex()))) {
2287 LOG(ERROR) << "Could not add MAC attribute for GetStation message.";
2288 return;
2289 }
2290
2291 netlink_manager_->SendNl80211Message(
2292 &get_station,
2293 Bind(&WiFi::OnReceivedStationInfo, weak_ptr_factory_.GetWeakPtr()),
2294 Bind(&NetlinkManager::OnNetlinkMessageError));
2295
2296 request_station_info_callback_.Reset(
2297 Bind(&WiFi::RequestStationInfo, weak_ptr_factory_.GetWeakPtr()));
2298 dispatcher()->PostDelayedTask(request_station_info_callback_.callback(),
2299 kRequestStationInfoPeriodSeconds * 1000);
2300}
2301
2302void WiFi::OnReceivedStationInfo(const Nl80211Message &nl80211_message) {
2303 // Verify NL80211_CMD_NEW_STATION
2304 if (nl80211_message.command() != NewStationMessage::kCommand) {
2305 LOG(ERROR) << "Received unexpected command:"
2306 << nl80211_message.command();
2307 return;
2308 }
2309
2310 if (!current_service_ || !current_service_->IsConnected()) {
2311 LOG(ERROR) << "Not accepting station info because we are not connected.";
2312 return;
2313 }
2314
2315 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(supplicant_bss_);
2316 if (endpoint_it == endpoint_by_rpcid_.end()) {
2317 LOG(ERROR) << "Can't get endpoint for current supplicant BSS."
2318 << supplicant_bss_;
2319 return;
2320 }
2321
2322 ByteString station_bssid;
2323 if (!nl80211_message.const_attributes()->GetRawAttributeValue(
2324 NL80211_ATTR_MAC, &station_bssid)) {
2325 LOG(ERROR) << "Unable to get MAC attribute from received station info.";
2326 return;
2327 }
2328
2329 WiFiEndpointRefPtr endpoint(endpoint_it->second);
2330
2331 if (!station_bssid.Equals(
2332 ByteString::CreateFromHexString(endpoint->bssid_hex()))) {
2333 LOG(ERROR) << "Received station info for a non-current BSS.";
2334 return;
2335 }
2336
2337 AttributeListConstRefPtr station_info;
2338 if (!nl80211_message.const_attributes()->ConstGetNestedAttributeList(
2339 NL80211_ATTR_STA_INFO, &station_info)) {
2340 LOG(ERROR) << "Received station info had no NL80211_ATTR_STA_INFO.";
2341 return;
2342 }
2343
2344 uint8_t signal;
2345 if (!station_info->GetU8AttributeValue(NL80211_STA_INFO_SIGNAL, &signal)) {
2346 LOG(ERROR) << "Received station info had no NL80211_STA_INFO_SIGNAL.";
2347 return;
2348 }
2349
2350 endpoint->UpdateSignalStrength(static_cast<signed char>(signal));
Paul Stewartbaf87072013-10-04 17:03:37 -07002351
2352 link_statistics_.Clear();
2353
2354 map<int, string> u32_property_map = {
2355 { NL80211_STA_INFO_INACTIVE_TIME, kInactiveTimeMillisecondsProperty },
2356 { NL80211_STA_INFO_RX_PACKETS, kPacketReceiveSuccessesProperty },
2357 { NL80211_STA_INFO_TX_FAILED, kPacketTransmitFailuresProperty },
2358 { NL80211_STA_INFO_TX_PACKETS, kPacketTransmitSuccessesProperty },
2359 { NL80211_STA_INFO_TX_RETRIES, kTransmitRetriesProperty }
2360 };
2361
2362 for (const auto &kv : u32_property_map) {
2363 uint32 value;
2364 if (station_info->GetU32AttributeValue(kv.first, &value)) {
2365 link_statistics_.SetUint(kv.second, value);
2366 }
2367 }
2368
2369 map<int, string> s8_property_map = {
2370 { NL80211_STA_INFO_SIGNAL, kLastReceiveSignalDbmProperty },
2371 { NL80211_STA_INFO_SIGNAL_AVG, kAverageReceiveSignalDbmProperty }
2372 };
2373
2374 for (const auto &kv : s8_property_map) {
2375 uint8 value;
2376 if (station_info->GetU8AttributeValue(kv.first, &value)) {
2377 // Despite these values being reported as a U8 by the kernel, these
2378 // should be interpreted as signed char.
2379 link_statistics_.SetInt(kv.second, static_cast<signed char>(value));
2380 }
2381 }
2382
2383 AttributeListConstRefPtr transmit_info;
2384 if (station_info->ConstGetNestedAttributeList(
2385 NL80211_STA_INFO_TX_BITRATE, &transmit_info)) {
2386 // TODO(pstew): Support VHT rate parameters. crbug.com/305050
2387 uint16 rate = 0; // In 100Kbps.
2388 uint8 mcs = 0;
2389 bool is_40_mhz = false;
2390 bool is_short_gi = false;
2391 string mcs_info;
2392 transmit_info->GetU16AttributeValue(NL80211_RATE_INFO_BITRATE, &rate);
2393 if (transmit_info->GetU8AttributeValue(NL80211_RATE_INFO_MCS, &mcs)) {
2394 mcs_info = StringPrintf(" MCS %d", mcs);
2395 }
2396 transmit_info->GetFlagAttributeValue(NL80211_RATE_INFO_40_MHZ_WIDTH,
2397 &is_40_mhz);
2398 transmit_info->GetFlagAttributeValue(NL80211_RATE_INFO_SHORT_GI,
2399 &is_short_gi);
2400 if (rate) {
2401 link_statistics_.SetString(kTransmitBitrateProperty,
2402 StringPrintf("%d.%d MBit/s%s%s%s",
2403 rate / 10, rate % 10,
2404 mcs_info.c_str(),
2405 is_40_mhz ? " 40MHz" : "",
2406 is_short_gi ? " short GI" : ""));
2407 }
2408 }
Paul Stewart7cd45722013-08-12 14:50:14 -07002409}
2410
2411void WiFi::StopRequestingStationInfo() {
2412 SLOG(WiFi, 2) << "WiFi Device " << link_name() << ": " << __func__;
2413 request_station_info_callback_.Cancel();
Paul Stewartbaf87072013-10-04 17:03:37 -07002414 link_statistics_.Clear();
Paul Stewart7cd45722013-08-12 14:50:14 -07002415}
2416
Paul Stewartdf4c7d62013-11-12 14:05:00 -08002417bool WiFi::TDLSDiscover(const string &peer) {
2418 try {
2419 supplicant_interface_proxy_->TDLSDiscover(peer);
2420 } catch (const DBus::Error &e) { // NOLINT
Paul Stewartc6fbad92013-11-13 14:50:52 -08002421 LOG(ERROR) << "exception while performing TDLS discover: " << e.what();
Paul Stewartdf4c7d62013-11-12 14:05:00 -08002422 return false;
2423 }
2424 return true;
2425}
2426
2427bool WiFi::TDLSSetup(const string &peer) {
2428 try {
2429 supplicant_interface_proxy_->TDLSSetup(peer);
2430 } catch (const DBus::Error &e) { // NOLINT
Paul Stewartc6fbad92013-11-13 14:50:52 -08002431 LOG(ERROR) << "exception while performing TDLS setup: " << e.what();
Paul Stewartdf4c7d62013-11-12 14:05:00 -08002432 return false;
2433 }
2434 return true;
2435}
2436
2437string WiFi::TDLSStatus(const string &peer) {
2438 try {
2439 return supplicant_interface_proxy_->TDLSStatus(peer);
2440 } catch (const DBus::Error &e) { // NOLINT
Paul Stewartc6fbad92013-11-13 14:50:52 -08002441 LOG(ERROR) << "exception while getting TDLS status: " << e.what();
Paul Stewartdf4c7d62013-11-12 14:05:00 -08002442 return "";
2443 }
2444}
2445
2446bool WiFi::TDLSTeardown(const string &peer) {
2447 try {
2448 supplicant_interface_proxy_->TDLSTeardown(peer);
2449 } catch (const DBus::Error &e) { // NOLINT
Paul Stewartc6fbad92013-11-13 14:50:52 -08002450 LOG(ERROR) << "exception while performing TDLS teardown: " << e.what();
Paul Stewartdf4c7d62013-11-12 14:05:00 -08002451 return false;
2452 }
2453 return true;
2454}
2455
Paul Stewartc6fbad92013-11-13 14:50:52 -08002456string WiFi::PerformTDLSOperation(const string &operation,
2457 const string &peer,
2458 Error *error) {
2459 bool success = false;
2460
2461 SLOG(WiFi, 2) << "TDLS command received: " << operation
2462 << " for peer " << peer;
2463 if (operation == kTDLSDiscoverOperation) {
2464 success = TDLSDiscover(peer);
2465 } else if (operation == kTDLSSetupOperation) {
2466 success = TDLSSetup(peer);
2467 } else if (operation == kTDLSStatusOperation) {
2468 string supplicant_status = TDLSStatus(peer);
2469 SLOG(WiFi, 2) << "TDLS status returned: " << supplicant_status;
2470 if (!supplicant_status.empty()) {
2471 if (supplicant_status == WPASupplicant::kTDLSStateConnected) {
2472 return kTDLSConnectedState;
2473 } else if (supplicant_status == WPASupplicant::kTDLSStateDisabled) {
2474 return kTDLSDisabledState;
2475 } else if (supplicant_status ==
2476 WPASupplicant::kTDLSStatePeerDoesNotExist) {
2477 return kTDLSNonexistentState;
2478 } else if (supplicant_status ==
2479 WPASupplicant::kTDLSStatePeerNotConnected) {
2480 return kTDLSDisconnectedState;
2481 } else {
2482 return kTDLSUnknownState;
2483 }
2484 }
2485 } else if (operation == kTDLSTeardownOperation) {
2486 success = TDLSTeardown(peer);
2487 } else {
2488 error->Populate(Error::kInvalidArguments, "Unknown operation");
2489 return "";
2490 }
2491
2492 if (!success) {
2493 Error::PopulateAndLog(error, Error::kOperationFailed,
2494 "TDLS operation failed");
2495 }
2496
2497 return "";
2498}
2499
Paul Stewartb50f0b92011-05-16 16:31:42 -07002500} // namespace shill