blob: aa45c2ee7a4fb9bf0a4c5143c17a82c452d8a958 [file] [log] [blame]
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartb50f0b92011-05-16 16:31:42 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Chris Masone2b105542011-06-22 10:58:09 -07005#include "shill/wifi.h"
6
Paul Stewartb50f0b92011-05-16 16:31:42 -07007#include <time.h>
8#include <stdio.h>
mukesh agrawalc7426a42011-06-03 13:04:28 -07009#include <string.h>
mukesh agrawalf2f68a52011-09-01 12:15:48 -070010#include <netinet/ether.h>
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -070011#include <linux/if.h> // Needs definitions from netinet/ether.h
Paul Stewartb50f0b92011-05-16 16:31:42 -070012
mukesh agrawal8a3188d2011-12-01 20:56:44 +000013#include <algorithm>
mukesh agrawalab87ea42011-05-18 11:44:49 -070014#include <map>
Paul Stewartced6a0b2011-11-08 15:32:04 -080015#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
19#include <base/logging.h>
mukesh agrawal15908392011-11-16 18:29:25 +000020#include <base/stringprintf.h>
mukesh agrawal7a4e4002011-09-06 11:26:05 -070021#include <base/string_number_conversions.h>
22#include <base/string_util.h>
Chris Masoneb925cc82011-06-22 15:39:57 -070023#include <chromeos/dbus/service_constants.h>
Paul Stewartb50f0b92011-05-16 16:31:42 -070024
25#include "shill/control_interface.h"
Paul Stewartced6a0b2011-11-08 15:32:04 -080026#include "shill/dbus_adaptor.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070027#include "shill/device.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070028#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070029#include "shill/event_dispatcher.h"
mukesh agrawal7a4e4002011-09-06 11:26:05 -070030#include "shill/key_value_store.h"
31#include "shill/ieee80211.h"
Chris Masone7aa5f902011-07-11 11:13:35 -070032#include "shill/manager.h"
33#include "shill/profile.h"
mukesh agrawal4d0401c2012-01-06 16:05:31 -080034#include "shill/property_accessor.h"
Darin Petkovd1967262011-07-18 14:55:18 -070035#include "shill/proxy_factory.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080036#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070037#include "shill/supplicant_interface_proxy_interface.h"
38#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080039#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070040#include "shill/wifi_endpoint.h"
41#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070042#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070043
mukesh agrawal15908392011-11-16 18:29:25 +000044using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070045using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080046using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070047using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070048using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070049
Paul Stewartb50f0b92011-05-16 16:31:42 -070050namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070051
52// statics
mukesh agrawal4d0401c2012-01-06 16:05:31 -080053const char *WiFi::kDefaultBgscanMethod =
54 wpa_supplicant::kNetworkBgscanMethodSimple;
55const uint16 WiFi::kDefaultBgscanShortIntervalSeconds = 30;
56const int32 WiFi::kDefaultBgscanSignalThresholdDbm = -50;
57const uint16 WiFi::kDefaultScanIntervalSeconds = 180;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070058// Note that WiFi generates some manager-level errors, because it implements
59// the Manager.GetWiFiService flimflam API. The API is implemented here,
60// rather than in manager, to keep WiFi-specific logic in the right place.
61const char WiFi::kManagerErrorPassphraseRequired[] = "must specify passphrase";
62const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
63const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
64const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
65const char WiFi::kManagerErrorTypeRequired[] = "must specify service type";
66const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
67 "security mode is unsupported";
68const char WiFi::kManagerErrorUnsupportedServiceType[] =
69 "service type is unsupported";
70const char WiFi::kManagerErrorUnsupportedServiceMode[] =
71 "service mode is unsupported";
mukesh agrawal7ec71312011-11-10 02:08:26 +000072const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalb54601c2011-06-07 17:39:22 -070073
Paul Stewartb50f0b92011-05-16 16:31:42 -070074WiFi::WiFi(ControlInterface *control_interface,
75 EventDispatcher *dispatcher,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070076 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070077 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080078 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070079 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070080 : Device(control_interface,
81 dispatcher,
82 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070083 link,
Chris Masone626719f2011-08-18 16:58:48 -070084 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080085 interface_index,
86 Technology::kWifi),
Darin Petkovab565bb2011-10-06 02:55:51 -070087 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawalb54601c2011-06-07 17:39:22 -070088 task_factory_(this),
mukesh agrawal15908392011-11-16 18:29:25 +000089 link_up_(false),
90 supplicant_state_(kInterfaceStateUnknown),
91 supplicant_bss_("(unknown)"),
mukesh agrawal4d0401c2012-01-06 16:05:31 -080092 bgscan_method_(kDefaultBgscanMethod),
93 bgscan_short_interval_seconds_(kDefaultBgscanShortIntervalSeconds),
94 bgscan_signal_threshold_dbm_(kDefaultBgscanSignalThresholdDbm),
Chris Masone853b81b2011-06-24 14:11:41 -070095 scan_pending_(false),
mukesh agrawal4d0401c2012-01-06 16:05:31 -080096 scan_interval_seconds_(kDefaultScanIntervalSeconds) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070097 PropertyStore *store = this->mutable_store();
mukesh agrawal4d0401c2012-01-06 16:05:31 -080098 HelpRegisterDerivedString(store,
99 flimflam::kBgscanMethodProperty,
100 &WiFi::GetBgscanMethod,
101 &WiFi::SetBgscanMethod);
102 HelpRegisterDerivedUint16(store,
103 flimflam::kBgscanShortIntervalProperty,
104 &WiFi::GetBgscanShortInterval,
105 &WiFi::SetBgscanShortInterval);
106 HelpRegisterDerivedInt32(store,
107 flimflam::kBgscanSignalThresholdProperty,
108 &WiFi::GetBgscanSignalThreshold,
109 &WiFi::SetBgscanSignalThreshold);
Chris Masone853b81b2011-06-24 14:11:41 -0700110
Chris Masoneb925cc82011-06-22 15:39:57 -0700111 // TODO(quiche): Decide if scan_pending_ is close enough to
112 // "currently scanning" that we don't care, or if we want to track
113 // scan pending/currently scanning/no scan scheduled as a tri-state
114 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700115 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800116 HelpRegisterDerivedUint16(store,
117 flimflam::kScanIntervalProperty,
118 &WiFi::GetScanInterval,
119 &WiFi::SetScanInterval);
Paul Stewartac4ac002011-08-26 12:04:26 -0700120 VLOG(2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700121}
122
mukesh agrawalaf571952011-07-14 14:31:12 -0700123WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700124
mukesh agrawalab87ea42011-05-18 11:44:49 -0700125void WiFi::Start() {
mukesh agrawalab87ea42011-05-18 11:44:49 -0700126 ::DBus::Path interface_path;
127
mukesh agrawalaf571952011-07-14 14:31:12 -0700128 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700129 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700130 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700131 try {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800132 map<string, DBus::Variant> create_interface_args;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700133 create_interface_args["Ifname"].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700134 append_string(link_name().c_str());
mukesh agrawalc7426a42011-06-03 13:04:28 -0700135 create_interface_args["Driver"].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700136 append_string(wpa_supplicant::kDriverNL80211);
mukesh agrawalc7426a42011-06-03 13:04:28 -0700137 // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
138 // (file with pkcs config info)
139 interface_path =
140 supplicant_process_proxy_->CreateInterface(create_interface_args);
141 } catch (const DBus::Error e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700142 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700143 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700144 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawal7ec71312011-11-10 02:08:26 +0000145 // TODO(quiche): Is it okay to crash here, if device is missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700146 } else {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000147 // TODO(quiche): Log error.
mukesh agrawalc7426a42011-06-03 13:04:28 -0700148 }
149 }
150
mukesh agrawalab87ea42011-05-18 11:44:49 -0700151 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700152 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700153 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700154
mukesh agrawal7ec71312011-11-10 02:08:26 +0000155 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700156
mukesh agrawal7ec71312011-11-10 02:08:26 +0000157 // Clear out any networks that might previously have been configured
mukesh agrawalc7426a42011-06-03 13:04:28 -0700158 // for this interface.
159 supplicant_interface_proxy_->RemoveAllNetworks();
160
mukesh agrawal7ec71312011-11-10 02:08:26 +0000161 // Flush interface's BSS cache, so that we get BSSAdded signals for
mukesh agrawalc7426a42011-06-03 13:04:28 -0700162 // all BSSes (not just new ones since the last scan).
163 supplicant_interface_proxy_->FlushBSS(0);
164
Darin Petkovc0865312011-09-16 15:31:20 -0700165 Scan(NULL);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700166 Device::Start();
167}
168
mukesh agrawalab87ea42011-05-18 11:44:49 -0700169void WiFi::Stop() {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700170 VLOG(2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal7ec71312011-11-10 02:08:26 +0000171 // TODO(quiche): Remove interface from supplicant.
mukesh agrawal31950242011-07-14 11:53:38 -0700172 supplicant_interface_proxy_.reset(); // breaks a reference cycle
173 supplicant_process_proxy_.reset();
mukesh agrawal15908392011-11-16 18:29:25 +0000174 endpoint_by_rpcid_.clear();
mukesh agrawal15908392011-11-16 18:29:25 +0000175 rpcid_by_service_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700176
Paul Stewartced6a0b2011-11-08 15:32:04 -0800177 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
178 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700179 ++it) {
180 manager()->DeregisterService(*it);
181 }
Paul Stewartced6a0b2011-11-08 15:32:04 -0800182 services_.clear(); // breaks reference cycles
mukesh agrawal7ec71312011-11-10 02:08:26 +0000183 pending_service_ = NULL; // breaks a reference cycle
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700184
mukesh agrawalab87ea42011-05-18 11:44:49 -0700185 Device::Stop();
mukesh agrawal7ec71312011-11-10 02:08:26 +0000186 // TODO(quiche): Anything else to do?
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700187
188 VLOG(3) << "WiFi " << link_name() << " task_factory_ "
189 << (task_factory_.empty() ? "is empty." : "is not empty.");
190 VLOG(3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
191 << (supplicant_process_proxy_.get() ? "is set." : "is not set.");
192 VLOG(3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
193 << (supplicant_interface_proxy_.get() ? "is set." : "is not set.");
mukesh agrawal7ec71312011-11-10 02:08:26 +0000194 VLOG(3) << "WiFi " << link_name() << " pending_service_ "
195 << (pending_service_.get() ? "is set." : "is not set.");
mukesh agrawal165e6142011-11-22 02:22:56 +0000196 VLOG(3) << "WiFi " << link_name() << " has " << endpoint_by_rpcid_.size()
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700197 << " EndpointMap entries.";
mukesh agrawal165e6142011-11-22 02:22:56 +0000198 VLOG(3) << "WiFi " << link_name() << " has " << services_.size()
199 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700200}
201
Paul Stewarta41e38d2011-11-11 07:47:29 -0800202bool WiFi::Load(StoreInterface *storage) {
203 LoadHiddenServices(storage);
204 return Device::Load(storage);
205}
206
mukesh agrawal1830fa12011-09-26 14:31:40 -0700207void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700208 LOG(INFO) << __func__;
209
mukesh agrawal7ec71312011-11-10 02:08:26 +0000210 // Needs to send a D-Bus message, but may be called from D-Bus
211 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700212 // to event loop.
213 dispatcher()->PostTask(
214 task_factory_.NewRunnableMethod(&WiFi::ScanTask));
215}
216
Paul Stewartfdd16072011-09-16 12:41:35 -0700217bool WiFi::TechnologyIs(const Technology::Identifier type) const {
218 return type == Technology::kWifi;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700219}
220
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000221bool WiFi::IsConnectingTo(const WiFiService &service) const {
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800222 return pending_service_ == &service ||
223 (current_service_ == &service &&
224 service.state() != Service::kStateConnected);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000225}
226
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700227void WiFi::LinkEvent(unsigned int flags, unsigned int change) {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000228 // TODO(quiche): Figure out how to relate these events to supplicant
229 // events. E.g., may be we can ignore LinkEvent, in favor of events
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700230 // from SupplicantInterfaceProxy?
231 Device::LinkEvent(flags, change);
232 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
233 LOG(INFO) << link_name() << " is up; should start L3!";
234 link_up_ = true;
Paul Stewart2bf1d352011-12-06 15:02:55 -0800235 if (AcquireIPConfig()) {
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700236 SetServiceState(Service::kStateConfiguring);
237 } else {
238 LOG(ERROR) << "Unable to acquire DHCP config.";
239 }
240 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700241 LOG(INFO) << link_name() << " is down";
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700242 link_up_ = false;
mukesh agrawal7ec71312011-11-10 02:08:26 +0000243 // TODO(quiche): Attempt to reconnect to current SSID, another SSID,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700244 // or initiate a scan.
245 }
246}
247
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000248void WiFi::BSSAdded(const ::DBus::Path &path,
249 const map<string, ::DBus::Variant> &properties) {
250 // Called from a D-Bus signal handler, and nay need to send a D-Bus
251 // message. So defer work to event loop.
252 dispatcher()->PostTask(
253 task_factory_.NewRunnableMethod(&WiFi::BSSAddedTask, path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000254}
255
256void WiFi::BSSRemoved(const ::DBus::Path &path) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000257 // Called from a D-Bus signal handler, and nay need to send a D-Bus
258 // message. So defer work to event loop.
259 dispatcher()->PostTask(
260 task_factory_.NewRunnableMethod(&WiFi::BSSRemovedTask, path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700261}
262
mukesh agrawal7ec71312011-11-10 02:08:26 +0000263void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
mukesh agrawal15908392011-11-16 18:29:25 +0000264 LOG(INFO) << "In " << __func__ << "(): called";
265 // Called from D-Bus signal handler, but may need to send a D-Bus
266 // message. So defer work to event loop.
267 dispatcher()->PostTask(
268 task_factory_.NewRunnableMethod(&WiFi::PropertiesChangedTask,
269 properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000270}
271
mukesh agrawalb54601c2011-06-07 17:39:22 -0700272void WiFi::ScanDone() {
273 LOG(INFO) << __func__;
274
mukesh agrawal7ec71312011-11-10 02:08:26 +0000275 // Defer handling of scan result processing, because that processing
276 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700277 // registration can't be done in the context of a D-Bus signal
278 // handler.
Paul Stewartac4ac002011-08-26 12:04:26 -0700279 dispatcher()->PostTask(
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700280 task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700281}
282
mukesh agrawal6e277772011-09-29 15:04:23 -0700283void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000284 map<string, DBus::Variant> service_params) {
mukesh agrawal15908392011-11-16 18:29:25 +0000285 CHECK(service);
mukesh agrawal445e72c2011-06-22 11:13:50 -0700286 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700287
mukesh agrawal7ec71312011-11-10 02:08:26 +0000288 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000289 if (pending_service_ && pending_service_ == service) {
290 // TODO(quiche): Return an error to the caller. crosbug.com/23832
291 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
292 << service->friendly_name()
293 << ", which is already pending.";
294 return;
295 }
296
297 if (pending_service_ && pending_service_ != service) {
298 DisconnectFrom(pending_service_);
299 }
mukesh agrawal32399322011-09-01 10:53:43 -0700300
mukesh agrawal6e277772011-09-29 15:04:23 -0700301 try {
mukesh agrawal15908392011-11-16 18:29:25 +0000302 // TODO(quiche): Set a timeout here. In the normal case, we expect
303 // that, if wpa_supplicant fails to connect, it will eventually send
304 // a signal that its CurrentBSS has changed. But there may be cases
305 // where the signal is not sent. (crosbug.com/23206)
mukesh agrawal64896322011-12-01 01:13:10 +0000306 const uint32_t scan_ssid = 1; // "True": Use directed probe.
307 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
308 append_uint32(scan_ssid);
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800309 service_params[wpa_supplicant::kNetworkPropertyBgscan].writer().
310 append_string(CreateBgscanConfigString().c_str());
mukesh agrawal6e277772011-09-29 15:04:23 -0700311 network_path =
312 supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000313 rpcid_by_service_[service] = network_path;
mukesh agrawal6e277772011-09-29 15:04:23 -0700314 } catch (const DBus::Error e) { // NOLINT
315 LOG(ERROR) << "exception while adding network: " << e.what();
316 return;
317 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700318
mukesh agrawal445e72c2011-06-22 11:13:50 -0700319 supplicant_interface_proxy_->SelectNetwork(network_path);
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700320
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000321 pending_service_ = service;
322 CHECK(current_service_.get() != pending_service_.get());
323
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700324 // SelectService here (instead of in LinkEvent, like Ethernet), so
325 // that, if we fail to bring up L2, we can attribute failure correctly.
326 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000327 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700328 // reconsider if this is the right place to change the selected service.
329 // see discussion in crosbug.com/20191.
330 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000331}
332
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000333void WiFi::DisconnectFrom(WiFiService *service) {
334 if (service != current_service_ && service != pending_service_) {
335 // TODO(quiche): Once we have asynchronous reply support, we should
336 // generate a D-Bus error here. (crosbug.com/23832)
337 LOG(WARNING) << "In " << __func__ << "(): "
338 << " ignoring request to disconnect from service "
339 << service->friendly_name()
340 << " which is neither current nor pending";
341 return;
342 }
343
344 if (pending_service_ && service != pending_service_) {
345 // TODO(quiche): Once we have asynchronous reply support, we should
346 // generate a D-Bus error here. (crosbug.com/23832)
347 LOG(WARNING) << "In " << __func__ << "(): "
348 << " ignoring request to disconnect from service "
349 << service->friendly_name()
350 << " which is not the pending service.";
351 return;
352 }
353
354 if (!pending_service_ && service != current_service_) {
355 // TODO(quiche): Once we have asynchronous reply support, we should
356 // generate a D-Bus error here. (crosbug.com/23832)
357 LOG(WARNING) << "In " << __func__ << "(): "
358 << " ignoring request to disconnect from service "
359 << service->friendly_name()
360 << " which is not the current service.";
361 return;
362 }
363
364 pending_service_ = NULL;
365 try {
366 supplicant_interface_proxy_->Disconnect();
367 // We'll call RemoveNetwork and reset |current_service_| after
368 // supplicant notifies us that the CurrentBSS has changed.
369 } catch (const DBus::Error e) { // NOLINT
370 // Can't depend on getting a notification of CurrentBSS change.
371 // So effect changes immediately.
372 ReverseServiceMap::const_iterator rpcid_it =
373 rpcid_by_service_.find(service);
374 DCHECK(rpcid_it != rpcid_by_service_.end());
375 if (rpcid_it == rpcid_by_service_.end()) {
376 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
377 << service->friendly_name() << ": "
378 << "could not find supplicant network to disable.";
379 } else {
380 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
381 }
382 current_service_ = NULL;
383 }
384
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800385 CHECK(current_service_ == NULL ||
386 current_service_.get() != pending_service_.get());
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000387}
388
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000389bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800390 return !current_service_ && !pending_service_;
391}
392
mukesh agrawal4d0401c2012-01-06 16:05:31 -0800393string WiFi::CreateBgscanConfigString() {
394 return StringPrintf("%s:%d:%d:%d",
395 bgscan_method_.c_str(),
396 bgscan_short_interval_seconds_,
397 bgscan_signal_threshold_dbm_,
398 scan_interval_seconds_);
399}
400
401void WiFi::SetBgscanMethod(const string &method, Error *error) {
402 if (method != wpa_supplicant::kNetworkBgscanMethodSimple &&
403 method != wpa_supplicant::kNetworkBgscanMethodLearn) {
404 const string error_message =
405 StringPrintf("Unrecognized bgscan method %s", method.c_str());
406 LOG(WARNING) << error_message;
407 error->Populate(Error::kInvalidArguments, error_message);
408 return;
409 }
410
411 bgscan_method_ = method;
412 // We do not update kNetworkPropertyBgscan for |pending_service_| or
413 // |current_service_|, because supplicant does not allow for
414 // reconfiguration without disconnect and reconnect.
415}
416
417void WiFi::SetBgscanShortInterval(const uint16 &seconds, Error */*error*/) {
418 bgscan_short_interval_seconds_ = seconds;
419 // We do not update kNetworkPropertyBgscan for |pending_service_| or
420 // |current_service_|, because supplicant does not allow for
421 // reconfiguration without disconnect and reconnect.
422}
423
424void WiFi::SetBgscanSignalThreshold(const int32 &dbm, Error */*error*/) {
425 bgscan_signal_threshold_dbm_ = dbm;
426 // We do not update kNetworkPropertyBgscan for |pending_service_| or
427 // |current_service_|, because supplicant does not allow for
428 // reconfiguration without disconnect and reconnect.
429}
430
431void WiFi::SetScanInterval(const uint16 &seconds, Error */*error*/) {
432 scan_interval_seconds_ = seconds;
433 // We do not update |pending_service_| or |current_service_|, because
434 // supplicant does not allow for reconfiguration without disconnect
435 // and reconnect.
436
437 // TODO(quiche): Update scan timer. crosbug.com/24337
438}
439
mukesh agrawal165e6142011-11-22 02:22:56 +0000440// To avoid creating duplicate services, call FindServiceForEndpoint
441// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000442WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
443 bool hidden_ssid) {
444 WiFiServiceRefPtr service =
445 new WiFiService(control_interface(),
446 dispatcher(),
447 manager(),
448 this,
449 endpoint.ssid(),
450 endpoint.network_mode(),
451 endpoint.security_mode(),
452 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000453 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000454 return service;
455}
456
457void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
458 VLOG(3) << "WiFi " << link_name() << " CurrentBSS "
459 << supplicant_bss_ << " -> " << new_bss;
460 supplicant_bss_ = new_bss;
461 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
462 HandleDisconnect();
463 } else {
464 HandleRoam(new_bss);
465 }
466
467 SelectService(current_service_);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000468 // Invariant check: a Service can either be current, or pending, but
469 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000470 CHECK(current_service_.get() != pending_service_.get() ||
471 current_service_.get() == NULL);
472
473 // TODO(quiche): Update BSSID property on the Service
474 // (crosbug.com/22377).
475}
476
477void WiFi::HandleDisconnect() {
478 // Identify the affected service. We expect to get a disconnect
479 // event when we fall off a Service that we were connected
480 // to. However, we also allow for the case where we get a disconnect
481 // event while attempting to connect from a disconnected state.
482 WiFiService *affected_service =
483 current_service_.get() ? current_service_.get() : pending_service_.get();
484
485 current_service_ = NULL;
486 if (!affected_service) {
487 VLOG(2) << "WiFi " << link_name()
488 << " disconnected while not connected or connecting";
489 return;
490 }
491
492 ReverseServiceMap::const_iterator rpcid_it =
493 rpcid_by_service_.find(affected_service);
494 if (rpcid_it == rpcid_by_service_.end()) {
495 VLOG(2) << "WiFi " << link_name() << " disconnected from "
496 << " (or failed to connect to) "
497 << affected_service->friendly_name() << ", "
498 << "but could not find supplicant network to disable.";
499 return;
500 }
501
502 VLOG(2) << "WiFi " << link_name() << " disconnected from "
503 << " (or failed to connect to) "
504 << affected_service->friendly_name();
505 // TODO(quiche): Reconsider giving up immediately. Maybe give
506 // wpa_supplicant some time to retry, first.
507 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000508 // TODO(quiche): If we initated the disconnect, we should probably
509 // go to the idle state instead. crosbug.com/24700
510 affected_service->SetFailure(Service::kFailureUnknown);
mukesh agrawal15908392011-11-16 18:29:25 +0000511
512 if (affected_service == pending_service_.get()) {
513 // The attempt to connect to |pending_service_| failed. Clear
514 // |pending_service_|, to indicate we're no longer in the middle
515 // of a connect request.
516 pending_service_ = NULL;
517 } else if (pending_service_.get()) {
518 // We've attributed the disconnection to what was the
519 // |current_service_|, rather than the |pending_service_|.
520 //
521 // If we're wrong about that (i.e. supplicant reported this
522 // CurrentBSS change after attempting to connect to
523 // |pending_service_|), we're depending on supplicant to retry
524 // connecting to |pending_service_|, and delivering another
525 // CurrentBSS change signal in the future.
526 //
527 // Log this fact, to help us debug (in case our assumptions are
528 // wrong).
529 VLOG(2) << "WiFi " << link_name() << " pending connection to "
530 << pending_service_->friendly_name()
531 << " after disconnect";
532 }
533}
534
535// We use the term "Roam" loosely. In particular, we include the case
536// where we "Roam" to a BSS from the disconnected state.
537void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
538 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
539 if (endpoint_it == endpoint_by_rpcid_.end()) {
540 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
541 << new_bss;
542 return;
543 }
544
545 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000546 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000547 if (!service.get()) {
548 LOG(WARNING) << "WiFi " << link_name()
549 << " could not find Service for Endpoint "
550 << endpoint.bssid_string()
551 << " (service will be unchanged)";
552 return;
553 }
554
555 VLOG(2) << "WiFi " << link_name()
556 << " roamed to Endpoint " << endpoint.bssid_string()
557 << " (SSID " << endpoint.ssid_string() << ")";
558
559 if (pending_service_.get() &&
560 service.get() != pending_service_.get()) {
561 // The Service we've roamed on to is not the one we asked for.
562 // We assume that this is transient, and that wpa_supplicant
563 // is trying / will try to connect to |pending_service_|.
564 //
565 // If it succeeds, we'll end up back here, but with |service|
566 // pointing at the same service as |pending_service_|.
567 //
568 // If it fails, we'll process things in HandleDisconnect.
569 //
570 // So we leave |pending_service_| untouched.
571 VLOG(2) << "WiFi " << link_name()
572 << " new current Endpoint "
573 << endpoint.bssid_string()
574 << " is not part of pending service "
575 << pending_service_->friendly_name();
576
577 // Sanity check: if we didn't roam onto |pending_service_|, we
578 // should still be on |current_service_|.
579 if (service.get() != current_service_.get()) {
580 LOG(WARNING) << "WiFi " << link_name()
581 << " new current Endpoint "
582 << endpoint.bssid_string()
583 << " is neither part of pending service "
584 << pending_service_->friendly_name()
585 << " nor part of current service "
586 << (current_service_.get() ?
587 current_service_->friendly_name() :
588 "(NULL)");
589 // Although we didn't expect to get here, we should keep
590 // |current_service_| in sync with what supplicant has done.
591 current_service_ = service;
592 }
593 return;
594 }
595
596 if (pending_service_.get()) {
597 // We assume service.get() == pending_service_.get() here, because
598 // of the return in the previous if clause.
599 //
600 // Boring case: we've connected to the service we asked
601 // for. Simply update |current_service_| and |pending_service_|.
602 current_service_ = service;
603 pending_service_ = NULL;
604 return;
605 }
606
607 // |pending_service_| was NULL, so we weren't attempting to connect
608 // to a new Service. Sanity check that we're still on
609 // |current_service_|.
610 if (service.get() != current_service_.get()) {
611 LOG(WARNING)
612 << "WiFi " << link_name()
613 << " new current Endpoint "
614 << endpoint.bssid_string()
615 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000616 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000617 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000618 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000619 // We didn't expect to be here, but let's cope as well as we
620 // can. Update |current_service_| to keep it in sync with
621 // supplicant.
622 current_service_ = service;
623 return;
624 }
625
626 // At this point, we know that |pending_service_| was NULL, and that
627 // we're still on |current_service_|. This is the most boring case
628 // of all, because there's no state to update here.
629 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700630}
631
Paul Stewarta41e38d2011-11-11 07:47:29 -0800632WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
633 const string &mode,
634 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800635 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
636 it != services_.end();
637 ++it) {
638 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
639 (*it)->IsSecurityMatch(security)) {
640 return *it;
641 }
642 }
643 return NULL;
644}
645
mukesh agrawal165e6142011-11-22 02:22:56 +0000646WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
647 return FindService(endpoint.ssid(),
648 endpoint.network_mode(),
649 endpoint.security_mode());
650}
651
Paul Stewartced6a0b2011-11-08 15:32:04 -0800652ByteArrays WiFi::GetHiddenSSIDList() {
653 // Create a unique set of hidden SSIDs.
654 set<ByteArray> hidden_ssids_set;
655 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
656 it != services_.end();
657 ++it) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800658 if ((*it)->hidden_ssid() && (*it)->favorite()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800659 hidden_ssids_set.insert((*it)->ssid());
660 }
661 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800662 VLOG(2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800663 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
664 if (!hidden_ssids.empty()) {
665 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
666 // not fit in.
667 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
668 hidden_ssids.erase(
669 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
670 hidden_ssids.end());
671 }
672 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
673 // SSIDs to wpa_supplicant, we need to explicitly specify the default
674 // behavior of doing a broadcast probe.
675 hidden_ssids.push_back(ByteArray());
676 }
677 return hidden_ssids;
678}
679
Paul Stewarta41e38d2011-11-11 07:47:29 -0800680bool WiFi::LoadHiddenServices(StoreInterface *storage) {
681 bool created_hidden_service = false;
682 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
683 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
684 bool is_hidden = false;
685 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
686 VLOG(2) << "Storage group " << *it << " returned by GetGroupsWithKey "
687 << "failed for GetBool(" << flimflam::kWifiHiddenSsid
688 << ") -- possible non-bool key";
689 continue;
690 }
691 if (!is_hidden) {
692 continue;
693 }
694 string ssid_hex;
695 vector<uint8_t> ssid_bytes;
696 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
697 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
698 VLOG(2) << "Hidden network is missing/invalid \""
699 << flimflam::kSSIDProperty << "\" property";
700 continue;
701 }
702 string device_address;
703 string network_mode;
704 string security;
705 // It is gross that we have to do this, but the only place we can
706 // get information about the service is from its storage name.
707 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
708 &network_mode, &security) ||
709 device_address != address()) {
710 VLOG(2) << "Hidden network has unparsable storage identifier \""
711 << *it << "\"";
712 continue;
713 }
714 if (FindService(ssid_bytes, network_mode, security).get()) {
715 // If service already exists, we have nothing to do, since the
716 // service has already loaded its configuration from storage.
717 // This is guaranteed to happen in both cases where Load() is
718 // called on a device (via a ConfigureDevice() call on the
719 // profile):
720 // - In RegisterDevice() the Device hasn't been started yet,
721 // so it has no services registered, except for those
722 // created by previous iterations of LoadHiddenService().
723 // The latter can happen if another profile in the Manager's
724 // stack defines the same ssid/mode/security. Even this
725 // case is okay, since even if the profiles differ
726 // materially on configuration and credentials, the "right"
727 // one will be configured in the course of the
728 // RegisterService() call below.
729 // - In PushProfile(), all registered services have been
730 // introduced to the profile via ConfigureService() prior
731 // to calling ConfigureDevice() on the profile.
732 continue;
733 }
734 WiFiServiceRefPtr service(new WiFiService(control_interface(),
735 dispatcher(),
736 manager(),
737 this,
738 ssid_bytes,
739 network_mode,
740 security,
741 true));
742 services_.push_back(service);
743
744 // By registering the service, the rest of the configuration
745 // will be loaded from the profile into the service via ConfigureService().
746 manager()->RegisterService(service);
747
748 created_hidden_service = true;
749 }
750
751 // If we are idle and we created a service as a result of opening the
752 // profile, we should initiate a scan for our new hidden SSID.
753 if (running() && created_hidden_service &&
754 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
755 Scan(NULL);
756 }
757
758 return created_hidden_service;
759}
760
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000761void WiFi::BSSAddedTask(
762 const ::DBus::Path &path,
763 const map<string, ::DBus::Variant> &properties) {
764 // TODO(quiche): Write test to verify correct behavior in the case
765 // where we get multiple BSSAdded events for a single endpoint.
766 // (Old Endpoint's refcount should fall to zero, and old Endpoint
767 // should be destroyed.)
768 //
769 // Note: we assume that BSSIDs are unique across endpoints. This
770 // means that if an AP reuses the same BSSID for multiple SSIDs, we
771 // lose.
772 WiFiEndpointRefPtr endpoint(new WiFiEndpoint(properties));
773 endpoint_by_rpcid_[path] = endpoint;
774 LOG(INFO) << "Found endpoint. "
775 << "ssid: " << endpoint->ssid_string() << ", "
776 << "bssid: " << endpoint->bssid_string() << ", "
777 << "signal: " << endpoint->signal_strength() << ", "
778 << "security: " << endpoint->security_mode();
779
780 if (endpoint->ssid_string().empty()) {
781 // Don't bother trying to find or create a Service for an Endpoint
782 // without an SSID. We wouldn't be able to connect to it anyway.
783 return;
784 }
785
786 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
787 if (service) {
788 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
789 << " to service " << service->friendly_name() << ".";
790 service->AddEndpoint(endpoint);
791
792 if (manager()->HasService(service)) {
793 manager()->UpdateService(service);
794 } else {
795 DCHECK_EQ(1, service->NumEndpoints()); // Expect registered by now if >1.
796 manager()->RegisterService(service);
797 }
798
799 return;
800 }
801
802 const bool hidden_ssid = false;
803 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
804 LOG(INFO) << "New service " << service->GetRpcIdentifier()
805 << " (" << service->friendly_name() << ")";
806 service->AddEndpoint(endpoint);
807 manager()->RegisterService(service);
808}
809
810void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
811 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
812 if (i == endpoint_by_rpcid_.end()) {
813 LOG(WARNING) << "WiFi " << link_name()
814 << " could not find BSS " << path
815 << " to remove.";
816 return;
817 }
818
819 WiFiEndpointRefPtr endpoint = i->second;
820 CHECK(endpoint);
821 endpoint_by_rpcid_.erase(i);
822
823 if (endpoint->ssid_string().empty()) {
824 // In BSSAdded, we don't create Services for Endpoints with empty
825 // SSIDs. So don't bother looking for a Service to update.
826 return;
827 }
828
829 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
830 CHECK(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000831 VLOG(2) << "Removing Endpoint " << endpoint->bssid_string()
832 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000833 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000834
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000835 bool disconnect_service = !service->HasEndpoints() &&
836 (service->IsConnecting() || service->IsConnected());
837 bool forget_service =
838 // Forget Services without Endpoints, except that we always keep
839 // hidden services around. (We need them around to populate the
840 // hidden SSIDs list.)
841 !service->HasEndpoints() && !service->hidden_ssid();
842 bool deregister_service =
843 // Only deregister a Service if we're forgetting it. Otherwise,
844 // Manager can't keep our configuration up-to-date (as Profiles
845 // change).
846 forget_service;
847
848 if (disconnect_service) {
849 DisconnectFrom(service);
850 }
851
852 if (deregister_service) {
853 manager()->DeregisterService(service);
854 } else {
855 manager()->UpdateService(service);
856 }
857
858 if (forget_service) {
859 vector<WiFiServiceRefPtr>::iterator it;
860 it = std::find(services_.begin(), services_.end(), service);
861 if (it != services_.end()) {
862 services_.erase(it);
863 }
864 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000865}
866
mukesh agrawal15908392011-11-16 18:29:25 +0000867void WiFi::PropertiesChangedTask(
868 const map<string, ::DBus::Variant> &properties) {
869 // TODO(quiche): Handle changes in other properties (e.g. signal
870 // strength).
871
872 // Note that order matters here. In particular, we want to process
873 // changes in the current BSS before changes in state. This is so
874 // that we update the state of the correct Endpoint/Service.
875
876 map<string, ::DBus::Variant>::const_iterator properties_it =
877 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
878 if (properties_it != properties.end()) {
879 CurrentBSSChanged(properties_it->second.reader().get_path());
880 }
881
882 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
883 if (properties_it != properties.end()) {
884 StateChanged(properties_it->second.reader().get_string());
885 }
886}
887
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700888void WiFi::ScanDoneTask() {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700889 LOG(INFO) << __func__;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700890 scan_pending_ = false;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700891}
892
mukesh agrawal32399322011-09-01 10:53:43 -0700893void WiFi::ScanTask() {
894 VLOG(2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800895 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -0700896 scan_args[wpa_supplicant::kPropertyScanType].writer().
897 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -0800898
899 ByteArrays hidden_ssids = GetHiddenSSIDList();
900 if (!hidden_ssids.empty()) {
901 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
902 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
903 }
904
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800905 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
mukesh agrawal32399322011-09-01 10:53:43 -0700906 supplicant_interface_proxy_->Scan(scan_args);
907 scan_pending_ = true;
908}
909
mukesh agrawal15908392011-11-16 18:29:25 +0000910void WiFi::StateChanged(const string &new_state) {
911 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +0000912 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +0000913 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
914 << old_state << " -> " << new_state;
915
916 WiFiService *affected_service;
917 // Identify the service to which the state change applies. If
918 // |pending_service_| is non-NULL, then the state change applies to
919 // |pending_service_|. Otherwise, it applies to |current_service_|.
920 //
921 // This policy is driven by the fact that the |pending_service_|
922 // doesn't become the |current_service_| until wpa_supplicant
923 // reports a CurrentBSS change to the |pending_service_|. And the
924 // CurrentBSS change won't we reported until the |pending_service_|
925 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
926 affected_service =
927 pending_service_.get() ? pending_service_.get() : current_service_.get();
928 if (!affected_service) {
929 VLOG(2) << "WiFi " << link_name() << " " << __func__
930 << " with no service";
931 return;
932 }
933
934 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
935 // TODO(quiche): Check if we have a race with LinkEvent and/or
936 // IPConfigUpdatedCallback here.
937
938 // After 802.11 negotiation is Completed, we start Configuring
939 // IP connectivity.
940 affected_service->SetState(Service::kStateConfiguring);
941 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000942 // TODO(quiche): Resolve race with LinkEvent. It's possible for us
943 // to receive this state notification after LinkEvent. In that case,
944 // our state transitions are Associating -> Configuring ->
945 // Associating -> Configuring -> Ready. (crosbug.com/22831)
mukesh agrawal15908392011-11-16 18:29:25 +0000946 affected_service->SetState(Service::kStateAssociating);
947 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
948 new_state == wpa_supplicant::kInterfaceStateAssociating ||
949 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
950 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
951 // Ignore transitions into these states from Completed, to avoid
952 // bothering the user when roaming, or re-keying.
953 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
954 affected_service->SetState(Service::kStateAssociating);
955 // TOOD(quiche): On backwards transitions, we should probably set
956 // a timeout for getting back into the completed state. At present,
957 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000958 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +0000959 // (crosbug.com/23207)
960 } else {
961 // Other transitions do not affect Service state.
962 //
963 // Note in particular that we ignore a State change into
964 // kInterfaceStateDisconnected, in favor of observing the corresponding
965 // change in CurrentBSS.
966 }
mukesh agrawal7ec71312011-11-10 02:08:26 +0000967}
968
969// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700970WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
971 if (!args.ContainsString(flimflam::kTypeProperty)) {
972 error->Populate(Error::kInvalidArguments, kManagerErrorTypeRequired);
973 return NULL;
974 }
975
976 if (args.GetString(flimflam::kTypeProperty) != flimflam::kTypeWifi) {
977 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceType);
978 return NULL;
979 }
980
981 if (args.ContainsString(flimflam::kModeProperty) &&
982 args.GetString(flimflam::kModeProperty) !=
983 flimflam::kModeManaged) {
984 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceMode);
985 return NULL;
986 }
987
988 if (!args.ContainsString(flimflam::kSSIDProperty)) {
989 error->Populate(Error::kInvalidArguments, kManagerErrorSSIDRequired);
990 return NULL;
991 }
992
993 string ssid = args.GetString(flimflam::kSSIDProperty);
994 if (ssid.length() < 1) {
995 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooShort);
996 return NULL;
997 }
998
999 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
1000 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooLong);
1001 return NULL;
1002 }
1003
1004 string security_method;
1005 if (args.ContainsString(flimflam::kSecurityProperty)) {
1006 security_method = args.GetString(flimflam::kSecurityProperty);
1007 } else {
1008 security_method = flimflam::kSecurityNone;
1009 }
1010
1011 if (security_method != flimflam::kSecurityNone &&
1012 security_method != flimflam::kSecurityWep &&
1013 security_method != flimflam::kSecurityPsk &&
1014 security_method != flimflam::kSecurityWpa &&
1015 security_method != flimflam::kSecurityRsn &&
1016 security_method != flimflam::kSecurity8021x) {
1017 error->Populate(Error::kNotSupported,
1018 kManagerErrorUnsupportedSecurityMode);
1019 return NULL;
1020 }
1021
1022 if ((security_method == flimflam::kSecurityWep ||
1023 security_method == flimflam::kSecurityPsk ||
1024 security_method == flimflam::kSecurityWpa ||
1025 security_method == flimflam::kSecurityRsn) &&
1026 !args.ContainsString(flimflam::kPassphraseProperty)) {
1027 error->Populate(Error::kInvalidArguments,
1028 kManagerErrorPassphraseRequired);
1029 return NULL;
1030 }
1031
Paul Stewart6ab23a92011-11-09 17:17:47 -08001032 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001033 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
1034 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -08001035 } else {
1036 // If the service is not found, and the caller hasn't specified otherwise,
1037 // we assume this is is a hidden network.
1038 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -08001039 }
1040
Paul Stewart6ab23a92011-11-09 17:17:47 -08001041 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
1042 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
1043 security_method));
1044 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -07001045 service = new WiFiService(control_interface(),
1046 dispatcher(),
1047 manager(),
1048 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -08001049 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -07001050 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -08001051 security_method,
1052 hidden_ssid);
1053 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +00001054 // NB: We do not register the newly created Service with the Manager.
1055 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001056 }
1057
mukesh agrawal1a056262011-10-05 14:36:54 -07001058 if (security_method == flimflam::kSecurityWep ||
1059 security_method == flimflam::kSecurityPsk ||
1060 security_method == flimflam::kSecurityWpa ||
1061 security_method == flimflam::kSecurityRsn) {
1062 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
1063 error);
1064 if (error->IsFailure()) {
1065 return NULL;
1066 }
1067 }
1068
mukesh agrawal7ec71312011-11-10 02:08:26 +00001069 // TODO(quiche): Apply any other configuration parameters.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001070
1071 return service;
1072}
1073
mukesh agrawal4d0401c2012-01-06 16:05:31 -08001074void WiFi::HelpRegisterDerivedInt32(
1075 PropertyStore *store,
1076 const string &name,
1077 int32(WiFi::*get)(Error *error),
1078 void(WiFi::*set)(const int32 &value, Error *error)) {
1079 store->RegisterDerivedInt32(
1080 name,
1081 Int32Accessor(new CustomAccessor<WiFi, int32>(this, get, set)));
1082}
1083
1084void WiFi::HelpRegisterDerivedString(
1085 PropertyStore *store,
1086 const string &name,
1087 string(WiFi::*get)(Error *error),
1088 void(WiFi::*set)(const string &value, Error *error)) {
1089 store->RegisterDerivedString(
1090 name,
1091 StringAccessor(new CustomAccessor<WiFi, string>(this, get, set)));
1092}
1093
1094void WiFi::HelpRegisterDerivedUint16(
1095 PropertyStore *store,
1096 const string &name,
1097 uint16(WiFi::*get)(Error *error),
1098 void(WiFi::*set)(const uint16 &value, Error *error)) {
1099 store->RegisterDerivedUint16(
1100 name,
1101 Uint16Accessor(new CustomAccessor<WiFi, uint16>(this, get, set)));
1102}
1103
Paul Stewartb50f0b92011-05-16 16:31:42 -07001104} // namespace shill