blob: cc8fd0070e3d35cf0e00f69ec68bef39f2e5143b [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"
Darin Petkovd1967262011-07-18 14:55:18 -070034#include "shill/proxy_factory.h"
Paul Stewarta41e38d2011-11-11 07:47:29 -080035#include "shill/store_interface.h"
mukesh agrawalaf571952011-07-14 14:31:12 -070036#include "shill/supplicant_interface_proxy_interface.h"
37#include "shill/supplicant_process_proxy_interface.h"
Gaurav Shah435de2c2011-11-17 19:01:07 -080038#include "shill/technology.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070039#include "shill/wifi_endpoint.h"
40#include "shill/wifi_service.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070041#include "shill/wpa_supplicant.h"
Paul Stewartb50f0b92011-05-16 16:31:42 -070042
mukesh agrawal15908392011-11-16 18:29:25 +000043using base::StringPrintf;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070044using std::map;
Paul Stewartced6a0b2011-11-08 15:32:04 -080045using std::set;
mukesh agrawalab87ea42011-05-18 11:44:49 -070046using std::string;
mukesh agrawal7a4e4002011-09-06 11:26:05 -070047using std::vector;
mukesh agrawalab87ea42011-05-18 11:44:49 -070048
Paul Stewartb50f0b92011-05-16 16:31:42 -070049namespace shill {
mukesh agrawal7a4e4002011-09-06 11:26:05 -070050
51// statics
52//
53// Note that WiFi generates some manager-level errors, because it implements
54// the Manager.GetWiFiService flimflam API. The API is implemented here,
55// rather than in manager, to keep WiFi-specific logic in the right place.
56const char WiFi::kManagerErrorPassphraseRequired[] = "must specify passphrase";
57const char WiFi::kManagerErrorSSIDRequired[] = "must specify SSID";
58const char WiFi::kManagerErrorSSIDTooLong[] = "SSID is too long";
59const char WiFi::kManagerErrorSSIDTooShort[] = "SSID is too short";
60const char WiFi::kManagerErrorTypeRequired[] = "must specify service type";
61const char WiFi::kManagerErrorUnsupportedSecurityMode[] =
62 "security mode is unsupported";
63const char WiFi::kManagerErrorUnsupportedServiceType[] =
64 "service type is unsupported";
65const char WiFi::kManagerErrorUnsupportedServiceMode[] =
66 "service mode is unsupported";
mukesh agrawal7ec71312011-11-10 02:08:26 +000067const char WiFi::kInterfaceStateUnknown[] = "shill-unknown";
mukesh agrawalb54601c2011-06-07 17:39:22 -070068
Paul Stewartb50f0b92011-05-16 16:31:42 -070069WiFi::WiFi(ControlInterface *control_interface,
70 EventDispatcher *dispatcher,
Paul Stewartf1ce5d22011-05-19 13:10:20 -070071 Manager *manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070072 const string& link,
Paul Stewarta41e38d2011-11-11 07:47:29 -080073 const string &address,
Paul Stewartb50f0b92011-05-16 16:31:42 -070074 int interface_index)
Chris Masonea82b7112011-05-25 15:16:29 -070075 : Device(control_interface,
76 dispatcher,
77 manager,
Chris Masone3bd3c8c2011-06-13 08:20:26 -070078 link,
Chris Masone626719f2011-08-18 16:58:48 -070079 address,
Gaurav Shah435de2c2011-11-17 19:01:07 -080080 interface_index,
81 Technology::kWifi),
Darin Petkovab565bb2011-10-06 02:55:51 -070082 proxy_factory_(ProxyFactory::GetInstance()),
mukesh agrawalb54601c2011-06-07 17:39:22 -070083 task_factory_(this),
mukesh agrawal15908392011-11-16 18:29:25 +000084 link_up_(false),
85 supplicant_state_(kInterfaceStateUnknown),
86 supplicant_bss_("(unknown)"),
Chris Masone853b81b2011-06-24 14:11:41 -070087 bgscan_short_interval_(0),
88 bgscan_signal_threshold_(0),
89 scan_pending_(false),
mukesh agrawal15908392011-11-16 18:29:25 +000090 scan_interval_(0) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070091 PropertyStore *store = this->mutable_store();
Paul Stewartac4ac002011-08-26 12:04:26 -070092 store->RegisterString(flimflam::kBgscanMethodProperty, &bgscan_method_);
93 store->RegisterUint16(flimflam::kBgscanShortIntervalProperty,
Chris Masone853b81b2011-06-24 14:11:41 -070094 &bgscan_short_interval_);
Paul Stewartac4ac002011-08-26 12:04:26 -070095 store->RegisterInt32(flimflam::kBgscanSignalThresholdProperty,
Chris Masone853b81b2011-06-24 14:11:41 -070096 &bgscan_signal_threshold_);
97
Chris Masoneb925cc82011-06-22 15:39:57 -070098 // TODO(quiche): Decide if scan_pending_ is close enough to
99 // "currently scanning" that we don't care, or if we want to track
100 // scan pending/currently scanning/no scan scheduled as a tri-state
101 // kind of thing.
Paul Stewartac4ac002011-08-26 12:04:26 -0700102 store->RegisterConstBool(flimflam::kScanningProperty, &scan_pending_);
103 store->RegisterUint16(flimflam::kScanIntervalProperty, &scan_interval_);
104 VLOG(2) << "WiFi device " << link_name() << " initialized.";
Paul Stewartb50f0b92011-05-16 16:31:42 -0700105}
106
mukesh agrawalaf571952011-07-14 14:31:12 -0700107WiFi::~WiFi() {}
Paul Stewartb50f0b92011-05-16 16:31:42 -0700108
mukesh agrawalab87ea42011-05-18 11:44:49 -0700109void WiFi::Start() {
mukesh agrawalab87ea42011-05-18 11:44:49 -0700110 ::DBus::Path interface_path;
111
mukesh agrawalaf571952011-07-14 14:31:12 -0700112 supplicant_process_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700113 proxy_factory_->CreateSupplicantProcessProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700114 wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700115 try {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800116 map<string, DBus::Variant> create_interface_args;
mukesh agrawalc7426a42011-06-03 13:04:28 -0700117 create_interface_args["Ifname"].writer().
Paul Stewartac4ac002011-08-26 12:04:26 -0700118 append_string(link_name().c_str());
mukesh agrawalc7426a42011-06-03 13:04:28 -0700119 create_interface_args["Driver"].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700120 append_string(wpa_supplicant::kDriverNL80211);
mukesh agrawalc7426a42011-06-03 13:04:28 -0700121 // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
122 // (file with pkcs config info)
123 interface_path =
124 supplicant_process_proxy_->CreateInterface(create_interface_args);
125 } catch (const DBus::Error e) { // NOLINT
mukesh agrawal6e277772011-09-29 15:04:23 -0700126 if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
mukesh agrawalc7426a42011-06-03 13:04:28 -0700127 interface_path =
Paul Stewartac4ac002011-08-26 12:04:26 -0700128 supplicant_process_proxy_->GetInterface(link_name());
mukesh agrawal7ec71312011-11-10 02:08:26 +0000129 // TODO(quiche): Is it okay to crash here, if device is missing?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700130 } else {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000131 // TODO(quiche): Log error.
mukesh agrawalc7426a42011-06-03 13:04:28 -0700132 }
133 }
134
mukesh agrawalab87ea42011-05-18 11:44:49 -0700135 supplicant_interface_proxy_.reset(
Darin Petkovab565bb2011-10-06 02:55:51 -0700136 proxy_factory_->CreateSupplicantInterfaceProxy(
mukesh agrawal6e277772011-09-29 15:04:23 -0700137 this, interface_path, wpa_supplicant::kDBusAddr));
mukesh agrawalc7426a42011-06-03 13:04:28 -0700138
mukesh agrawal7ec71312011-11-10 02:08:26 +0000139 // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
mukesh agrawalc7426a42011-06-03 13:04:28 -0700140
mukesh agrawal7ec71312011-11-10 02:08:26 +0000141 // Clear out any networks that might previously have been configured
mukesh agrawalc7426a42011-06-03 13:04:28 -0700142 // for this interface.
143 supplicant_interface_proxy_->RemoveAllNetworks();
144
mukesh agrawal7ec71312011-11-10 02:08:26 +0000145 // Flush interface's BSS cache, so that we get BSSAdded signals for
mukesh agrawalc7426a42011-06-03 13:04:28 -0700146 // all BSSes (not just new ones since the last scan).
147 supplicant_interface_proxy_->FlushBSS(0);
148
Darin Petkovc0865312011-09-16 15:31:20 -0700149 Scan(NULL);
mukesh agrawalab87ea42011-05-18 11:44:49 -0700150 Device::Start();
151}
152
mukesh agrawalab87ea42011-05-18 11:44:49 -0700153void WiFi::Stop() {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700154 VLOG(2) << "WiFi " << link_name() << " stopping.";
mukesh agrawal7ec71312011-11-10 02:08:26 +0000155 // TODO(quiche): Remove interface from supplicant.
mukesh agrawal31950242011-07-14 11:53:38 -0700156 supplicant_interface_proxy_.reset(); // breaks a reference cycle
157 supplicant_process_proxy_.reset();
mukesh agrawal15908392011-11-16 18:29:25 +0000158 endpoint_by_rpcid_.clear();
mukesh agrawal15908392011-11-16 18:29:25 +0000159 rpcid_by_service_.clear();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700160
Paul Stewartced6a0b2011-11-08 15:32:04 -0800161 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
162 it != services_.end();
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700163 ++it) {
164 manager()->DeregisterService(*it);
165 }
Paul Stewartced6a0b2011-11-08 15:32:04 -0800166 services_.clear(); // breaks reference cycles
mukesh agrawal7ec71312011-11-10 02:08:26 +0000167 pending_service_ = NULL; // breaks a reference cycle
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700168
mukesh agrawalab87ea42011-05-18 11:44:49 -0700169 Device::Stop();
mukesh agrawal7ec71312011-11-10 02:08:26 +0000170 // TODO(quiche): Anything else to do?
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700171
172 VLOG(3) << "WiFi " << link_name() << " task_factory_ "
173 << (task_factory_.empty() ? "is empty." : "is not empty.");
174 VLOG(3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
175 << (supplicant_process_proxy_.get() ? "is set." : "is not set.");
176 VLOG(3) << "WiFi " << link_name() << " supplicant_interface_proxy_ "
177 << (supplicant_interface_proxy_.get() ? "is set." : "is not set.");
mukesh agrawal7ec71312011-11-10 02:08:26 +0000178 VLOG(3) << "WiFi " << link_name() << " pending_service_ "
179 << (pending_service_.get() ? "is set." : "is not set.");
mukesh agrawal165e6142011-11-22 02:22:56 +0000180 VLOG(3) << "WiFi " << link_name() << " has " << endpoint_by_rpcid_.size()
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700181 << " EndpointMap entries.";
mukesh agrawal165e6142011-11-22 02:22:56 +0000182 VLOG(3) << "WiFi " << link_name() << " has " << services_.size()
183 << " Services.";
mukesh agrawalab87ea42011-05-18 11:44:49 -0700184}
185
Paul Stewarta41e38d2011-11-11 07:47:29 -0800186bool WiFi::Load(StoreInterface *storage) {
187 LoadHiddenServices(storage);
188 return Device::Load(storage);
189}
190
mukesh agrawal1830fa12011-09-26 14:31:40 -0700191void WiFi::Scan(Error */*error*/) {
mukesh agrawal32399322011-09-01 10:53:43 -0700192 LOG(INFO) << __func__;
193
mukesh agrawal7ec71312011-11-10 02:08:26 +0000194 // Needs to send a D-Bus message, but may be called from D-Bus
195 // signal handler context (via Manager::RequestScan). So defer work
mukesh agrawal32399322011-09-01 10:53:43 -0700196 // to event loop.
197 dispatcher()->PostTask(
198 task_factory_.NewRunnableMethod(&WiFi::ScanTask));
199}
200
Paul Stewartfdd16072011-09-16 12:41:35 -0700201bool WiFi::TechnologyIs(const Technology::Identifier type) const {
202 return type == Technology::kWifi;
Paul Stewartb50f0b92011-05-16 16:31:42 -0700203}
204
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000205bool WiFi::IsConnectingTo(const WiFiService &service) const {
206 return pending_service_ == &service
207 || (current_service_ == &service
208 && service.state() != Service::kStateConnected);
209}
210
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700211void WiFi::LinkEvent(unsigned int flags, unsigned int change) {
mukesh agrawal7ec71312011-11-10 02:08:26 +0000212 // TODO(quiche): Figure out how to relate these events to supplicant
213 // events. E.g., may be we can ignore LinkEvent, in favor of events
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700214 // from SupplicantInterfaceProxy?
215 Device::LinkEvent(flags, change);
216 if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
217 LOG(INFO) << link_name() << " is up; should start L3!";
218 link_up_ = true;
Paul Stewart2bf1d352011-12-06 15:02:55 -0800219 if (AcquireIPConfig()) {
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700220 SetServiceState(Service::kStateConfiguring);
221 } else {
222 LOG(ERROR) << "Unable to acquire DHCP config.";
223 }
224 } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
mukesh agrawal5c4dd0b2011-09-14 13:53:14 -0700225 LOG(INFO) << link_name() << " is down";
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700226 link_up_ = false;
mukesh agrawal7ec71312011-11-10 02:08:26 +0000227 // TODO(quiche): Attempt to reconnect to current SSID, another SSID,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700228 // or initiate a scan.
229 }
230}
231
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000232void WiFi::BSSAdded(const ::DBus::Path &path,
233 const map<string, ::DBus::Variant> &properties) {
234 // Called from a D-Bus signal handler, and nay need to send a D-Bus
235 // message. So defer work to event loop.
236 dispatcher()->PostTask(
237 task_factory_.NewRunnableMethod(&WiFi::BSSAddedTask, path, properties));
mukesh agrawal261daca2011-12-02 18:56:56 +0000238}
239
240void WiFi::BSSRemoved(const ::DBus::Path &path) {
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000241 // Called from a D-Bus signal handler, and nay need to send a D-Bus
242 // message. So defer work to event loop.
243 dispatcher()->PostTask(
244 task_factory_.NewRunnableMethod(&WiFi::BSSRemovedTask, path));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700245}
246
mukesh agrawal7ec71312011-11-10 02:08:26 +0000247void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
mukesh agrawal15908392011-11-16 18:29:25 +0000248 LOG(INFO) << "In " << __func__ << "(): called";
249 // Called from D-Bus signal handler, but may need to send a D-Bus
250 // message. So defer work to event loop.
251 dispatcher()->PostTask(
252 task_factory_.NewRunnableMethod(&WiFi::PropertiesChangedTask,
253 properties));
mukesh agrawal7ec71312011-11-10 02:08:26 +0000254}
255
mukesh agrawalb54601c2011-06-07 17:39:22 -0700256void WiFi::ScanDone() {
257 LOG(INFO) << __func__;
258
mukesh agrawal7ec71312011-11-10 02:08:26 +0000259 // Defer handling of scan result processing, because that processing
260 // may require the the registration of new D-Bus objects. And such
mukesh agrawalb54601c2011-06-07 17:39:22 -0700261 // registration can't be done in the context of a D-Bus signal
262 // handler.
Paul Stewartac4ac002011-08-26 12:04:26 -0700263 dispatcher()->PostTask(
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700264 task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
mukesh agrawalb54601c2011-06-07 17:39:22 -0700265}
266
mukesh agrawal6e277772011-09-29 15:04:23 -0700267void WiFi::ConnectTo(WiFiService *service,
mukesh agrawal64896322011-12-01 01:13:10 +0000268 map<string, DBus::Variant> service_params) {
mukesh agrawal15908392011-11-16 18:29:25 +0000269 CHECK(service);
mukesh agrawal445e72c2011-06-22 11:13:50 -0700270 DBus::Path network_path;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700271
mukesh agrawal7ec71312011-11-10 02:08:26 +0000272 // TODO(quiche): Handle cases where already connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000273 if (pending_service_ && pending_service_ == service) {
274 // TODO(quiche): Return an error to the caller. crosbug.com/23832
275 LOG(INFO) << "WiFi " << link_name() << " ignoring ConnectTo "
276 << service->friendly_name()
277 << ", which is already pending.";
278 return;
279 }
280
281 if (pending_service_ && pending_service_ != service) {
282 DisconnectFrom(pending_service_);
283 }
mukesh agrawal32399322011-09-01 10:53:43 -0700284
mukesh agrawal6e277772011-09-29 15:04:23 -0700285 try {
mukesh agrawal15908392011-11-16 18:29:25 +0000286 // TODO(quiche): Set a timeout here. In the normal case, we expect
287 // that, if wpa_supplicant fails to connect, it will eventually send
288 // a signal that its CurrentBSS has changed. But there may be cases
289 // where the signal is not sent. (crosbug.com/23206)
mukesh agrawal64896322011-12-01 01:13:10 +0000290 const uint32_t scan_ssid = 1; // "True": Use directed probe.
291 service_params[wpa_supplicant::kNetworkPropertyScanSSID].writer().
292 append_uint32(scan_ssid);
mukesh agrawal6e277772011-09-29 15:04:23 -0700293 network_path =
294 supplicant_interface_proxy_->AddNetwork(service_params);
mukesh agrawal15908392011-11-16 18:29:25 +0000295 rpcid_by_service_[service] = network_path;
mukesh agrawal6e277772011-09-29 15:04:23 -0700296 } catch (const DBus::Error e) { // NOLINT
297 LOG(ERROR) << "exception while adding network: " << e.what();
298 return;
299 }
mukesh agrawal445e72c2011-06-22 11:13:50 -0700300
mukesh agrawal445e72c2011-06-22 11:13:50 -0700301 supplicant_interface_proxy_->SelectNetwork(network_path);
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700302
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000303 pending_service_ = service;
304 CHECK(current_service_.get() != pending_service_.get());
305
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700306 // SelectService here (instead of in LinkEvent, like Ethernet), so
307 // that, if we fail to bring up L2, we can attribute failure correctly.
308 //
mukesh agrawal7ec71312011-11-10 02:08:26 +0000309 // TODO(quiche): When we add code for dealing with connection failures,
mukesh agrawalf2f68a52011-09-01 12:15:48 -0700310 // reconsider if this is the right place to change the selected service.
311 // see discussion in crosbug.com/20191.
312 SelectService(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000313}
314
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000315void WiFi::DisconnectFrom(WiFiService *service) {
316 if (service != current_service_ && service != pending_service_) {
317 // TODO(quiche): Once we have asynchronous reply support, we should
318 // generate a D-Bus error here. (crosbug.com/23832)
319 LOG(WARNING) << "In " << __func__ << "(): "
320 << " ignoring request to disconnect from service "
321 << service->friendly_name()
322 << " which is neither current nor pending";
323 return;
324 }
325
326 if (pending_service_ && service != pending_service_) {
327 // TODO(quiche): Once we have asynchronous reply support, we should
328 // generate a D-Bus error here. (crosbug.com/23832)
329 LOG(WARNING) << "In " << __func__ << "(): "
330 << " ignoring request to disconnect from service "
331 << service->friendly_name()
332 << " which is not the pending service.";
333 return;
334 }
335
336 if (!pending_service_ && service != current_service_) {
337 // TODO(quiche): Once we have asynchronous reply support, we should
338 // generate a D-Bus error here. (crosbug.com/23832)
339 LOG(WARNING) << "In " << __func__ << "(): "
340 << " ignoring request to disconnect from service "
341 << service->friendly_name()
342 << " which is not the current service.";
343 return;
344 }
345
346 pending_service_ = NULL;
347 try {
348 supplicant_interface_proxy_->Disconnect();
349 // We'll call RemoveNetwork and reset |current_service_| after
350 // supplicant notifies us that the CurrentBSS has changed.
351 } catch (const DBus::Error e) { // NOLINT
352 // Can't depend on getting a notification of CurrentBSS change.
353 // So effect changes immediately.
354 ReverseServiceMap::const_iterator rpcid_it =
355 rpcid_by_service_.find(service);
356 DCHECK(rpcid_it != rpcid_by_service_.end());
357 if (rpcid_it == rpcid_by_service_.end()) {
358 LOG(WARNING) << "WiFi " << link_name() << " can not disconnect from "
359 << service->friendly_name() << ": "
360 << "could not find supplicant network to disable.";
361 } else {
362 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
363 }
364 current_service_ = NULL;
365 }
366
367 CHECK(current_service_ == NULL
368 || current_service_.get() != pending_service_.get());
369}
370
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000371bool WiFi::IsIdle() const {
Paul Stewart3d9bcf52011-12-12 15:02:22 -0800372 return !current_service_ && !pending_service_;
373}
374
mukesh agrawal165e6142011-11-22 02:22:56 +0000375// To avoid creating duplicate services, call FindServiceForEndpoint
376// before calling this method.
mukesh agrawal15908392011-11-16 18:29:25 +0000377WiFiServiceRefPtr WiFi::CreateServiceForEndpoint(const WiFiEndpoint &endpoint,
378 bool hidden_ssid) {
379 WiFiServiceRefPtr service =
380 new WiFiService(control_interface(),
381 dispatcher(),
382 manager(),
383 this,
384 endpoint.ssid(),
385 endpoint.network_mode(),
386 endpoint.security_mode(),
387 hidden_ssid);
mukesh agrawal165e6142011-11-22 02:22:56 +0000388 services_.push_back(service);
mukesh agrawal15908392011-11-16 18:29:25 +0000389 return service;
390}
391
392void WiFi::CurrentBSSChanged(const ::DBus::Path &new_bss) {
393 VLOG(3) << "WiFi " << link_name() << " CurrentBSS "
394 << supplicant_bss_ << " -> " << new_bss;
395 supplicant_bss_ = new_bss;
396 if (new_bss == wpa_supplicant::kCurrentBSSNull) {
397 HandleDisconnect();
398 } else {
399 HandleRoam(new_bss);
400 }
401
402 SelectService(current_service_);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000403 // Invariant check: a Service can either be current, or pending, but
404 // not both.
mukesh agrawal15908392011-11-16 18:29:25 +0000405 CHECK(current_service_.get() != pending_service_.get() ||
406 current_service_.get() == NULL);
407
408 // TODO(quiche): Update BSSID property on the Service
409 // (crosbug.com/22377).
410}
411
412void WiFi::HandleDisconnect() {
413 // Identify the affected service. We expect to get a disconnect
414 // event when we fall off a Service that we were connected
415 // to. However, we also allow for the case where we get a disconnect
416 // event while attempting to connect from a disconnected state.
417 WiFiService *affected_service =
418 current_service_.get() ? current_service_.get() : pending_service_.get();
419
420 current_service_ = NULL;
421 if (!affected_service) {
422 VLOG(2) << "WiFi " << link_name()
423 << " disconnected while not connected or connecting";
424 return;
425 }
426
427 ReverseServiceMap::const_iterator rpcid_it =
428 rpcid_by_service_.find(affected_service);
429 if (rpcid_it == rpcid_by_service_.end()) {
430 VLOG(2) << "WiFi " << link_name() << " disconnected from "
431 << " (or failed to connect to) "
432 << affected_service->friendly_name() << ", "
433 << "but could not find supplicant network to disable.";
434 return;
435 }
436
437 VLOG(2) << "WiFi " << link_name() << " disconnected from "
438 << " (or failed to connect to) "
439 << affected_service->friendly_name();
440 // TODO(quiche): Reconsider giving up immediately. Maybe give
441 // wpa_supplicant some time to retry, first.
442 supplicant_interface_proxy_->RemoveNetwork(rpcid_it->second);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000443 // TODO(quiche): If we initated the disconnect, we should probably
444 // go to the idle state instead. crosbug.com/24700
445 affected_service->SetFailure(Service::kFailureUnknown);
mukesh agrawal15908392011-11-16 18:29:25 +0000446
447 if (affected_service == pending_service_.get()) {
448 // The attempt to connect to |pending_service_| failed. Clear
449 // |pending_service_|, to indicate we're no longer in the middle
450 // of a connect request.
451 pending_service_ = NULL;
452 } else if (pending_service_.get()) {
453 // We've attributed the disconnection to what was the
454 // |current_service_|, rather than the |pending_service_|.
455 //
456 // If we're wrong about that (i.e. supplicant reported this
457 // CurrentBSS change after attempting to connect to
458 // |pending_service_|), we're depending on supplicant to retry
459 // connecting to |pending_service_|, and delivering another
460 // CurrentBSS change signal in the future.
461 //
462 // Log this fact, to help us debug (in case our assumptions are
463 // wrong).
464 VLOG(2) << "WiFi " << link_name() << " pending connection to "
465 << pending_service_->friendly_name()
466 << " after disconnect";
467 }
468}
469
470// We use the term "Roam" loosely. In particular, we include the case
471// where we "Roam" to a BSS from the disconnected state.
472void WiFi::HandleRoam(const ::DBus::Path &new_bss) {
473 EndpointMap::iterator endpoint_it = endpoint_by_rpcid_.find(new_bss);
474 if (endpoint_it == endpoint_by_rpcid_.end()) {
475 LOG(WARNING) << "WiFi " << link_name() << " connected to unknown BSS "
476 << new_bss;
477 return;
478 }
479
480 const WiFiEndpoint &endpoint(*endpoint_it->second);
mukesh agrawal165e6142011-11-22 02:22:56 +0000481 WiFiServiceRefPtr service = FindServiceForEndpoint(endpoint);
mukesh agrawal15908392011-11-16 18:29:25 +0000482 if (!service.get()) {
483 LOG(WARNING) << "WiFi " << link_name()
484 << " could not find Service for Endpoint "
485 << endpoint.bssid_string()
486 << " (service will be unchanged)";
487 return;
488 }
489
490 VLOG(2) << "WiFi " << link_name()
491 << " roamed to Endpoint " << endpoint.bssid_string()
492 << " (SSID " << endpoint.ssid_string() << ")";
493
494 if (pending_service_.get() &&
495 service.get() != pending_service_.get()) {
496 // The Service we've roamed on to is not the one we asked for.
497 // We assume that this is transient, and that wpa_supplicant
498 // is trying / will try to connect to |pending_service_|.
499 //
500 // If it succeeds, we'll end up back here, but with |service|
501 // pointing at the same service as |pending_service_|.
502 //
503 // If it fails, we'll process things in HandleDisconnect.
504 //
505 // So we leave |pending_service_| untouched.
506 VLOG(2) << "WiFi " << link_name()
507 << " new current Endpoint "
508 << endpoint.bssid_string()
509 << " is not part of pending service "
510 << pending_service_->friendly_name();
511
512 // Sanity check: if we didn't roam onto |pending_service_|, we
513 // should still be on |current_service_|.
514 if (service.get() != current_service_.get()) {
515 LOG(WARNING) << "WiFi " << link_name()
516 << " new current Endpoint "
517 << endpoint.bssid_string()
518 << " is neither part of pending service "
519 << pending_service_->friendly_name()
520 << " nor part of current service "
521 << (current_service_.get() ?
522 current_service_->friendly_name() :
523 "(NULL)");
524 // Although we didn't expect to get here, we should keep
525 // |current_service_| in sync with what supplicant has done.
526 current_service_ = service;
527 }
528 return;
529 }
530
531 if (pending_service_.get()) {
532 // We assume service.get() == pending_service_.get() here, because
533 // of the return in the previous if clause.
534 //
535 // Boring case: we've connected to the service we asked
536 // for. Simply update |current_service_| and |pending_service_|.
537 current_service_ = service;
538 pending_service_ = NULL;
539 return;
540 }
541
542 // |pending_service_| was NULL, so we weren't attempting to connect
543 // to a new Service. Sanity check that we're still on
544 // |current_service_|.
545 if (service.get() != current_service_.get()) {
546 LOG(WARNING)
547 << "WiFi " << link_name()
548 << " new current Endpoint "
549 << endpoint.bssid_string()
550 << (current_service_.get() ?
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000551 StringPrintf(" is not part of current service %s",
mukesh agrawal15908392011-11-16 18:29:25 +0000552 current_service_->friendly_name().c_str()) :
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000553 " with no current service");
mukesh agrawal15908392011-11-16 18:29:25 +0000554 // We didn't expect to be here, but let's cope as well as we
555 // can. Update |current_service_| to keep it in sync with
556 // supplicant.
557 current_service_ = service;
558 return;
559 }
560
561 // At this point, we know that |pending_service_| was NULL, and that
562 // we're still on |current_service_|. This is the most boring case
563 // of all, because there's no state to update here.
564 return;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700565}
566
Paul Stewarta41e38d2011-11-11 07:47:29 -0800567WiFiServiceRefPtr WiFi::FindService(const vector<uint8_t> &ssid,
568 const string &mode,
569 const string &security) const {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800570 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
571 it != services_.end();
572 ++it) {
573 if ((*it)->ssid() == ssid && (*it)->mode() == mode &&
574 (*it)->IsSecurityMatch(security)) {
575 return *it;
576 }
577 }
578 return NULL;
579}
580
mukesh agrawal165e6142011-11-22 02:22:56 +0000581WiFiServiceRefPtr WiFi::FindServiceForEndpoint(const WiFiEndpoint &endpoint) {
582 return FindService(endpoint.ssid(),
583 endpoint.network_mode(),
584 endpoint.security_mode());
585}
586
Paul Stewartced6a0b2011-11-08 15:32:04 -0800587ByteArrays WiFi::GetHiddenSSIDList() {
588 // Create a unique set of hidden SSIDs.
589 set<ByteArray> hidden_ssids_set;
590 for (vector<WiFiServiceRefPtr>::const_iterator it = services_.begin();
591 it != services_.end();
592 ++it) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800593 if ((*it)->hidden_ssid() && (*it)->favorite()) {
Paul Stewartced6a0b2011-11-08 15:32:04 -0800594 hidden_ssids_set.insert((*it)->ssid());
595 }
596 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800597 VLOG(2) << "Found " << hidden_ssids_set.size() << " hidden services";
Paul Stewartced6a0b2011-11-08 15:32:04 -0800598 ByteArrays hidden_ssids(hidden_ssids_set.begin(), hidden_ssids_set.end());
599 if (!hidden_ssids.empty()) {
600 // TODO(pstew): Devise a better method for time-sharing with SSIDs that do
601 // not fit in.
602 if (hidden_ssids.size() >= wpa_supplicant::kScanMaxSSIDsPerScan) {
603 hidden_ssids.erase(
604 hidden_ssids.begin() + wpa_supplicant::kScanMaxSSIDsPerScan - 1,
605 hidden_ssids.end());
606 }
607 // Add Broadcast SSID, signified by an empty ByteArray. If we specify
608 // SSIDs to wpa_supplicant, we need to explicitly specify the default
609 // behavior of doing a broadcast probe.
610 hidden_ssids.push_back(ByteArray());
611 }
612 return hidden_ssids;
613}
614
Paul Stewarta41e38d2011-11-11 07:47:29 -0800615bool WiFi::LoadHiddenServices(StoreInterface *storage) {
616 bool created_hidden_service = false;
617 set<string> groups = storage->GetGroupsWithKey(flimflam::kWifiHiddenSsid);
618 for (set<string>::iterator it = groups.begin(); it != groups.end(); ++it) {
619 bool is_hidden = false;
620 if (!storage->GetBool(*it, flimflam::kWifiHiddenSsid, &is_hidden)) {
621 VLOG(2) << "Storage group " << *it << " returned by GetGroupsWithKey "
622 << "failed for GetBool(" << flimflam::kWifiHiddenSsid
623 << ") -- possible non-bool key";
624 continue;
625 }
626 if (!is_hidden) {
627 continue;
628 }
629 string ssid_hex;
630 vector<uint8_t> ssid_bytes;
631 if (!storage->GetString(*it, flimflam::kSSIDProperty, &ssid_hex) ||
632 !base::HexStringToBytes(ssid_hex, &ssid_bytes)) {
633 VLOG(2) << "Hidden network is missing/invalid \""
634 << flimflam::kSSIDProperty << "\" property";
635 continue;
636 }
637 string device_address;
638 string network_mode;
639 string security;
640 // It is gross that we have to do this, but the only place we can
641 // get information about the service is from its storage name.
642 if (!WiFiService::ParseStorageIdentifier(*it, &device_address,
643 &network_mode, &security) ||
644 device_address != address()) {
645 VLOG(2) << "Hidden network has unparsable storage identifier \""
646 << *it << "\"";
647 continue;
648 }
649 if (FindService(ssid_bytes, network_mode, security).get()) {
650 // If service already exists, we have nothing to do, since the
651 // service has already loaded its configuration from storage.
652 // This is guaranteed to happen in both cases where Load() is
653 // called on a device (via a ConfigureDevice() call on the
654 // profile):
655 // - In RegisterDevice() the Device hasn't been started yet,
656 // so it has no services registered, except for those
657 // created by previous iterations of LoadHiddenService().
658 // The latter can happen if another profile in the Manager's
659 // stack defines the same ssid/mode/security. Even this
660 // case is okay, since even if the profiles differ
661 // materially on configuration and credentials, the "right"
662 // one will be configured in the course of the
663 // RegisterService() call below.
664 // - In PushProfile(), all registered services have been
665 // introduced to the profile via ConfigureService() prior
666 // to calling ConfigureDevice() on the profile.
667 continue;
668 }
669 WiFiServiceRefPtr service(new WiFiService(control_interface(),
670 dispatcher(),
671 manager(),
672 this,
673 ssid_bytes,
674 network_mode,
675 security,
676 true));
677 services_.push_back(service);
678
679 // By registering the service, the rest of the configuration
680 // will be loaded from the profile into the service via ConfigureService().
681 manager()->RegisterService(service);
682
683 created_hidden_service = true;
684 }
685
686 // If we are idle and we created a service as a result of opening the
687 // profile, we should initiate a scan for our new hidden SSID.
688 if (running() && created_hidden_service &&
689 supplicant_state_ == wpa_supplicant::kInterfaceStateInactive) {
690 Scan(NULL);
691 }
692
693 return created_hidden_service;
694}
695
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000696void WiFi::BSSAddedTask(
697 const ::DBus::Path &path,
698 const map<string, ::DBus::Variant> &properties) {
699 // TODO(quiche): Write test to verify correct behavior in the case
700 // where we get multiple BSSAdded events for a single endpoint.
701 // (Old Endpoint's refcount should fall to zero, and old Endpoint
702 // should be destroyed.)
703 //
704 // Note: we assume that BSSIDs are unique across endpoints. This
705 // means that if an AP reuses the same BSSID for multiple SSIDs, we
706 // lose.
707 WiFiEndpointRefPtr endpoint(new WiFiEndpoint(properties));
708 endpoint_by_rpcid_[path] = endpoint;
709 LOG(INFO) << "Found endpoint. "
710 << "ssid: " << endpoint->ssid_string() << ", "
711 << "bssid: " << endpoint->bssid_string() << ", "
712 << "signal: " << endpoint->signal_strength() << ", "
713 << "security: " << endpoint->security_mode();
714
715 if (endpoint->ssid_string().empty()) {
716 // Don't bother trying to find or create a Service for an Endpoint
717 // without an SSID. We wouldn't be able to connect to it anyway.
718 return;
719 }
720
721 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
722 if (service) {
723 LOG(INFO) << "Assigned endpoint " << endpoint->bssid_string()
724 << " to service " << service->friendly_name() << ".";
725 service->AddEndpoint(endpoint);
726
727 if (manager()->HasService(service)) {
728 manager()->UpdateService(service);
729 } else {
730 DCHECK_EQ(1, service->NumEndpoints()); // Expect registered by now if >1.
731 manager()->RegisterService(service);
732 }
733
734 return;
735 }
736
737 const bool hidden_ssid = false;
738 service = CreateServiceForEndpoint(*endpoint, hidden_ssid);
739 LOG(INFO) << "New service " << service->GetRpcIdentifier()
740 << " (" << service->friendly_name() << ")";
741 service->AddEndpoint(endpoint);
742 manager()->RegisterService(service);
743}
744
745void WiFi::BSSRemovedTask(const ::DBus::Path &path) {
746 EndpointMap::iterator i = endpoint_by_rpcid_.find(path);
747 if (i == endpoint_by_rpcid_.end()) {
748 LOG(WARNING) << "WiFi " << link_name()
749 << " could not find BSS " << path
750 << " to remove.";
751 return;
752 }
753
754 WiFiEndpointRefPtr endpoint = i->second;
755 CHECK(endpoint);
756 endpoint_by_rpcid_.erase(i);
757
758 if (endpoint->ssid_string().empty()) {
759 // In BSSAdded, we don't create Services for Endpoints with empty
760 // SSIDs. So don't bother looking for a Service to update.
761 return;
762 }
763
764 WiFiServiceRefPtr service = FindServiceForEndpoint(*endpoint);
765 CHECK(service);
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000766 VLOG(2) << "Removing Endpoint " << endpoint->bssid_string()
767 << " from Service " << service->friendly_name();
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000768 service->RemoveEndpoint(endpoint);
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000769
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000770 bool disconnect_service = !service->HasEndpoints() &&
771 (service->IsConnecting() || service->IsConnected());
772 bool forget_service =
773 // Forget Services without Endpoints, except that we always keep
774 // hidden services around. (We need them around to populate the
775 // hidden SSIDs list.)
776 !service->HasEndpoints() && !service->hidden_ssid();
777 bool deregister_service =
778 // Only deregister a Service if we're forgetting it. Otherwise,
779 // Manager can't keep our configuration up-to-date (as Profiles
780 // change).
781 forget_service;
782
783 if (disconnect_service) {
784 DisconnectFrom(service);
785 }
786
787 if (deregister_service) {
788 manager()->DeregisterService(service);
789 } else {
790 manager()->UpdateService(service);
791 }
792
793 if (forget_service) {
794 vector<WiFiServiceRefPtr>::iterator it;
795 it = std::find(services_.begin(), services_.end(), service);
796 if (it != services_.end()) {
797 services_.erase(it);
798 }
799 }
mukesh agrawalb4bc57d2011-12-07 01:07:47 +0000800}
801
mukesh agrawal15908392011-11-16 18:29:25 +0000802void WiFi::PropertiesChangedTask(
803 const map<string, ::DBus::Variant> &properties) {
804 // TODO(quiche): Handle changes in other properties (e.g. signal
805 // strength).
806
807 // Note that order matters here. In particular, we want to process
808 // changes in the current BSS before changes in state. This is so
809 // that we update the state of the correct Endpoint/Service.
810
811 map<string, ::DBus::Variant>::const_iterator properties_it =
812 properties.find(wpa_supplicant::kInterfacePropertyCurrentBSS);
813 if (properties_it != properties.end()) {
814 CurrentBSSChanged(properties_it->second.reader().get_path());
815 }
816
817 properties_it = properties.find(wpa_supplicant::kInterfacePropertyState);
818 if (properties_it != properties.end()) {
819 StateChanged(properties_it->second.reader().get_string());
820 }
821}
822
mukesh agrawaldc42bb32011-07-28 10:40:26 -0700823void WiFi::ScanDoneTask() {
mukesh agrawalb54601c2011-06-07 17:39:22 -0700824 LOG(INFO) << __func__;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700825 scan_pending_ = false;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700826}
827
mukesh agrawal32399322011-09-01 10:53:43 -0700828void WiFi::ScanTask() {
829 VLOG(2) << "WiFi " << link_name() << " scan requested.";
Paul Stewarta41e38d2011-11-11 07:47:29 -0800830 map<string, DBus::Variant> scan_args;
mukesh agrawal6e277772011-09-29 15:04:23 -0700831 scan_args[wpa_supplicant::kPropertyScanType].writer().
832 append_string(wpa_supplicant::kScanTypeActive);
Paul Stewartced6a0b2011-11-08 15:32:04 -0800833
834 ByteArrays hidden_ssids = GetHiddenSSIDList();
835 if (!hidden_ssids.empty()) {
836 scan_args[wpa_supplicant::kPropertyScanSSIDs] =
837 DBusAdaptor::ByteArraysToVariant(hidden_ssids);
838 }
839
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800840 // TODO(quiche): Indicate scanning in UI. crosbug.com/14887
mukesh agrawal32399322011-09-01 10:53:43 -0700841 supplicant_interface_proxy_->Scan(scan_args);
842 scan_pending_ = true;
843}
844
mukesh agrawal15908392011-11-16 18:29:25 +0000845void WiFi::StateChanged(const string &new_state) {
846 const string old_state = supplicant_state_;
mukesh agrawal7ec71312011-11-10 02:08:26 +0000847 supplicant_state_ = new_state;
mukesh agrawal15908392011-11-16 18:29:25 +0000848 LOG(INFO) << "WiFi " << link_name() << " " << __func__ << " "
849 << old_state << " -> " << new_state;
850
851 WiFiService *affected_service;
852 // Identify the service to which the state change applies. If
853 // |pending_service_| is non-NULL, then the state change applies to
854 // |pending_service_|. Otherwise, it applies to |current_service_|.
855 //
856 // This policy is driven by the fact that the |pending_service_|
857 // doesn't become the |current_service_| until wpa_supplicant
858 // reports a CurrentBSS change to the |pending_service_|. And the
859 // CurrentBSS change won't we reported until the |pending_service_|
860 // reaches the wpa_supplicant::kInterfaceStateCompleted state.
861 affected_service =
862 pending_service_.get() ? pending_service_.get() : current_service_.get();
863 if (!affected_service) {
864 VLOG(2) << "WiFi " << link_name() << " " << __func__
865 << " with no service";
866 return;
867 }
868
869 if (new_state == wpa_supplicant::kInterfaceStateCompleted) {
870 // TODO(quiche): Check if we have a race with LinkEvent and/or
871 // IPConfigUpdatedCallback here.
872
873 // After 802.11 negotiation is Completed, we start Configuring
874 // IP connectivity.
875 affected_service->SetState(Service::kStateConfiguring);
876 } else if (new_state == wpa_supplicant::kInterfaceStateAssociated) {
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000877 // TODO(quiche): Resolve race with LinkEvent. It's possible for us
878 // to receive this state notification after LinkEvent. In that case,
879 // our state transitions are Associating -> Configuring ->
880 // Associating -> Configuring -> Ready. (crosbug.com/22831)
mukesh agrawal15908392011-11-16 18:29:25 +0000881 affected_service->SetState(Service::kStateAssociating);
882 } else if (new_state == wpa_supplicant::kInterfaceStateAuthenticating ||
883 new_state == wpa_supplicant::kInterfaceStateAssociating ||
884 new_state == wpa_supplicant::kInterfaceState4WayHandshake ||
885 new_state == wpa_supplicant::kInterfaceStateGroupHandshake) {
886 // Ignore transitions into these states from Completed, to avoid
887 // bothering the user when roaming, or re-keying.
888 if (old_state != wpa_supplicant::kInterfaceStateCompleted)
889 affected_service->SetState(Service::kStateAssociating);
890 // TOOD(quiche): On backwards transitions, we should probably set
891 // a timeout for getting back into the completed state. At present,
892 // we depend on wpa_supplicant eventually reporting that CurrentBSS
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000893 // has changed. But there may be cases where that signal is not sent.
mukesh agrawal15908392011-11-16 18:29:25 +0000894 // (crosbug.com/23207)
895 } else {
896 // Other transitions do not affect Service state.
897 //
898 // Note in particular that we ignore a State change into
899 // kInterfaceStateDisconnected, in favor of observing the corresponding
900 // change in CurrentBSS.
901 }
mukesh agrawal7ec71312011-11-10 02:08:26 +0000902}
903
904// Used by Manager.
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700905WiFiServiceRefPtr WiFi::GetService(const KeyValueStore &args, Error *error) {
906 if (!args.ContainsString(flimflam::kTypeProperty)) {
907 error->Populate(Error::kInvalidArguments, kManagerErrorTypeRequired);
908 return NULL;
909 }
910
911 if (args.GetString(flimflam::kTypeProperty) != flimflam::kTypeWifi) {
912 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceType);
913 return NULL;
914 }
915
916 if (args.ContainsString(flimflam::kModeProperty) &&
917 args.GetString(flimflam::kModeProperty) !=
918 flimflam::kModeManaged) {
919 error->Populate(Error::kNotSupported, kManagerErrorUnsupportedServiceMode);
920 return NULL;
921 }
922
923 if (!args.ContainsString(flimflam::kSSIDProperty)) {
924 error->Populate(Error::kInvalidArguments, kManagerErrorSSIDRequired);
925 return NULL;
926 }
927
928 string ssid = args.GetString(flimflam::kSSIDProperty);
929 if (ssid.length() < 1) {
930 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooShort);
931 return NULL;
932 }
933
934 if (ssid.length() > IEEE_80211::kMaxSSIDLen) {
935 error->Populate(Error::kInvalidNetworkName, kManagerErrorSSIDTooLong);
936 return NULL;
937 }
938
939 string security_method;
940 if (args.ContainsString(flimflam::kSecurityProperty)) {
941 security_method = args.GetString(flimflam::kSecurityProperty);
942 } else {
943 security_method = flimflam::kSecurityNone;
944 }
945
946 if (security_method != flimflam::kSecurityNone &&
947 security_method != flimflam::kSecurityWep &&
948 security_method != flimflam::kSecurityPsk &&
949 security_method != flimflam::kSecurityWpa &&
950 security_method != flimflam::kSecurityRsn &&
951 security_method != flimflam::kSecurity8021x) {
952 error->Populate(Error::kNotSupported,
953 kManagerErrorUnsupportedSecurityMode);
954 return NULL;
955 }
956
957 if ((security_method == flimflam::kSecurityWep ||
958 security_method == flimflam::kSecurityPsk ||
959 security_method == flimflam::kSecurityWpa ||
960 security_method == flimflam::kSecurityRsn) &&
961 !args.ContainsString(flimflam::kPassphraseProperty)) {
962 error->Populate(Error::kInvalidArguments,
963 kManagerErrorPassphraseRequired);
964 return NULL;
965 }
966
Paul Stewart6ab23a92011-11-09 17:17:47 -0800967 bool hidden_ssid;
Paul Stewartced6a0b2011-11-08 15:32:04 -0800968 if (args.ContainsBool(flimflam::kWifiHiddenSsid)) {
969 hidden_ssid = args.GetBool(flimflam::kWifiHiddenSsid);
Paul Stewart6ab23a92011-11-09 17:17:47 -0800970 } else {
971 // If the service is not found, and the caller hasn't specified otherwise,
972 // we assume this is is a hidden network.
973 hidden_ssid = true;
Paul Stewartced6a0b2011-11-08 15:32:04 -0800974 }
975
Paul Stewart6ab23a92011-11-09 17:17:47 -0800976 vector<uint8_t> ssid_bytes(ssid.begin(), ssid.end());
977 WiFiServiceRefPtr service(FindService(ssid_bytes, flimflam::kModeManaged,
978 security_method));
979 if (!service.get()) {
mukesh agrawal1a056262011-10-05 14:36:54 -0700980 service = new WiFiService(control_interface(),
981 dispatcher(),
982 manager(),
983 this,
Paul Stewart6ab23a92011-11-09 17:17:47 -0800984 ssid_bytes,
mukesh agrawal1a056262011-10-05 14:36:54 -0700985 flimflam::kModeManaged,
Paul Stewartced6a0b2011-11-08 15:32:04 -0800986 security_method,
987 hidden_ssid);
988 services_.push_back(service);
mukesh agrawal261daca2011-12-02 18:56:56 +0000989 // NB: We do not register the newly created Service with the Manager.
990 // The Service will be registered if/when we find Endpoints for it.
mukesh agrawal7a4e4002011-09-06 11:26:05 -0700991 }
992
mukesh agrawal1a056262011-10-05 14:36:54 -0700993 if (security_method == flimflam::kSecurityWep ||
994 security_method == flimflam::kSecurityPsk ||
995 security_method == flimflam::kSecurityWpa ||
996 security_method == flimflam::kSecurityRsn) {
997 service->SetPassphrase(args.GetString(flimflam::kPassphraseProperty),
998 error);
999 if (error->IsFailure()) {
1000 return NULL;
1001 }
1002 }
1003
mukesh agrawal7ec71312011-11-10 02:08:26 +00001004 // TODO(quiche): Apply any other configuration parameters.
mukesh agrawal7a4e4002011-09-06 11:26:05 -07001005
1006 return service;
1007}
1008
Paul Stewartb50f0b92011-05-16 16:31:42 -07001009} // namespace shill