blob: 6c0177dbdaae213a7787d27ff016898360d2e3f1 [file] [log] [blame]
mukesh agrawal8a3188d2011-12-01 20:56:44 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
mukesh agrawalb54601c2011-06-07 17:39:22 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/wifi_service.h"
6
mukesh agrawal43970a22013-02-15 16:00:07 -08007#include <algorithm>
Wade Guthrie9ec08062013-09-25 15:22:24 -07008#include <limits>
mukesh agrawalb54601c2011-06-07 17:39:22 -07009#include <string>
Gaurav Shah10109f22011-11-11 20:16:22 -080010#include <utility>
mukesh agrawalb54601c2011-06-07 17:39:22 -070011
Ben Chana0ddf462014-02-06 11:32:42 -080012#include <base/strings/stringprintf.h>
13#include <base/strings/string_number_conversions.h>
14#include <base/strings/string_split.h>
15#include <base/strings/string_util.h>
Chris Masone3bd3c8c2011-06-13 08:20:26 -070016#include <chromeos/dbus/service_constants.h>
mukesh agrawal6e277772011-09-29 15:04:23 -070017#include <dbus/dbus.h>
mukesh agrawalb54601c2011-06-07 17:39:22 -070018
mukesh agrawale1d90e92012-02-15 17:36:08 -080019#include "shill/adaptor_interfaces.h"
Paul Stewart5baebb72013-03-14 11:43:29 -070020#include "shill/certificate_file.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070021#include "shill/control_interface.h"
mukesh agrawalcbfb34e2013-04-17 19:33:25 -070022#include "shill/dbus_adaptor.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070023#include "shill/device.h"
Paul Stewartc43cbbe2013-04-11 06:29:30 -070024#include "shill/eap_credentials.h"
mukesh agrawal1a056262011-10-05 14:36:54 -070025#include "shill/error.h"
Paul Stewart26b327e2011-10-19 11:38:09 -070026#include "shill/event_dispatcher.h"
mukesh agrawal1a056262011-10-05 14:36:54 -070027#include "shill/ieee80211.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070028#include "shill/logging.h"
Paul Stewart4357f4e2012-04-26 17:39:26 -070029#include "shill/manager.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000030#include "shill/metrics.h"
Thieu Lef7709452011-11-15 01:13:19 +000031#include "shill/property_accessor.h"
Paul Stewartd08f4432011-11-04 07:48:20 -070032#include "shill/store_interface.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070033#include "shill/wifi.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070034#include "shill/wifi_endpoint.h"
Paul Stewart3c504012013-01-17 17:49:58 -080035#include "shill/wifi_provider.h"
mukesh agrawal6e277772011-09-29 15:04:23 -070036#include "shill/wpa_supplicant.h"
mukesh agrawalb54601c2011-06-07 17:39:22 -070037
Paul Stewarta283e4e2013-10-22 20:50:14 -070038using std::map;
mukesh agrawal261daca2011-12-02 18:56:56 +000039using std::set;
mukesh agrawalb54601c2011-06-07 17:39:22 -070040using std::string;
mukesh agrawal1a056262011-10-05 14:36:54 -070041using std::vector;
mukesh agrawalb54601c2011-06-07 17:39:22 -070042
43namespace shill {
mukesh agrawalb54601c2011-06-07 17:39:22 -070044
mukesh agrawalbf14e942012-03-02 14:36:34 -080045const char WiFiService::kAutoConnNoEndpoint[] = "no endpoints";
Paul Stewart3c504012013-01-17 17:49:58 -080046const char WiFiService::kAnyDeviceAddress[] = "any";
Paul Stewartbca08f82013-07-09 16:32:37 -070047const int WiFiService::kSuspectedCredentialFailureThreshold = 3;
mukesh agrawalbf14e942012-03-02 14:36:34 -080048
Paul Stewartd08f4432011-11-04 07:48:20 -070049const char WiFiService::kStorageHiddenSSID[] = "WiFi.HiddenSSID";
Paul Stewart2706aaf2011-12-14 16:44:04 -080050const char WiFiService::kStorageMode[] = "WiFi.Mode";
51const char WiFiService::kStoragePassphrase[] = "Passphrase";
52const char WiFiService::kStorageSecurity[] = "WiFi.Security";
Paul Stewart71a4d3b2013-01-18 18:12:56 -080053const char WiFiService::kStorageSecurityClass[] = "WiFi.SecurityClass";
Paul Stewart2706aaf2011-12-14 16:44:04 -080054const char WiFiService::kStorageSSID[] = "SSID";
mukesh agrawale1d90e92012-02-15 17:36:08 -080055bool WiFiService::logged_signal_warning = false;
Paul Stewartd08f4432011-11-04 07:48:20 -070056
mukesh agrawalb54601c2011-06-07 17:39:22 -070057WiFiService::WiFiService(ControlInterface *control_interface,
58 EventDispatcher *dispatcher,
Thieu Le3426c8f2012-01-11 17:35:11 -080059 Metrics *metrics,
Chris Masone6791a432011-07-12 13:23:19 -070060 Manager *manager,
Paul Stewart3c504012013-01-17 17:49:58 -080061 WiFiProvider *provider,
Paul Stewarta41e38d2011-11-11 07:47:29 -080062 const vector<uint8_t> &ssid,
63 const string &mode,
64 const string &security,
Paul Stewartced6a0b2011-11-08 15:32:04 -080065 bool hidden_ssid)
Thieu Le3426c8f2012-01-11 17:35:11 -080066 : Service(control_interface, dispatcher, metrics, manager,
67 Technology::kWifi),
Chris Masone75612302011-10-12 16:31:21 -070068 need_passphrase_(false),
mukesh agrawal6e277772011-09-29 15:04:23 -070069 security_(security),
Chris Masone092df3e2011-08-22 09:41:39 -070070 mode_(mode),
Paul Stewartced6a0b2011-11-08 15:32:04 -080071 hidden_ssid_(hidden_ssid),
Thieu Lee41a72d2012-02-06 20:46:51 +000072 frequency_(0),
mukesh agrawalf6b32092013-04-10 15:49:55 -070073 physical_mode_(Metrics::kWiFiNetworkPhyModeUndef),
Paul Stewart23b393a2012-09-25 21:21:06 -070074 raw_signal_strength_(0),
mukesh agrawal43970a22013-02-15 16:00:07 -080075 cipher_8021x_(kCryptoNone),
Paul Stewartbca08f82013-07-09 16:32:37 -070076 suspected_credential_failures_(0),
Paul Stewartecf4cd12012-04-17 11:08:39 -070077 ssid_(ssid),
Paul Stewarta5e7d5f2013-01-09 18:06:15 -080078 ieee80211w_required_(false),
Peter Qiu49438222014-03-14 14:21:09 -070079 expecting_disconnect_(false),
Paul Stewarteb713e82013-06-28 14:51:54 -070080 certificate_file_(new CertificateFile()),
Paul Stewart3c504012013-01-17 17:49:58 -080081 provider_(provider) {
mukesh agrawalde29fa82011-09-16 16:16:36 -070082 PropertyStore *store = this->mutable_store();
Ben Chanf024ef42013-09-20 14:21:38 -070083 store->RegisterConstString(kModeProperty, &mode_);
84 HelpRegisterWriteOnlyDerivedString(kPassphraseProperty,
mukesh agrawal292dc0f2012-01-26 18:02:46 -080085 &WiFiService::SetPassphrase,
86 &WiFiService::ClearPassphrase,
87 NULL);
Ben Chanf024ef42013-09-20 14:21:38 -070088 store->RegisterBool(kPassphraseRequiredProperty, &need_passphrase_);
89 HelpRegisterDerivedString(kSecurityProperty,
Paul Stewart6df20bd2013-03-13 19:31:25 -070090 &WiFiService::GetSecurity,
91 NULL);
Chris Masone3bd3c8c2011-06-13 08:20:26 -070092
Ben Chanf024ef42013-09-20 14:21:38 -070093 store->RegisterConstString(kWifiAuthMode, &auth_mode_);
94 store->RegisterBool(kWifiHiddenSsid, &hidden_ssid_);
95 store->RegisterConstUint16(kWifiFrequency, &frequency_);
mukesh agrawale7c7e652013-06-18 17:19:39 -070096 store->RegisterConstUint16s(kWifiFrequencyListProperty, &frequency_list_);
Ben Chanf024ef42013-09-20 14:21:38 -070097 store->RegisterConstUint16(kWifiPhyMode, &physical_mode_);
98 store->RegisterConstString(kWifiBSsid, &bssid_);
99 store->RegisterConstString(kCountryProperty, &country_code_);
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700100 store->RegisterConstStringmap(kWifiVendorInformationProperty,
101 &vendor_information_);
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800102 store->RegisterConstBool(kWifiProtectedManagementFrameRequiredProperty,
103 &ieee80211w_required_);
mukesh agrawal32399322011-09-01 10:53:43 -0700104
mukesh agrawald835b202011-10-07 15:26:47 -0700105 hex_ssid_ = base::HexEncode(ssid_.data(), ssid_.size());
Paul Stewart9d95dad2013-10-10 12:08:14 -0700106 store->RegisterConstString(kWifiHexSsid, &hex_ssid_);
107
mukesh agrawald835b202011-10-07 15:26:47 -0700108 string ssid_string(
109 reinterpret_cast<const char *>(ssid_.data()), ssid_.size());
Paul Stewart9d95dad2013-10-10 12:08:14 -0700110 WiFi::SanitizeSSID(&ssid_string);
mukesh agrawald835b202011-10-07 15:26:47 -0700111 set_friendly_name(ssid_string);
Chris Masone9d779932011-08-25 16:33:41 -0700112
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700113 SetEapCredentials(new EapCredentials());
114
mukesh agrawal6e277772011-09-29 15:04:23 -0700115 // TODO(quiche): determine if it is okay to set EAP.KeyManagement for
116 // a service that is not 802.1x.
Gaurav Shah29d68882012-01-30 19:06:42 -0800117 if (Is8021x()) {
Gaurav Shah10109f22011-11-11 20:16:22 -0800118 // Passphrases are not mandatory for 802.1X.
119 need_passphrase_ = false;
Ben Chanf024ef42013-09-20 14:21:38 -0700120 } else if (security_ == kSecurityPsk) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700121 SetEAPKeyManagement("WPA-PSK");
Ben Chanf024ef42013-09-20 14:21:38 -0700122 } else if (security_ == kSecurityRsn) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700123 SetEAPKeyManagement("WPA-PSK");
Ben Chanf024ef42013-09-20 14:21:38 -0700124 } else if (security_ == kSecurityWpa) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700125 SetEAPKeyManagement("WPA-PSK");
Ben Chanf024ef42013-09-20 14:21:38 -0700126 } else if (security_ == kSecurityWep) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700127 SetEAPKeyManagement("NONE");
Ben Chanf024ef42013-09-20 14:21:38 -0700128 } else if (security_ == kSecurityNone) {
mukesh agrawal6e277772011-09-29 15:04:23 -0700129 SetEAPKeyManagement("NONE");
mukesh agrawal6e277772011-09-29 15:04:23 -0700130 } else {
Gaurav Shah10109f22011-11-11 20:16:22 -0800131 LOG(ERROR) << "Unsupported security method " << security_;
mukesh agrawal6e277772011-09-29 15:04:23 -0700132 }
133
Paul Stewartd08f4432011-11-04 07:48:20 -0700134 // Until we know better (at Profile load time), use the generic name.
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800135 storage_identifier_ = GetDefaultStorageIdentifier();
mukesh agrawal29c13a12011-11-24 00:09:19 +0000136 UpdateConnectable();
mukesh agrawal43970a22013-02-15 16:00:07 -0800137 UpdateSecurity();
Paul Stewartcb59fed2012-03-21 21:14:46 -0700138
Paul Stewart45170bc2014-06-02 15:49:34 -0700139 // Now that |this| is a fully constructed WiFiService, synchronize observers
140 // with our current state, and emit the appropriate change notifications.
141 // (Initial observer state may have been set in our base class.)
142 NotifyPropertyChanges();
143
Ben Chanf024ef42013-09-20 14:21:38 -0700144 IgnoreParameterForConfigure(kModeProperty);
145 IgnoreParameterForConfigure(kSSIDProperty);
146 IgnoreParameterForConfigure(kSecurityProperty);
Paul Stewart4539d262013-10-10 12:56:31 -0700147 IgnoreParameterForConfigure(kWifiHexSsid);
Darin Petkov457728b2013-01-09 09:49:08 +0100148
mukesh agrawal6cfe53f2013-08-13 13:39:01 -0700149 InitializeCustomMetrics();
150
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700151 // Log the |unique_name| to |friendly_name| mapping for debugging purposes.
152 // The latter will be tagged for scrubbing.
153 LOG(INFO) << "Constructed WiFi service " << unique_name()
154 << " name: " << WiFi::LogSSID(friendly_name());
mukesh agrawalb54601c2011-06-07 17:39:22 -0700155}
156
Darin Petkov9cd7ca12012-07-03 11:06:40 +0200157WiFiService::~WiFiService() {}
mukesh agrawalb54601c2011-06-07 17:39:22 -0700158
mukesh agrawalbf14e942012-03-02 14:36:34 -0800159bool WiFiService::IsAutoConnectable(const char **reason) const {
160 if (!Service::IsAutoConnectable(reason)) {
161 return false;
162 }
163
164 // Only auto-connect to Services which have visible Endpoints.
165 // (Needed because hidden Services may remain registered with
166 // Manager even without visible Endpoints.)
167 if (!HasEndpoints()) {
168 *reason = kAutoConnNoEndpoint;
169 return false;
170 }
171
Paul Stewart3c504012013-01-17 17:49:58 -0800172 CHECK(wifi_) << "We have endpoints but no WiFi device is selected?";
173
mukesh agrawalbf14e942012-03-02 14:36:34 -0800174 // Do not preempt an existing connection (whether pending, or
175 // connected, and whether to this service, or another).
176 if (!wifi_->IsIdle()) {
177 *reason = kAutoConnBusy;
178 return false;
179 }
180
181 return true;
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000182}
183
mukesh agrawal43970a22013-02-15 16:00:07 -0800184void WiFiService::SetEAPKeyManagement(const string &key_management) {
185 Service::SetEAPKeyManagement(key_management);
186 UpdateSecurity();
187}
188
Darin Petkov4a66cc52012-06-15 10:08:29 +0200189void WiFiService::AddEndpoint(const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawal261daca2011-12-02 18:56:56 +0000190 DCHECK(endpoint->ssid() == ssid());
191 endpoints_.insert(endpoint);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800192 UpdateFromEndpoints();
mukesh agrawal261daca2011-12-02 18:56:56 +0000193}
194
Darin Petkov4a66cc52012-06-15 10:08:29 +0200195void WiFiService::RemoveEndpoint(const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawal261daca2011-12-02 18:56:56 +0000196 set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.find(endpoint);
197 DCHECK(i != endpoints_.end());
198 if (i == endpoints_.end()) {
199 LOG(WARNING) << "In " << __func__ << "(): "
Darin Petkov457728b2013-01-09 09:49:08 +0100200 << "ignoring non-existent endpoint "
mukesh agrawal261daca2011-12-02 18:56:56 +0000201 << endpoint->bssid_string();
202 return;
203 }
204 endpoints_.erase(i);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800205 if (current_endpoint_ == endpoint) {
206 current_endpoint_ = NULL;
207 }
208 UpdateFromEndpoints();
mukesh agrawal261daca2011-12-02 18:56:56 +0000209}
210
Paul Stewart3c504012013-01-17 17:49:58 -0800211void WiFiService::NotifyCurrentEndpoint(
212 const WiFiEndpointConstRefPtr &endpoint) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800213 DCHECK(!endpoint || (endpoints_.find(endpoint) != endpoints_.end()));
214 current_endpoint_ = endpoint;
215 UpdateFromEndpoints();
Thieu Lee41a72d2012-02-06 20:46:51 +0000216}
217
Paul Stewart3c504012013-01-17 17:49:58 -0800218void WiFiService::NotifyEndpointUpdated(
219 const WiFiEndpointConstRefPtr &endpoint) {
220 DCHECK(endpoints_.find(endpoint) != endpoints_.end());
mukesh agrawale1d90e92012-02-15 17:36:08 -0800221 UpdateFromEndpoints();
mukesh agrawalb20776f2012-02-10 16:00:36 -0800222}
223
Chris Masone6515aab2011-10-12 16:19:09 -0700224string WiFiService::GetStorageIdentifier() const {
Paul Stewartd08f4432011-11-04 07:48:20 -0700225 return storage_identifier_;
Chris Masone34af2182011-08-22 11:59:36 -0700226}
mukesh agrawal445e72c2011-06-22 11:13:50 -0700227
Rebecca Silbersteinae11f592014-08-11 15:55:43 -0700228bool WiFiService::SetPassphraseInternal(
229 const string &passphrase, Error *error) {
Ben Chanf024ef42013-09-20 14:21:38 -0700230 if (security_ == kSecurityWep) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000231 ValidateWEPPassphrase(passphrase, error);
Ben Chanf024ef42013-09-20 14:21:38 -0700232 } else if (security_ == kSecurityPsk ||
233 security_ == kSecurityWpa ||
234 security_ == kSecurityRsn) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000235 ValidateWPAPassphrase(passphrase, error);
236 } else {
237 error->Populate(Error::kNotSupported);
mukesh agrawal1a056262011-10-05 14:36:54 -0700238 }
Thieu Lef4cbda92011-11-10 23:41:24 +0000239
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700240 if (!error->IsSuccess()) {
241 return false;
242 }
243 if (passphrase_ == passphrase) {
244 // After a user logs in, Chrome may reconfigure a Service with the
245 // same credentials as before login. When that occurs, we don't
246 // want to bump the user off the network. Hence, we MUST return
247 // early. (See crbug.com/231456#c17)
248 return false;
Paul Stewart2706aaf2011-12-14 16:44:04 -0800249 }
mukesh agrawal29c13a12011-11-24 00:09:19 +0000250
Paul Stewart835934a2012-12-06 19:27:09 -0800251 passphrase_ = passphrase;
Rebecca Silbersteinae11f592014-08-11 15:55:43 -0700252 UpdateConnectable();
253 return true;
254}
255
256bool WiFiService::SetPassphrase(const string &passphrase, Error *error) {
257 if (!SetPassphraseInternal(passphrase, error))
258 return false;
Rebecca Silbersteinb6df8b12014-07-10 16:07:08 -0700259 OnCredentialChange();
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700260 return true;
mukesh agrawal1a056262011-10-05 14:36:54 -0700261}
262
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800263// ClearPassphrase is separate from SetPassphrase, because the default
264// value for |passphrase_| would not pass validation.
265void WiFiService::ClearPassphrase(Error */*error*/) {
266 passphrase_.clear();
Paul Stewart835934a2012-12-06 19:27:09 -0800267 ClearCachedCredentials();
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800268 UpdateConnectable();
269}
270
Paul Stewartfa11e282013-12-02 22:04:25 -0800271string WiFiService::GetTethering(Error */*error*/) const {
272 if (IsConnected() && wifi_ && wifi_->IsConnectedViaTether()) {
273 return kTetheringConfirmedState;
274 }
275
276 // Only perform BSSID tests if there is exactly one matching endpoint,
277 // so we ignore campuses that may use locally administered BSSIDs.
Paul Stewart7f5d9c02013-12-03 18:26:00 -0800278 if (endpoints_.size() == 1 &&
Paul Stewartfa11e282013-12-02 22:04:25 -0800279 (*endpoints_.begin())->has_tethering_signature()) {
280 return kTetheringSuspectedState;
281 }
282
283 return kTetheringNotDetectedState;
284}
285
Paul Stewarte7de2942013-04-25 17:07:31 -0700286string WiFiService::GetLoadableStorageIdentifier(
287 const StoreInterface &storage) const {
288 set<string> groups = storage.GetGroupsWithProperties(GetStorageProperties());
289 if (groups.empty()) {
290 LOG(WARNING) << "Configuration for service "
291 << unique_name()
292 << " is not available in the persistent store";
293 return "";
294 }
295 if (groups.size() > 1) {
296 LOG(WARNING) << "More than one configuration for service "
297 << unique_name()
298 << " is available; choosing the first.";
299 }
300 return *groups.begin();
301}
302
303bool WiFiService::IsLoadableFrom(const StoreInterface &storage) const {
304 return !storage.GetGroupsWithProperties(GetStorageProperties()).empty();
Paul Stewartd08f4432011-11-04 07:48:20 -0700305}
306
Paul Stewarta41e38d2011-11-11 07:47:29 -0800307bool WiFiService::IsVisible() const {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800308 // WiFi Services should be displayed only if they are in range (have
309 // endpoints that have shown up in a scan) or if the service is actively
310 // being connected.
mukesh agrawal8a3188d2011-12-01 20:56:44 +0000311 return HasEndpoints() || IsConnected() || IsConnecting();
Paul Stewarta41e38d2011-11-11 07:47:29 -0800312}
313
Paul Stewartd08f4432011-11-04 07:48:20 -0700314bool WiFiService::Load(StoreInterface *storage) {
Paul Stewarte7de2942013-04-25 17:07:31 -0700315 string id = GetLoadableStorageIdentifier(*storage);
316 if (id.empty()) {
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800317 return false;
Paul Stewartd08f4432011-11-04 07:48:20 -0700318 }
319
320 // Set our storage identifier to match the storage name in the Profile.
321 storage_identifier_ = id;
322
323 // Load properties common to all Services.
324 if (!Service::Load(storage)) {
325 return false;
326 }
327
328 // Load properties specific to WiFi services.
329 storage->GetBool(id, kStorageHiddenSSID, &hidden_ssid_);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000330
Paul Stewart2706aaf2011-12-14 16:44:04 -0800331 // NB: mode, security and ssid parameters are never read in from
332 // Load() as they are provided from the scan.
333
334 string passphrase;
335 if (storage->GetCryptedString(id, kStoragePassphrase, &passphrase)) {
336 Error error;
Rebecca Silbersteinae11f592014-08-11 15:55:43 -0700337 SetPassphraseInternal(passphrase, &error);
Paul Stewartfa013ab2013-04-11 07:12:03 -0700338 if (!error.IsSuccess() &&
339 !(passphrase.empty() && error.type() == Error::kNotSupported)) {
Paul Stewart71b9ed52014-01-29 08:53:06 -0800340 LOG(ERROR) << "Passphrase could not be set: " << error;
Paul Stewart2706aaf2011-12-14 16:44:04 -0800341 }
342 }
343
Peter Qiu49438222014-03-14 14:21:09 -0700344 expecting_disconnect_ = false;
Paul Stewartd08f4432011-11-04 07:48:20 -0700345 return true;
346}
347
348bool WiFiService::Save(StoreInterface *storage) {
349 // Save properties common to all Services.
350 if (!Service::Save(storage)) {
351 return false;
352 }
353
354 // Save properties specific to WiFi services.
355 const string id = GetStorageIdentifier();
Paul Stewart2706aaf2011-12-14 16:44:04 -0800356 storage->SetBool(id, kStorageHiddenSSID, hidden_ssid_);
357 storage->SetString(id, kStorageMode, mode_);
358 storage->SetCryptedString(id, kStoragePassphrase, passphrase_);
359 storage->SetString(id, kStorageSecurity, security_);
Paul Stewart71a4d3b2013-01-18 18:12:56 -0800360 storage->SetString(id, kStorageSecurityClass, GetSecurityClass(security_));
Paul Stewart2706aaf2011-12-14 16:44:04 -0800361 storage->SetString(id, kStorageSSID, hex_ssid_);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000362
Paul Stewartd08f4432011-11-04 07:48:20 -0700363 return true;
364}
365
Paul Stewart65512e12012-03-26 18:01:08 -0700366bool WiFiService::Unload() {
Peter Qiu91b461f2014-03-26 10:19:45 -0700367 // Expect the service to be disconnected if is currently connected or
368 // in the process of connecting.
369 if (IsConnected() || IsConnecting()) {
370 expecting_disconnect_ = true;
371 } else {
372 expecting_disconnect_ = false;
373 }
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800374 Service::Unload();
Albert Chaulk0e1cdea2013-02-27 15:32:55 -0800375 if (wifi_) {
376 wifi_->DestroyServiceLease(*this);
377 }
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800378 hidden_ssid_ = false;
Paul Stewartbca08f82013-07-09 16:32:37 -0700379 ResetSuspectedCredentialFailures();
Wade Guthrie005bd342012-05-02 09:37:07 -0700380 Error unused_error;
381 ClearPassphrase(&unused_error);
Paul Stewart3c504012013-01-17 17:49:58 -0800382 return provider_->OnServiceUnloaded(this);
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800383}
384
Paul Stewart7cc90682014-05-30 17:01:56 -0700385void WiFiService::SetState(ConnectState state) {
Paul Stewart7cc90682014-05-30 17:01:56 -0700386 Service::SetState(state);
Paul Stewart45170bc2014-06-02 15:49:34 -0700387 NotifyPropertyChanges();
Paul Stewart7cc90682014-05-30 17:01:56 -0700388}
389
Paul Stewart6ab23a92011-11-09 17:17:47 -0800390bool WiFiService::IsSecurityMatch(const string &security) const {
391 return GetSecurityClass(security) == GetSecurityClass(security_);
392}
393
Paul Stewartbca08f82013-07-09 16:32:37 -0700394bool WiFiService::AddSuspectedCredentialFailure() {
395 if (!has_ever_connected()) {
396 return true;
397 }
398 ++suspected_credential_failures_;
399 return suspected_credential_failures_ >= kSuspectedCredentialFailureThreshold;
400}
401
402void WiFiService::ResetSuspectedCredentialFailures() {
403 suspected_credential_failures_ = 0;
404}
405
Thieu Le48e6d6d2011-12-06 00:40:27 +0000406void WiFiService::InitializeCustomMetrics() const {
mukesh agrawal6cfe53f2013-08-13 13:39:01 -0700407 SLOG(Metrics, 2) << __func__ << " for " << unique_name();
Thieu Le48e6d6d2011-12-06 00:40:27 +0000408 string histogram = metrics()->GetFullMetricName(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700409 Metrics::kMetricTimeToJoinMillisecondsSuffix,
mukesh agrawal6cfe53f2013-08-13 13:39:01 -0700410 technology());
Wade Guthrie7ac610b2013-10-01 17:48:14 -0700411 metrics()->AddServiceStateTransitionTimer(*this,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000412 histogram,
413 Service::kStateAssociating,
414 Service::kStateConfiguring);
415}
416
Thieu Leb84ba342012-03-02 15:15:19 -0800417void WiFiService::SendPostReadyStateMetrics(
Ben Chan7fab8972014-08-10 17:14:46 -0700418 int64_t time_resume_to_ready_milliseconds) const {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000419 metrics()->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700420 metrics()->GetFullMetricName(Metrics::kMetricNetworkChannelSuffix,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000421 technology()),
422 Metrics::WiFiFrequencyToChannel(frequency_),
423 Metrics::kMetricNetworkChannelMax);
Thieu Lead1ec2c2012-01-05 23:39:48 +0000424
425 DCHECK(physical_mode_ < Metrics::kWiFiNetworkPhyModeMax);
426 metrics()->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700427 metrics()->GetFullMetricName(Metrics::kMetricNetworkPhyModeSuffix,
Thieu Lead1ec2c2012-01-05 23:39:48 +0000428 technology()),
429 static_cast<Metrics::WiFiNetworkPhyMode>(physical_mode_),
430 Metrics::kWiFiNetworkPhyModeMax);
431
Paul Stewart4108db92013-03-11 12:13:24 -0700432 string security_mode = security_;
433 if (current_endpoint_) {
434 security_mode = current_endpoint_->security_mode();
435 }
Thieu Lead1ec2c2012-01-05 23:39:48 +0000436 Metrics::WiFiSecurity security_uma =
Paul Stewart4108db92013-03-11 12:13:24 -0700437 Metrics::WiFiSecurityStringToEnum(security_mode);
Thieu Lead1ec2c2012-01-05 23:39:48 +0000438 DCHECK(security_uma != Metrics::kWiFiSecurityUnknown);
439 metrics()->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700440 metrics()->GetFullMetricName(Metrics::kMetricNetworkSecuritySuffix,
Thieu Lead1ec2c2012-01-05 23:39:48 +0000441 technology()),
442 security_uma,
443 Metrics::kMetricNetworkSecurityMax);
Thieu Leb84ba342012-03-02 15:15:19 -0800444
Paul Stewart21f40962013-03-01 14:27:28 -0800445 if (Is8021x()) {
Paul Stewartc43cbbe2013-04-11 06:29:30 -0700446 eap()->OutputConnectionMetrics(metrics(), technology());
Paul Stewart21f40962013-03-01 14:27:28 -0800447 }
448
Paul Stewart23b393a2012-09-25 21:21:06 -0700449 // We invert the sign of the signal strength value, since UMA histograms
450 // cannot represent negative numbers (it stores them but cannot display
451 // them), and dBm values of interest start at 0 and go negative from there.
452 metrics()->SendToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700453 metrics()->GetFullMetricName(Metrics::kMetricNetworkSignalStrengthSuffix,
Paul Stewart23b393a2012-09-25 21:21:06 -0700454 technology()),
455 -raw_signal_strength_,
456 Metrics::kMetricNetworkSignalStrengthMin,
457 Metrics::kMetricNetworkSignalStrengthMax,
458 Metrics::kMetricNetworkSignalStrengthNumBuckets);
459
Thieu Leb84ba342012-03-02 15:15:19 -0800460 if (time_resume_to_ready_milliseconds > 0) {
461 metrics()->SendToUMA(
462 metrics()->GetFullMetricName(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700463 Metrics::kMetricTimeResumeToReadyMillisecondsSuffix, technology()),
Thieu Leb84ba342012-03-02 15:15:19 -0800464 time_resume_to_ready_milliseconds,
465 Metrics::kTimerHistogramMillisecondsMin,
466 Metrics::kTimerHistogramMillisecondsMax,
467 Metrics::kTimerHistogramNumBuckets);
468 }
Paul Stewarte4cedde2013-07-17 08:56:44 -0700469
470 Metrics::WiFiApMode ap_mode_uma = Metrics::WiFiApModeStringToEnum(mode_);
471 metrics()->SendEnumToUMA(
mukesh agrawal132e96f2014-04-24 11:49:42 -0700472 metrics()->GetFullMetricName(Metrics::kMetricNetworkApModeSuffix,
473 technology()),
Paul Stewarte4cedde2013-07-17 08:56:44 -0700474 ap_mode_uma,
475 Metrics::kWiFiApModeMax);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000476}
477
mukesh agrawal32399322011-09-01 10:53:43 -0700478// private methods
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700479void WiFiService::HelpRegisterConstDerivedString(
480 const string &name,
481 string(WiFiService::*get)(Error *)) {
482 mutable_store()->RegisterDerivedString(
483 name,
484 StringAccessor(
485 new CustomAccessor<WiFiService, string>(this, get, NULL)));
486}
487
Paul Stewart6df20bd2013-03-13 19:31:25 -0700488void WiFiService::HelpRegisterDerivedString(
489 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700490 string(WiFiService::*get)(Error *error),
491 bool(WiFiService::*set)(const string &, Error *)) {
Paul Stewart6df20bd2013-03-13 19:31:25 -0700492 mutable_store()->RegisterDerivedString(
493 name,
494 StringAccessor(new CustomAccessor<WiFiService, string>(this, get, set)));
495}
496
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800497void WiFiService::HelpRegisterWriteOnlyDerivedString(
498 const string &name,
mukesh agrawalbebf1b82013-04-23 15:06:33 -0700499 bool(WiFiService::*set)(const string &, Error *),
Alex Vakulenko8a532292014-06-16 17:18:44 -0700500 void(WiFiService::*clear)(Error *error),
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800501 const string *default_value) {
502 mutable_store()->RegisterDerivedString(
Thieu Lef7709452011-11-15 01:13:19 +0000503 name,
mukesh agrawal292dc0f2012-01-26 18:02:46 -0800504 StringAccessor(
505 new CustomWriteOnlyAccessor<WiFiService, string>(
506 this, set, clear, default_value)));
Thieu Lef7709452011-11-15 01:13:19 +0000507}
508
mukesh agrawaldc7b8442012-09-27 13:48:14 -0700509void WiFiService::Connect(Error *error, const char *reason) {
Wade Guthrie005bd342012-05-02 09:37:07 -0700510 if (!connectable()) {
Darin Petkov457728b2013-01-09 09:49:08 +0100511 LOG(ERROR) << "Can't connect. Service " << unique_name()
512 << " is not connectable.";
Christopher Wiley1ce658d2012-10-10 10:02:03 -0700513 Error::PopulateAndLog(error,
514 Error::kOperationFailed,
515 Error::GetDefaultMessage(Error::kOperationFailed));
516 return;
517 }
518 if (IsConnecting() || IsConnected()) {
Darin Petkov457728b2013-01-09 09:49:08 +0100519 LOG(WARNING) << "Can't connect. Service " << unique_name()
Christopher Wiley1ce658d2012-10-10 10:02:03 -0700520 << " is already connecting or connected.";
521 Error::PopulateAndLog(error,
522 Error::kAlreadyConnected,
523 Error::GetDefaultMessage(Error::kAlreadyConnected));
Wade Guthrie005bd342012-05-02 09:37:07 -0700524 return;
525 }
Paul Stewart3c504012013-01-17 17:49:58 -0800526
527 WiFiRefPtr wifi = wifi_;
528 if (!wifi) {
529 // If this is a hidden service before it has been found in a scan, we
530 // may need to late-bind to any available WiFi Device. We don't actually
Wade Guthrie9ec08062013-09-25 15:22:24 -0700531 // set |wifi_| in this case since we do not yet see any endpoints. This
Paul Stewart3c504012013-01-17 17:49:58 -0800532 // will mean this service is not disconnectable until an endpoint is
533 // found.
534 wifi = ChooseDevice();
535 if (!wifi) {
536 LOG(ERROR) << "Can't connect. Service " << unique_name()
537 << " cannot find a WiFi device.";
538 Error::PopulateAndLog(error,
539 Error::kOperationFailed,
540 Error::GetDefaultMessage(Error::kOperationFailed));
541 return;
542 }
543 }
544
545 if (wifi->IsCurrentService(this)) {
Darin Petkov457728b2013-01-09 09:49:08 +0100546 LOG(WARNING) << "Can't connect. Service " << unique_name()
Wade Guthrie8bc50882012-10-31 16:23:20 -0700547 << " is the current service (but, in " << GetStateString()
Paul Stewart3c504012013-01-17 17:49:58 -0800548 << " state, not connected).";
Wade Guthrie8bc50882012-10-31 16:23:20 -0700549 Error::PopulateAndLog(error,
550 Error::kInProgress,
551 Error::GetDefaultMessage(Error::kInProgress));
552 return;
553 }
Wade Guthrie005bd342012-05-02 09:37:07 -0700554
Peter Qiu574996a2014-04-04 10:55:47 -0700555 // Report number of BSSes available for this service.
556 metrics()->NotifyWifiAvailableBSSes(endpoints_.size());
557
Paul Stewarta283e4e2013-10-22 20:50:14 -0700558 if (Is8021x()) {
559 // If EAP key management is not set, set to a default.
560 if (GetEAPKeyManagement().empty())
561 SetEAPKeyManagement("WPA-EAP");
562 ClearEAPCertification();
563 }
564
Peter Qiu49438222014-03-14 14:21:09 -0700565 expecting_disconnect_ = false;
Paul Stewarta283e4e2013-10-22 20:50:14 -0700566 Service::Connect(error, reason);
567 wifi->ConnectTo(this);
568}
569
570DBusPropertiesMap WiFiService::GetSupplicantConfigurationParameters() const {
571 DBusPropertiesMap params;
572 DBus::MessageIter writer;
573
Paul Stewart0654ece2013-03-26 15:21:26 -0700574 params[WPASupplicant::kNetworkPropertyMode].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700575 append_uint32(WiFiEndpoint::ModeStringToUint(mode_));
576
Ben Chanf024ef42013-09-20 14:21:38 -0700577 if (mode_ == kModeAdhoc && frequency_ != 0) {
Wade Guthrie9ec08062013-09-25 15:22:24 -0700578 // Frequency is required in order to successfully connect to an IBSS
Paul Stewarte2d7c502012-07-16 16:35:10 -0700579 // with wpa_supplicant. If we have one from our endpoint, insert it
580 // here.
Paul Stewart0654ece2013-03-26 15:21:26 -0700581 params[WPASupplicant::kNetworkPropertyFrequency].writer().
Paul Stewarte2d7c502012-07-16 16:35:10 -0700582 append_int32(frequency_);
583 }
584
Gaurav Shah29d68882012-01-30 19:06:42 -0800585 if (Is8021x()) {
Paul Stewartc350e682014-06-19 15:44:30 -0700586 eap()->PopulateSupplicantProperties(certificate_file_.get(), &params);
Ben Chanf024ef42013-09-20 14:21:38 -0700587 } else if (security_ == kSecurityPsk ||
588 security_ == kSecurityRsn ||
589 security_ == kSecurityWpa) {
Ben Chana0ddf462014-02-06 11:32:42 -0800590 const string psk_proto =
591 base::StringPrintf("%s %s",
592 WPASupplicant::kSecurityModeWPA,
593 WPASupplicant::kSecurityModeRSN);
Paul Stewart0654ece2013-03-26 15:21:26 -0700594 params[WPASupplicant::kPropertySecurityProtocol].writer().
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800595 append_string(psk_proto.c_str());
Paul Stewart0654ece2013-03-26 15:21:26 -0700596 params[WPASupplicant::kPropertyPreSharedKey].writer().
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800597 append_string(passphrase_.c_str());
Ben Chanf024ef42013-09-20 14:21:38 -0700598 } else if (security_ == kSecurityWep) {
Paul Stewart0654ece2013-03-26 15:21:26 -0700599 params[WPASupplicant::kPropertyAuthAlg].writer().
600 append_string(WPASupplicant::kSecurityAuthAlg);
Paul Stewarta283e4e2013-10-22 20:50:14 -0700601 Error unused_error;
Thieu Lef4cbda92011-11-10 23:41:24 +0000602 int key_index;
Ben Chan7fab8972014-08-10 17:14:46 -0700603 std::vector<uint8_t> password_bytes;
Paul Stewarta283e4e2013-10-22 20:50:14 -0700604 ParseWEPPassphrase(passphrase_, &key_index, &password_bytes, &unused_error);
Paul Stewart0654ece2013-03-26 15:21:26 -0700605 writer = params[WPASupplicant::kPropertyWEPKey +
Thieu Lef4cbda92011-11-10 23:41:24 +0000606 base::IntToString(key_index)].writer();
607 writer << password_bytes;
Paul Stewart0654ece2013-03-26 15:21:26 -0700608 params[WPASupplicant::kPropertyWEPTxKeyIndex].writer().
Thieu Lef4cbda92011-11-10 23:41:24 +0000609 append_uint32(key_index);
Ben Chanf024ef42013-09-20 14:21:38 -0700610 } else if (security_ == kSecurityNone) {
Gaurav Shahf8721ee2011-11-07 09:12:46 -0800611 // Nothing special to do here.
mukesh agrawal6e277772011-09-29 15:04:23 -0700612 } else {
Paul Stewarta283e4e2013-10-22 20:50:14 -0700613 NOTIMPLEMENTED() << "Unsupported security method " << security_;
mukesh agrawal6e277772011-09-29 15:04:23 -0700614 }
615
Paul Stewart0654ece2013-03-26 15:21:26 -0700616 params[WPASupplicant::kNetworkPropertyEapKeyManagement].writer().
mukesh agrawal6e277772011-09-29 15:04:23 -0700617 append_string(key_management().c_str());
Gaurav Shah7ad8e532011-11-11 17:14:49 -0800618
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800619 if (ieee80211w_required_) {
620 // TODO(pstew): We should also enable IEEE 802.11w if the user
621 // explicitly enables support for this through a service / device
Paul Stewartee6b3d72013-07-12 16:07:51 -0700622 // property. crbug.com/219950
Paul Stewart0654ece2013-03-26 15:21:26 -0700623 params[WPASupplicant::kNetworkPropertyIeee80211w].writer().
624 append_uint32(WPASupplicant::kNetworkIeee80211wEnabled);
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800625 }
626
Gaurav Shah7ad8e532011-11-11 17:14:49 -0800627 // See note in dbus_adaptor.cc on why we need to use a local.
Paul Stewart0654ece2013-03-26 15:21:26 -0700628 writer = params[WPASupplicant::kNetworkPropertySSID].writer();
mukesh agrawal6e277772011-09-29 15:04:23 -0700629 writer << ssid_;
630
Paul Stewarta283e4e2013-10-22 20:50:14 -0700631 return params;
mukesh agrawalb54601c2011-06-07 17:39:22 -0700632}
633
Paul Stewarta283e4e2013-10-22 20:50:14 -0700634
Samuel Tan0d061192014-07-07 15:45:15 -0700635void WiFiService::Disconnect(Error *error, const char *reason) {
636 Service::Disconnect(error, reason);
Paul Stewart3c504012013-01-17 17:49:58 -0800637 if (!wifi_) {
638 // If we are connecting to a hidden service, but have not yet found
639 // any endpoints, we could end up with a disconnect request without
640 // a wifi_ reference. This is not a fatal error.
641 LOG_IF(ERROR, IsConnecting())
642 << "WiFi endpoints do not (yet) exist. Cannot disconnect service "
643 << unique_name();
644 LOG_IF(FATAL, IsConnected())
645 << "WiFi device does not exist. Cannot disconnect service "
646 << unique_name();
647 error->Populate(Error::kOperationFailed);
648 return;
649 }
Rebecca Silbersteind7cdc5e2014-08-12 16:01:01 -0700650 wifi_->DisconnectFromIfActive(this);
mukesh agrawal0ed0f2e2011-12-05 20:36:17 +0000651}
652
Paul Stewart1cf7eb82013-12-03 19:34:36 -0800653string WiFiService::GetDeviceRpcId(Error *error) const {
Paul Stewart3c504012013-01-17 17:49:58 -0800654 if (!wifi_) {
655 error->Populate(Error::kNotFound, "Not associated with a device");
mukesh agrawalcbfb34e2013-04-17 19:33:25 -0700656 return DBusAdaptor::kNullPath;
Paul Stewart3c504012013-01-17 17:49:58 -0800657 }
Chris Masone95207da2011-06-29 16:50:49 -0700658 return wifi_->GetRpcIdentifier();
659}
660
mukesh agrawal29c13a12011-11-24 00:09:19 +0000661void WiFiService::UpdateConnectable() {
Gaurav Shah10109f22011-11-11 20:16:22 -0800662 bool is_connectable = false;
Ben Chanf024ef42013-09-20 14:21:38 -0700663 if (security_ == kSecurityNone) {
mukesh agrawal29c13a12011-11-24 00:09:19 +0000664 DCHECK(passphrase_.empty());
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800665 need_passphrase_ = false;
Gaurav Shah10109f22011-11-11 20:16:22 -0800666 is_connectable = true;
Gaurav Shah29d68882012-01-30 19:06:42 -0800667 } else if (Is8021x()) {
668 is_connectable = Is8021xConnectable();
Ben Chanf024ef42013-09-20 14:21:38 -0700669 } else if (security_ == kSecurityWep ||
670 security_ == kSecurityWpa ||
671 security_ == kSecurityPsk ||
672 security_ == kSecurityRsn) {
Paul Stewartd8ad3c42012-01-09 12:39:38 -0800673 need_passphrase_ = passphrase_.empty();
Gaurav Shah10109f22011-11-11 20:16:22 -0800674 is_connectable = !need_passphrase_;
mukesh agrawal29c13a12011-11-24 00:09:19 +0000675 }
mukesh agrawalcbfb34e2013-04-17 19:33:25 -0700676 SetConnectable(is_connectable);
mukesh agrawal29c13a12011-11-24 00:09:19 +0000677}
678
mukesh agrawale1d90e92012-02-15 17:36:08 -0800679void WiFiService::UpdateFromEndpoints() {
680 const WiFiEndpoint *representative_endpoint = NULL;
681
682 if (current_endpoint_) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800683 representative_endpoint = current_endpoint_;
684 } else {
Ben Chan7fab8972014-08-10 17:14:46 -0700685 int16_t best_signal = std::numeric_limits<int16_t>::min();
Paul Stewart6db7b242014-05-02 15:34:21 -0700686 for (const auto &endpoint : endpoints_) {
687 if (endpoint->signal_strength() >= best_signal) {
688 best_signal = endpoint->signal_strength();
689 representative_endpoint = endpoint;
mukesh agrawale1d90e92012-02-15 17:36:08 -0800690 }
691 }
692 }
693
Paul Stewart3c504012013-01-17 17:49:58 -0800694 WiFiRefPtr wifi;
695 if (representative_endpoint) {
696 wifi = representative_endpoint->device();
Paul Stewart8653f462013-02-06 12:21:05 -0800697 } else if (IsConnected() || IsConnecting()) {
698 LOG(WARNING) << "Service " << unique_name()
699 << " will disconnect due to no remaining endpoints.";
Paul Stewart3c504012013-01-17 17:49:58 -0800700 }
701
702 SetWiFi(wifi);
703
Paul Stewart6db7b242014-05-02 15:34:21 -0700704 for (const auto &endpoint : endpoints_) {
705 if (endpoint->ieee80211w_required()) {
Paul Stewarta5e7d5f2013-01-09 18:06:15 -0800706 // Never reset ieee80211w_required_ to false, so we track whether we have
707 // ever seen an AP that requires 802.11w.
708 ieee80211w_required_ = true;
709 }
710 }
711
Ben Chan7fab8972014-08-10 17:14:46 -0700712 set<uint16_t> frequency_set;
mukesh agrawale7c7e652013-06-18 17:19:39 -0700713 for (const auto &endpoint : endpoints_) {
714 frequency_set.insert(endpoint->frequency());
715 }
716 frequency_list_.assign(frequency_set.begin(), frequency_set.end());
717
mukesh agrawal43970a22013-02-15 16:00:07 -0800718 if (Is8021x())
719 cipher_8021x_ = ComputeCipher8021x(endpoints_);
720
Ben Chan7fab8972014-08-10 17:14:46 -0700721 uint16_t frequency = 0;
722 int16_t signal = std::numeric_limits<int16_t>::min();
mukesh agrawal923f14f2012-06-04 16:46:08 -0700723 string bssid;
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700724 string country_code;
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700725 Stringmap vendor_information;
Ben Chan7fab8972014-08-10 17:14:46 -0700726 uint16_t physical_mode = Metrics::kWiFiNetworkPhyModeUndef;
Paul Stewart23b393a2012-09-25 21:21:06 -0700727 // Represent "unknown raw signal strength" as 0.
728 raw_signal_strength_ = 0;
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700729 if (representative_endpoint) {
mukesh agrawale1d90e92012-02-15 17:36:08 -0800730 frequency = representative_endpoint->frequency();
731 signal = representative_endpoint->signal_strength();
Paul Stewart23b393a2012-09-25 21:21:06 -0700732 raw_signal_strength_ = signal;
mukesh agrawal923f14f2012-06-04 16:46:08 -0700733 bssid = representative_endpoint->bssid_string();
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700734 country_code = representative_endpoint->country_code();
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700735 vendor_information = representative_endpoint->GetVendorInformation();
mukesh agrawalf6b32092013-04-10 15:49:55 -0700736 physical_mode = representative_endpoint->physical_mode();
mukesh agrawale1d90e92012-02-15 17:36:08 -0800737 }
738
739 if (frequency_ != frequency) {
740 frequency_ = frequency;
Ben Chanf024ef42013-09-20 14:21:38 -0700741 adaptor()->EmitUint16Changed(kWifiFrequency, frequency_);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800742 }
mukesh agrawal923f14f2012-06-04 16:46:08 -0700743 if (bssid_ != bssid) {
744 bssid_ = bssid;
Ben Chanf024ef42013-09-20 14:21:38 -0700745 adaptor()->EmitStringChanged(kWifiBSsid, bssid_);
mukesh agrawal923f14f2012-06-04 16:46:08 -0700746 }
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700747 if (country_code_ != country_code) {
748 country_code_ = country_code;
Ben Chanf024ef42013-09-20 14:21:38 -0700749 adaptor()->EmitStringChanged(kCountryProperty, country_code_);
Paul Stewartbdbd3c32013-04-17 09:47:21 -0700750 }
Paul Stewart72b2fdc2012-06-02 08:58:51 -0700751 if (vendor_information_ != vendor_information) {
752 vendor_information_ = vendor_information;
753 adaptor()->EmitStringmapChanged(kWifiVendorInformationProperty,
754 vendor_information_);
755 }
mukesh agrawalf6b32092013-04-10 15:49:55 -0700756 if (physical_mode_ != physical_mode) {
757 physical_mode_ = physical_mode;
Ben Chanf024ef42013-09-20 14:21:38 -0700758 adaptor()->EmitUint16Changed(kWifiPhyMode, physical_mode_);
mukesh agrawalf6b32092013-04-10 15:49:55 -0700759 }
mukesh agrawale7c7e652013-06-18 17:19:39 -0700760 adaptor()->EmitUint16sChanged(kWifiFrequencyListProperty, frequency_list_);
mukesh agrawale1d90e92012-02-15 17:36:08 -0800761 SetStrength(SignalToStrength(signal));
mukesh agrawal43970a22013-02-15 16:00:07 -0800762 UpdateSecurity();
Paul Stewart45170bc2014-06-02 15:49:34 -0700763 NotifyPropertyChanges();
mukesh agrawal43970a22013-02-15 16:00:07 -0800764}
765
766void WiFiService::UpdateSecurity() {
767 CryptoAlgorithm algorithm = kCryptoNone;
768 bool key_rotation = false;
769 bool endpoint_auth = false;
770
Ben Chanf024ef42013-09-20 14:21:38 -0700771 if (security_ == kSecurityNone) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800772 // initial values apply
Ben Chanf024ef42013-09-20 14:21:38 -0700773 } else if (security_ == kSecurityWep) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800774 algorithm = kCryptoRc4;
775 key_rotation = Is8021x();
776 endpoint_auth = Is8021x();
Ben Chanf024ef42013-09-20 14:21:38 -0700777 } else if (security_ == kSecurityPsk ||
778 security_ == kSecurityWpa) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800779 algorithm = kCryptoRc4;
780 key_rotation = true;
781 endpoint_auth = false;
Ben Chanf024ef42013-09-20 14:21:38 -0700782 } else if (security_ == kSecurityRsn) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800783 algorithm = kCryptoAes;
784 key_rotation = true;
785 endpoint_auth = false;
Ben Chanf024ef42013-09-20 14:21:38 -0700786 } else if (security_ == kSecurity8021x) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800787 algorithm = cipher_8021x_;
788 key_rotation = true;
789 endpoint_auth = true;
790 }
791 SetSecurity(algorithm, key_rotation, endpoint_auth);
792}
793
794// static
795Service::CryptoAlgorithm WiFiService::ComputeCipher8021x(
796 const set<WiFiEndpointConstRefPtr> &endpoints) {
797
798 if (endpoints.empty())
799 return kCryptoNone; // Will update after scan results.
800
801 // Find weakest cipher (across endpoints) of the strongest ciphers
802 // (per endpoint).
803 Service::CryptoAlgorithm cipher = Service::kCryptoAes;
Paul Stewart6db7b242014-05-02 15:34:21 -0700804 for (const auto &endpoint : endpoints) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800805 Service::CryptoAlgorithm endpoint_cipher;
Paul Stewart6db7b242014-05-02 15:34:21 -0700806 if (endpoint->has_rsn_property()) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800807 endpoint_cipher = Service::kCryptoAes;
Paul Stewart6db7b242014-05-02 15:34:21 -0700808 } else if (endpoint->has_wpa_property()) {
mukesh agrawal43970a22013-02-15 16:00:07 -0800809 endpoint_cipher = Service::kCryptoRc4;
810 } else {
811 // We could be in the Dynamic WEP case here. But that's okay,
812 // because |cipher_8021x_| is not defined in that case.
813 endpoint_cipher = Service::kCryptoNone;
814 }
815 cipher = std::min(cipher, endpoint_cipher);
816 }
817 return cipher;
mukesh agrawale1d90e92012-02-15 17:36:08 -0800818}
819
mukesh agrawal1a056262011-10-05 14:36:54 -0700820// static
Thieu Lef4cbda92011-11-10 23:41:24 +0000821void WiFiService::ValidateWEPPassphrase(const std::string &passphrase,
822 Error *error) {
823 ParseWEPPassphrase(passphrase, NULL, NULL, error);
mukesh agrawal1a056262011-10-05 14:36:54 -0700824}
825
826// static
Thieu Lef4cbda92011-11-10 23:41:24 +0000827void WiFiService::ValidateWPAPassphrase(const std::string &passphrase,
828 Error *error) {
mukesh agrawal1a056262011-10-05 14:36:54 -0700829 unsigned int length = passphrase.length();
Ben Chan7fab8972014-08-10 17:14:46 -0700830 vector<uint8_t> passphrase_bytes;
mukesh agrawal1a056262011-10-05 14:36:54 -0700831
832 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) {
833 if (length != IEEE_80211::kWPAHexLen &&
834 (length < IEEE_80211::kWPAAsciiMinLen ||
835 length > IEEE_80211::kWPAAsciiMaxLen)) {
836 error->Populate(Error::kInvalidPassphrase);
837 }
838 } else {
839 if (length < IEEE_80211::kWPAAsciiMinLen ||
840 length > IEEE_80211::kWPAAsciiMaxLen) {
841 error->Populate(Error::kInvalidPassphrase);
842 }
843 }
Thieu Lef4cbda92011-11-10 23:41:24 +0000844}
mukesh agrawal1a056262011-10-05 14:36:54 -0700845
Thieu Lef4cbda92011-11-10 23:41:24 +0000846// static
847void WiFiService::ParseWEPPassphrase(const string &passphrase,
848 int *key_index,
Ben Chan7fab8972014-08-10 17:14:46 -0700849 std::vector<uint8_t> *password_bytes,
Thieu Lef4cbda92011-11-10 23:41:24 +0000850 Error *error) {
851 unsigned int length = passphrase.length();
852 int key_index_local;
853 std::string password_text;
854 bool is_hex = false;
855
856 switch (length) {
857 case IEEE_80211::kWEP40AsciiLen:
858 case IEEE_80211::kWEP104AsciiLen:
859 key_index_local = 0;
860 password_text = passphrase;
861 break;
862 case IEEE_80211::kWEP40AsciiLen + 2:
863 case IEEE_80211::kWEP104AsciiLen + 2:
864 if (CheckWEPKeyIndex(passphrase, error)) {
Alex Vakulenko8a532292014-06-16 17:18:44 -0700865 base::StringToInt(passphrase.substr(0, 1), &key_index_local);
Thieu Lef4cbda92011-11-10 23:41:24 +0000866 password_text = passphrase.substr(2);
867 }
868 break;
869 case IEEE_80211::kWEP40HexLen:
870 case IEEE_80211::kWEP104HexLen:
871 if (CheckWEPIsHex(passphrase, error)) {
872 key_index_local = 0;
873 password_text = passphrase;
874 is_hex = true;
875 }
876 break;
877 case IEEE_80211::kWEP40HexLen + 2:
878 case IEEE_80211::kWEP104HexLen + 2:
Alex Vakulenko8a532292014-06-16 17:18:44 -0700879 if (CheckWEPKeyIndex(passphrase, error) &&
Thieu Lef4cbda92011-11-10 23:41:24 +0000880 CheckWEPIsHex(passphrase.substr(2), error)) {
Alex Vakulenko8a532292014-06-16 17:18:44 -0700881 base::StringToInt(passphrase.substr(0, 1), &key_index_local);
Thieu Lef4cbda92011-11-10 23:41:24 +0000882 password_text = passphrase.substr(2);
883 is_hex = true;
884 } else if (CheckWEPPrefix(passphrase, error) &&
885 CheckWEPIsHex(passphrase.substr(2), error)) {
886 key_index_local = 0;
887 password_text = passphrase.substr(2);
888 is_hex = true;
889 }
890 break;
891 case IEEE_80211::kWEP40HexLen + 4:
892 case IEEE_80211::kWEP104HexLen + 4:
893 if (CheckWEPKeyIndex(passphrase, error) &&
894 CheckWEPPrefix(passphrase.substr(2), error) &&
895 CheckWEPIsHex(passphrase.substr(4), error)) {
Alex Vakulenko8a532292014-06-16 17:18:44 -0700896 base::StringToInt(passphrase.substr(0, 1), &key_index_local);
Thieu Lef4cbda92011-11-10 23:41:24 +0000897 password_text = passphrase.substr(4);
898 is_hex = true;
899 }
900 break;
901 default:
902 error->Populate(Error::kInvalidPassphrase);
903 break;
904 }
905
mukesh agrawal1a056262011-10-05 14:36:54 -0700906 if (error->IsSuccess()) {
Thieu Lef4cbda92011-11-10 23:41:24 +0000907 if (key_index)
908 *key_index = key_index_local;
909 if (password_bytes) {
910 if (is_hex)
911 base::HexStringToBytes(password_text, password_bytes);
912 else
913 password_bytes->insert(password_bytes->end(),
914 password_text.begin(),
915 password_text.end());
916 }
mukesh agrawal1a056262011-10-05 14:36:54 -0700917 }
918}
919
920// static
921bool WiFiService::CheckWEPIsHex(const string &passphrase, Error *error) {
Ben Chan7fab8972014-08-10 17:14:46 -0700922 vector<uint8_t> passphrase_bytes;
mukesh agrawal1a056262011-10-05 14:36:54 -0700923 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) {
924 return true;
925 } else {
926 error->Populate(Error::kInvalidPassphrase);
927 return false;
928 }
929}
930
931// static
932bool WiFiService::CheckWEPKeyIndex(const string &passphrase, Error *error) {
933 if (StartsWithASCII(passphrase, "0:", false) ||
934 StartsWithASCII(passphrase, "1:", false) ||
935 StartsWithASCII(passphrase, "2:", false) ||
936 StartsWithASCII(passphrase, "3:", false)) {
937 return true;
938 } else {
939 error->Populate(Error::kInvalidPassphrase);
940 return false;
941 }
942}
943
944// static
945bool WiFiService::CheckWEPPrefix(const string &passphrase, Error *error) {
946 if (StartsWithASCII(passphrase, "0x", false)) {
947 return true;
948 } else {
949 error->Populate(Error::kInvalidPassphrase);
950 return false;
951 }
952}
953
Paul Stewart6ab23a92011-11-09 17:17:47 -0800954// static
Paul Stewart6ab23a92011-11-09 17:17:47 -0800955string WiFiService::GetSecurityClass(const string &security) {
Ben Chanf024ef42013-09-20 14:21:38 -0700956 if (security == kSecurityRsn ||
957 security == kSecurityWpa) {
958 return kSecurityPsk;
Paul Stewartd08f4432011-11-04 07:48:20 -0700959 } else {
Paul Stewart6ab23a92011-11-09 17:17:47 -0800960 return security;
Paul Stewartd08f4432011-11-04 07:48:20 -0700961 }
962}
963
Wade Guthrie9ec08062013-09-25 15:22:24 -0700964
Ben Chan7fab8972014-08-10 17:14:46 -0700965int16_t WiFiService::SignalLevel() const {
Wade Guthrie9ec08062013-09-25 15:22:24 -0700966 return current_endpoint_ ? current_endpoint_->signal_strength() :
Ben Chan7fab8972014-08-10 17:14:46 -0700967 std::numeric_limits<int16_t>::min();
Wade Guthrie9ec08062013-09-25 15:22:24 -0700968}
969
Paul Stewarta41e38d2011-11-11 07:47:29 -0800970// static
971bool WiFiService::ParseStorageIdentifier(const string &storage_name,
972 string *address,
973 string *mode,
974 string *security) {
975 vector<string> wifi_parts;
976 base::SplitString(storage_name, '_', &wifi_parts);
Paul Stewart0756db92012-01-27 08:34:47 -0800977 if ((wifi_parts.size() != 5 && wifi_parts.size() != 6) ||
Ben Chanf024ef42013-09-20 14:21:38 -0700978 wifi_parts[0] != kTypeWifi) {
Paul Stewarta41e38d2011-11-11 07:47:29 -0800979 return false;
980 }
981 *address = wifi_parts[1];
982 *mode = wifi_parts[3];
Paul Stewart0756db92012-01-27 08:34:47 -0800983 if (wifi_parts.size() == 5) {
984 *security = wifi_parts[4];
985 } else {
986 // Account for security type "802_1x" which got split up above.
987 *security = wifi_parts[4] + "_" + wifi_parts[5];
988 }
Paul Stewarta41e38d2011-11-11 07:47:29 -0800989 return true;
990}
991
mukesh agrawale1d90e92012-02-15 17:36:08 -0800992// static
Paul Stewart85aea152013-01-22 09:31:56 -0800993bool WiFiService::FixupServiceEntries(StoreInterface *storage) {
994 bool fixed_entry = false;
995 set<string> groups = storage->GetGroups();
Paul Stewart6db7b242014-05-02 15:34:21 -0700996 for (const auto &id : groups) {
Paul Stewart85aea152013-01-22 09:31:56 -0800997 string device_address, network_mode, security;
998 if (!ParseStorageIdentifier(id, &device_address,
999 &network_mode, &security)) {
1000 continue;
1001 }
1002 if (!storage->GetString(id, kStorageType, NULL)) {
Ben Chanf024ef42013-09-20 14:21:38 -07001003 storage->SetString(id, kStorageType, kTypeWifi);
Paul Stewart85aea152013-01-22 09:31:56 -08001004 fixed_entry = true;
1005 }
1006 if (!storage->GetString(id, kStorageMode, NULL)) {
1007 storage->SetString(id, kStorageMode, network_mode);
1008 fixed_entry = true;
1009 }
1010 if (!storage->GetString(id, kStorageSecurity, NULL)) {
1011 storage->SetString(id, kStorageSecurity, security);
1012 fixed_entry = true;
1013 }
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001014 if (!storage->GetString(id, kStorageSecurityClass, NULL)) {
1015 storage->SetString(id, kStorageSecurityClass, GetSecurityClass(security));
1016 fixed_entry = true;
1017 }
Paul Stewart85aea152013-01-22 09:31:56 -08001018 }
1019 return fixed_entry;
1020}
1021
1022// static
Paul Stewartd2e1c362013-03-03 19:06:07 -08001023bool WiFiService::IsValidMode(const string &mode) {
Ben Chanf024ef42013-09-20 14:21:38 -07001024 return mode == kModeManaged || mode == kModeAdhoc;
Paul Stewartd2e1c362013-03-03 19:06:07 -08001025}
1026
1027// static
Paul Stewart3c504012013-01-17 17:49:58 -08001028bool WiFiService::IsValidSecurityMethod(const string &method) {
Ben Chanf024ef42013-09-20 14:21:38 -07001029 return method == kSecurityNone ||
1030 method == kSecurityWep ||
1031 method == kSecurityPsk ||
1032 method == kSecurityWpa ||
1033 method == kSecurityRsn ||
1034 method == kSecurity8021x;
Paul Stewart3c504012013-01-17 17:49:58 -08001035}
1036
1037// static
Ben Chan7fab8972014-08-10 17:14:46 -07001038uint8_t WiFiService::SignalToStrength(int16_t signal_dbm) {
1039 int16_t strength;
mukesh agrawale1d90e92012-02-15 17:36:08 -08001040 if (signal_dbm > 0) {
1041 if (!logged_signal_warning) {
1042 LOG(WARNING) << "Signal strength is suspiciously high. "
1043 << "Assuming value " << signal_dbm << " is not in dBm.";
1044 logged_signal_warning = true;
1045 }
1046 strength = signal_dbm;
1047 } else {
1048 strength = 120 + signal_dbm; // Call -20dBm "perfect".
1049 }
1050
mukesh agrawal8f3f7752012-02-17 19:42:09 -08001051 if (strength > kStrengthMax) {
1052 strength = kStrengthMax;
1053 } else if (strength < kStrengthMin) {
1054 strength = kStrengthMin;
mukesh agrawale1d90e92012-02-15 17:36:08 -08001055 }
1056 return strength;
1057}
1058
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001059KeyValueStore WiFiService::GetStorageProperties() const {
1060 KeyValueStore args;
Ben Chanf024ef42013-09-20 14:21:38 -07001061 args.SetString(kStorageType, kTypeWifi);
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001062 args.SetString(kStorageSSID, hex_ssid_);
1063 args.SetString(kStorageMode, mode_);
1064 args.SetString(kStorageSecurityClass, GetSecurityClass(security_));
1065 return args;
Paul Stewart6ab23a92011-11-09 17:17:47 -08001066}
1067
Paul Stewart71a4d3b2013-01-18 18:12:56 -08001068string WiFiService::GetDefaultStorageIdentifier() const {
1069 string security = GetSecurityClass(security_);
1070 return StringToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s",
Ben Chanf024ef42013-09-20 14:21:38 -07001071 kTypeWifi,
Paul Stewart3c504012013-01-17 17:49:58 -08001072 kAnyDeviceAddress,
Paul Stewartd08f4432011-11-04 07:48:20 -07001073 hex_ssid_.c_str(),
1074 mode_.c_str(),
1075 security.c_str()));
1076}
1077
Paul Stewart6df20bd2013-03-13 19:31:25 -07001078string WiFiService::GetSecurity(Error */*error*/) {
1079 if (current_endpoint_) {
1080 return current_endpoint_->security_mode();
1081 }
1082 return security_;
1083}
1084
Paul Stewart835934a2012-12-06 19:27:09 -08001085void WiFiService::ClearCachedCredentials() {
Paul Stewart3c504012013-01-17 17:49:58 -08001086 if (wifi_) {
1087 wifi_->ClearCachedCredentials(this);
1088 }
Paul Stewart835934a2012-12-06 19:27:09 -08001089}
1090
Paul Stewartc43cbbe2013-04-11 06:29:30 -07001091void WiFiService::OnEapCredentialsChanged() {
Rebecca Silbersteinb6df8b12014-07-10 16:07:08 -07001092 OnCredentialChange();
1093}
1094
1095void WiFiService::OnCredentialChange() {
Paul Stewart835934a2012-12-06 19:27:09 -08001096 ClearCachedCredentials();
Rebecca Silbersteinb6df8b12014-07-10 16:07:08 -07001097 SetHasEverConnected(false);
Gaurav Shah10109f22011-11-11 20:16:22 -08001098 UpdateConnectable();
Rebecca Silbersteinb6df8b12014-07-10 16:07:08 -07001099 ResetSuspectedCredentialFailures();
Gaurav Shah10109f22011-11-11 20:16:22 -08001100}
1101
Paul Stewart4357f4e2012-04-26 17:39:26 -07001102void WiFiService::OnProfileConfigured() {
1103 if (profile() || !hidden_ssid()) {
1104 return;
1105 }
1106 // This situation occurs when a hidden WiFi service created via GetService
1107 // has been persisted to a profile in Manager::ConfigureService(). Now
1108 // that configuration is saved, we must join the service with its profile,
1109 // which will make this SSID eligible for directed probes during scans.
1110 manager()->RegisterService(this);
1111}
1112
Gaurav Shah29d68882012-01-30 19:06:42 -08001113bool WiFiService::Is8021x() const {
Ben Chanf024ef42013-09-20 14:21:38 -07001114 if (security_ == kSecurity8021x)
Gaurav Shah29d68882012-01-30 19:06:42 -08001115 return true;
1116
1117 // Dynamic WEP + 802.1x.
Ben Chanf024ef42013-09-20 14:21:38 -07001118 if (security_ == kSecurityWep &&
Paul Stewart9413bcc2013-04-04 16:12:43 -07001119 GetEAPKeyManagement() == WPASupplicant::kKeyManagementIeee8021X)
Gaurav Shah29d68882012-01-30 19:06:42 -08001120 return true;
1121 return false;
1122}
1123
Paul Stewart3c504012013-01-17 17:49:58 -08001124WiFiRefPtr WiFiService::ChooseDevice() {
Paul Stewartee6b3d72013-07-12 16:07:51 -07001125 // TODO(pstew): Style frowns on dynamic_cast. crbug.com/220387
Paul Stewart3c504012013-01-17 17:49:58 -08001126 DeviceRefPtr device =
1127 manager()->GetEnabledDeviceWithTechnology(Technology::kWifi);
1128 return dynamic_cast<WiFi *>(device.get());
1129}
1130
1131void WiFiService::ResetWiFi() {
1132 SetWiFi(NULL);
1133}
1134
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001135void WiFiService::SetWiFi(const WiFiRefPtr &new_wifi) {
1136 if (wifi_ == new_wifi) {
Paul Stewart3c504012013-01-17 17:49:58 -08001137 return;
1138 }
1139 ClearCachedCredentials();
1140 if (wifi_) {
1141 wifi_->DisassociateFromService(this);
1142 }
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001143 if (new_wifi) {
Ben Chanf024ef42013-09-20 14:21:38 -07001144 adaptor()->EmitRpcIdentifierChanged(kDeviceProperty,
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001145 new_wifi->GetRpcIdentifier());
1146 } else {
Ben Chanf024ef42013-09-20 14:21:38 -07001147 adaptor()->EmitRpcIdentifierChanged(kDeviceProperty,
mukesh agrawalcbfb34e2013-04-17 19:33:25 -07001148 DBusAdaptor::kNullPath);
1149 }
1150 wifi_ = new_wifi;
Paul Stewart3c504012013-01-17 17:49:58 -08001151}
1152
mukesh agrawalb54601c2011-06-07 17:39:22 -07001153} // namespace shill