1 // 2 // Copyright (C) 2012 The Android Open Source Project 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 #include "shill/wifi/wifi_service.h" 18 19 #include <algorithm> 20 #include <limits> 21 #include <string> 22 #include <utility> 23 24 #include <base/strings/stringprintf.h> 25 #include <base/strings/string_number_conversions.h> 26 #include <base/strings/string_split.h> 27 #include <base/strings/string_util.h> 28 #if defined(__ANDROID__) 29 #include <dbus/service_constants.h> 30 #else 31 #include <chromeos/dbus/service_constants.h> 32 #endif // __ANDROID__ 33 34 #include "shill/adaptor_interfaces.h" 35 #include "shill/certificate_file.h" 36 #include "shill/control_interface.h" 37 #include "shill/device.h" 38 #include "shill/eap_credentials.h" 39 #include "shill/error.h" 40 #include "shill/event_dispatcher.h" 41 #include "shill/logging.h" 42 #include "shill/manager.h" 43 #include "shill/metrics.h" 44 #include "shill/net/ieee80211.h" 45 #include "shill/property_accessor.h" 46 #include "shill/store_interface.h" 47 #include "shill/supplicant/wpa_supplicant.h" 48 #include "shill/wifi/wifi.h" 49 #include "shill/wifi/wifi_endpoint.h" 50 #include "shill/wifi/wifi_provider.h" 51 52 using std::map; 53 using std::set; 54 using std::string; 55 using std::vector; 56 57 namespace shill { 58 59 namespace Logging { 60 static auto kModuleLogScope = ScopeLogger::kService; 61 static string ObjectID(const WiFiService* w) { return w->GetRpcIdentifier(); } 62 } 63 64 const char WiFiService::kAutoConnNoEndpoint[] = "no endpoints"; 65 const char WiFiService::kAnyDeviceAddress[] = "any"; 66 const int WiFiService::kSuspectedCredentialFailureThreshold = 3; 67 68 const char WiFiService::kStorageHiddenSSID[] = "WiFi.HiddenSSID"; 69 const char WiFiService::kStorageMode[] = "WiFi.Mode"; 70 const char WiFiService::kStoragePassphrase[] = "Passphrase"; 71 const char WiFiService::kStorageSecurity[] = "WiFi.Security"; 72 const char WiFiService::kStorageSecurityClass[] = "WiFi.SecurityClass"; 73 const char WiFiService::kStorageSSID[] = "SSID"; 74 const char WiFiService::kStoragePreferredDevice[] = "WiFi.PreferredDevice"; 75 const char WiFiService::kStorageRoamThreshold[] = "WiFi.RoamThreshold"; 76 const char WiFiService::kStorageRoamThresholdSet[] = "WiFi.RoamThresholdSet"; 77 78 bool WiFiService::logged_signal_warning = false; 79 80 WiFiService::WiFiService(ControlInterface* control_interface, 81 EventDispatcher* dispatcher, 82 Metrics* metrics, 83 Manager* manager, 84 WiFiProvider* provider, 85 const vector<uint8_t>& ssid, 86 const string& mode, 87 const string& security, 88 bool hidden_ssid) 89 : Service(control_interface, dispatcher, metrics, manager, 90 Technology::kWifi), 91 need_passphrase_(false), 92 security_(security), 93 mode_(mode), 94 hidden_ssid_(hidden_ssid), 95 frequency_(0), 96 physical_mode_(Metrics::kWiFiNetworkPhyModeUndef), 97 raw_signal_strength_(0), 98 cipher_8021x_(kCryptoNone), 99 suspected_credential_failures_(0), 100 ssid_(ssid), 101 ieee80211w_required_(false), 102 expecting_disconnect_(false), 103 certificate_file_(new CertificateFile()), 104 roam_threshold_db_(0), 105 roam_threshold_db_set_(false), 106 provider_(provider) { 107 PropertyStore* store = this->mutable_store(); 108 store->RegisterConstString(kModeProperty, &mode_); 109 HelpRegisterWriteOnlyDerivedString(kPassphraseProperty, 110 &WiFiService::SetPassphrase, 111 &WiFiService::ClearPassphrase, 112 nullptr); 113 store->RegisterBool(kPassphraseRequiredProperty, &need_passphrase_); 114 HelpRegisterConstDerivedString(kSecurityProperty, 115 &WiFiService::GetSecurity); 116 HelpRegisterConstDerivedString(kSecurityClassProperty, 117 &WiFiService::GetSecurityClass); 118 119 store->RegisterConstString(kWifiAuthMode, &auth_mode_); 120 store->RegisterBool(kWifiHiddenSsid, &hidden_ssid_); 121 store->RegisterConstUint16(kWifiFrequency, &frequency_); 122 store->RegisterConstUint16s(kWifiFrequencyListProperty, &frequency_list_); 123 store->RegisterConstUint16(kWifiPhyMode, &physical_mode_); 124 store->RegisterConstString(kWifiBSsid, &bssid_); 125 store->RegisterConstString(kCountryProperty, &country_code_); 126 store->RegisterConstStringmap(kWifiVendorInformationProperty, 127 &vendor_information_); 128 store->RegisterConstBool(kWifiProtectedManagementFrameRequiredProperty, 129 &ieee80211w_required_); 130 131 hex_ssid_ = base::HexEncode(ssid_.data(), ssid_.size()); 132 store->RegisterConstString(kWifiHexSsid, &hex_ssid_); 133 HelpRegisterDerivedString(kWifiPreferredDeviceProperty, 134 &WiFiService::GetPreferredDevice, 135 &WiFiService::SetPreferredDevice); 136 HelpRegisterDerivedUint16(kWifiRoamThresholdProperty, 137 &WiFiService::GetRoamThreshold, 138 &WiFiService::SetRoamThreshold, 139 &WiFiService::ClearRoamThreshold); 140 141 string ssid_string( 142 reinterpret_cast<const char*>(ssid_.data()), ssid_.size()); 143 WiFi::SanitizeSSID(&ssid_string); 144 set_friendly_name(ssid_string); 145 146 SetEapCredentials(new EapCredentials()); 147 148 // TODO(quiche): determine if it is okay to set EAP.KeyManagement for 149 // a service that is not 802.1x. 150 if (Is8021x()) { 151 // Passphrases are not mandatory for 802.1X. 152 need_passphrase_ = false; 153 } else if (security_ == kSecurityPsk) { 154 SetEAPKeyManagement("WPA-PSK"); 155 } else if (security_ == kSecurityRsn) { 156 SetEAPKeyManagement("WPA-PSK"); 157 } else if (security_ == kSecurityWpa) { 158 SetEAPKeyManagement("WPA-PSK"); 159 } else if (security_ == kSecurityWep) { 160 SetEAPKeyManagement("NONE"); 161 } else if (security_ == kSecurityNone) { 162 SetEAPKeyManagement("NONE"); 163 } else { 164 LOG(ERROR) << "Unsupported security method " << security_; 165 } 166 167 // Until we know better (at Profile load time), use the generic name. 168 storage_identifier_ = GetDefaultStorageIdentifier(); 169 UpdateConnectable(); 170 UpdateSecurity(); 171 172 // Now that |this| is a fully constructed WiFiService, synchronize observers 173 // with our current state, and emit the appropriate change notifications. 174 // (Initial observer state may have been set in our base class.) 175 NotifyPropertyChanges(); 176 177 IgnoreParameterForConfigure(kModeProperty); 178 IgnoreParameterForConfigure(kSSIDProperty); 179 IgnoreParameterForConfigure(kSecurityProperty); 180 IgnoreParameterForConfigure(kSecurityClassProperty); 181 IgnoreParameterForConfigure(kWifiHexSsid); 182 183 InitializeCustomMetrics(); 184 185 // Log the |unique_name| to |friendly_name| mapping for debugging purposes. 186 // The latter will be tagged for scrubbing. 187 LOG(INFO) << "Constructed WiFi service " << unique_name() 188 << " name: " << WiFi::LogSSID(friendly_name()); 189 } 190 191 WiFiService::~WiFiService() {} 192 193 bool WiFiService::IsAutoConnectable(const char** reason) const { 194 if (!Service::IsAutoConnectable(reason)) { 195 return false; 196 } 197 198 // Only auto-connect to Services which have visible Endpoints. 199 // (Needed because hidden Services may remain registered with 200 // Manager even without visible Endpoints.) 201 if (!HasEndpoints()) { 202 *reason = kAutoConnNoEndpoint; 203 return false; 204 } 205 206 CHECK(wifi_) << "We have endpoints but no WiFi device is selected?"; 207 208 // Do not preempt an existing connection (whether pending, or 209 // connected, and whether to this service, or another). 210 if (!wifi_->IsIdle()) { 211 *reason = kAutoConnBusy; 212 return false; 213 } 214 215 return true; 216 } 217 218 void WiFiService::SetEAPKeyManagement(const string& key_management) { 219 Service::SetEAPKeyManagement(key_management); 220 UpdateSecurity(); 221 } 222 223 void WiFiService::AddEndpoint(const WiFiEndpointConstRefPtr& endpoint) { 224 DCHECK(endpoint->ssid() == ssid()); 225 endpoints_.insert(endpoint); 226 UpdateFromEndpoints(); 227 } 228 229 void WiFiService::RemoveEndpoint(const WiFiEndpointConstRefPtr& endpoint) { 230 set<WiFiEndpointConstRefPtr>::iterator i = endpoints_.find(endpoint); 231 DCHECK(i != endpoints_.end()); 232 if (i == endpoints_.end()) { 233 LOG(WARNING) << "In " << __func__ << "(): " 234 << "ignoring non-existent endpoint " 235 << endpoint->bssid_string(); 236 return; 237 } 238 endpoints_.erase(i); 239 if (current_endpoint_ == endpoint) { 240 current_endpoint_ = nullptr; 241 } 242 UpdateFromEndpoints(); 243 } 244 245 void WiFiService::NotifyCurrentEndpoint( 246 const WiFiEndpointConstRefPtr& endpoint) { 247 DCHECK(!endpoint || (endpoints_.find(endpoint) != endpoints_.end())); 248 current_endpoint_ = endpoint; 249 UpdateFromEndpoints(); 250 } 251 252 void WiFiService::NotifyEndpointUpdated( 253 const WiFiEndpointConstRefPtr& endpoint) { 254 DCHECK(endpoints_.find(endpoint) != endpoints_.end()); 255 UpdateFromEndpoints(); 256 } 257 258 string WiFiService::GetStorageIdentifier() const { 259 return storage_identifier_; 260 } 261 262 bool WiFiService::SetPassphrase(const string& passphrase, Error* error) { 263 if (security_ == kSecurityWep) { 264 ValidateWEPPassphrase(passphrase, error); 265 } else if (security_ == kSecurityPsk || 266 security_ == kSecurityWpa || 267 security_ == kSecurityRsn) { 268 ValidateWPAPassphrase(passphrase, error); 269 } else { 270 error->Populate(Error::kNotSupported); 271 } 272 273 if (!error->IsSuccess()) { 274 LOG(ERROR) << "Passphrase could not be set: " << error->message(); 275 return false; 276 } 277 278 return SetPassphraseInternal(passphrase, Service::kReasonPropertyUpdate); 279 } 280 281 bool WiFiService::SetPassphraseInternal( 282 const string& passphrase, 283 Service::UpdateCredentialsReason reason) { 284 if (passphrase_ == passphrase) { 285 // After a user logs in, Chrome may reconfigure a Service with the 286 // same credentials as before login. When that occurs, we don't 287 // want to bump the user off the network. Hence, we MUST return 288 // early. (See crbug.com/231456#c17) 289 return false; 290 } 291 passphrase_ = passphrase; 292 OnCredentialChange(reason); 293 return true; 294 } 295 296 // ClearPassphrase is separate from SetPassphrase, because the default 297 // value for |passphrase_| would not pass validation. 298 void WiFiService::ClearPassphrase(Error* /*error*/) { 299 passphrase_.clear(); 300 ClearCachedCredentials(); 301 UpdateConnectable(); 302 } 303 304 string WiFiService::GetPreferredDevice(Error* /*error*/) { 305 return preferred_device_; 306 } 307 308 bool WiFiService::SetPreferredDevice(const string& device_name, 309 Error* /*error*/) { 310 // Reset device if it is not the preferred device. 311 if (!device_name.empty() && wifi_ && wifi_->link_name() != device_name) { 312 ResetWiFi(); 313 } 314 preferred_device_ = device_name; 315 return true; 316 } 317 318 string WiFiService::GetTethering(Error* /*error*/) const { 319 if (IsConnected() && wifi_ && wifi_->IsConnectedViaTether()) { 320 return kTetheringConfirmedState; 321 } 322 323 // Only perform BSSID tests if there is exactly one matching endpoint, 324 // so we ignore campuses that may use locally administered BSSIDs. 325 if (endpoints_.size() == 1 && 326 (*endpoints_.begin())->has_tethering_signature()) { 327 return kTetheringSuspectedState; 328 } 329 330 return kTetheringNotDetectedState; 331 } 332 333 string WiFiService::GetLoadableStorageIdentifier( 334 const StoreInterface& storage) const { 335 set<string> groups = storage.GetGroupsWithProperties(GetStorageProperties()); 336 if (groups.empty()) { 337 LOG(WARNING) << "Configuration for service " 338 << unique_name() 339 << " is not available in the persistent store"; 340 return ""; 341 } 342 if (groups.size() > 1) { 343 LOG(WARNING) << "More than one configuration for service " 344 << unique_name() 345 << " is available; choosing the first."; 346 } 347 return *groups.begin(); 348 } 349 350 bool WiFiService::IsLoadableFrom(const StoreInterface& storage) const { 351 return !storage.GetGroupsWithProperties(GetStorageProperties()).empty(); 352 } 353 354 bool WiFiService::IsVisible() const { 355 // WiFi Services should be displayed only if they are in range (have 356 // endpoints that have shown up in a scan) or if the service is actively 357 // being connected. 358 return HasEndpoints() || IsConnected() || IsConnecting(); 359 } 360 361 bool WiFiService::Load(StoreInterface* storage) { 362 string id = GetLoadableStorageIdentifier(*storage); 363 if (id.empty()) { 364 return false; 365 } 366 367 // Set our storage identifier to match the storage name in the Profile. 368 storage_identifier_ = id; 369 370 // Load properties common to all Services. 371 if (!Service::Load(storage)) { 372 return false; 373 } 374 375 // Load properties specific to WiFi services. 376 storage->GetBool(id, kStorageHiddenSSID, &hidden_ssid_); 377 378 // NB: mode, security and ssid parameters are never read in from 379 // Load() as they are provided from the scan. 380 381 string passphrase; 382 if (storage->GetCryptedString(id, kStoragePassphrase, &passphrase)) { 383 if (SetPassphraseInternal(passphrase, Service::kReasonCredentialsLoaded)) { 384 SLOG(this, 3) << "Loaded passphrase in WiFiService::Load."; 385 } 386 } 387 388 string preferred_device; 389 storage->GetString(id, kStoragePreferredDevice, &preferred_device); 390 SetPreferredDevice(preferred_device, nullptr); 391 392 uint64_t stored_roam_threshold_temp; 393 storage->GetUint64(id, kStorageRoamThreshold, &stored_roam_threshold_temp); 394 // Storing a uint64_t in a uint16_t is safe here since we know that we only 395 // set this storage value to a uint16_t value in WiFiService::Save. 396 roam_threshold_db_ = static_cast<uint16_t>(stored_roam_threshold_temp); 397 storage->GetBool(id, kStorageRoamThresholdSet, &roam_threshold_db_set_); 398 399 expecting_disconnect_ = false; 400 return true; 401 } 402 403 bool WiFiService::Save(StoreInterface* storage) { 404 // Save properties common to all Services. 405 if (!Service::Save(storage)) { 406 return false; 407 } 408 409 // Save properties specific to WiFi services. 410 const string id = GetStorageIdentifier(); 411 storage->SetBool(id, kStorageHiddenSSID, hidden_ssid_); 412 storage->SetString(id, kStorageMode, mode_); 413 storage->SetCryptedString(id, kStoragePassphrase, passphrase_); 414 storage->SetString(id, kStorageSecurity, security_); 415 storage->SetString(id, kStorageSecurityClass, 416 ComputeSecurityClass(security_)); 417 storage->SetString(id, kStorageSSID, hex_ssid_); 418 storage->SetUint64(id, kStorageRoamThreshold, 419 static_cast<uint64_t>(roam_threshold_db_)); 420 storage->SetBool(id, kStorageRoamThresholdSet, roam_threshold_db_set_); 421 Service::SaveString(storage, id, kStoragePreferredDevice, preferred_device_, 422 false, false); 423 424 return true; 425 } 426 427 bool WiFiService::Unload() { 428 // Expect the service to be disconnected if is currently connected or 429 // in the process of connecting. 430 if (IsConnected() || IsConnecting()) { 431 expecting_disconnect_ = true; 432 } else { 433 expecting_disconnect_ = false; 434 } 435 Service::Unload(); 436 if (wifi_) { 437 wifi_->DestroyServiceLease(*this); 438 } 439 hidden_ssid_ = false; 440 ResetSuspectedCredentialFailures(); 441 Error unused_error; 442 ClearPassphrase(&unused_error); 443 preferred_device_.clear(); 444 roam_threshold_db_ = 0; 445 roam_threshold_db_set_ = false; 446 return provider_->OnServiceUnloaded(this); 447 } 448 449 void WiFiService::SetState(ConnectState state) { 450 Service::SetState(state); 451 NotifyPropertyChanges(); 452 } 453 454 bool WiFiService::IsSecurityMatch(const string& security) const { 455 return ComputeSecurityClass(security) == ComputeSecurityClass(security_); 456 } 457 458 bool WiFiService::AddSuspectedCredentialFailure() { 459 if (!has_ever_connected()) { 460 return true; 461 } 462 ++suspected_credential_failures_; 463 return suspected_credential_failures_ >= kSuspectedCredentialFailureThreshold; 464 } 465 466 void WiFiService::ResetSuspectedCredentialFailures() { 467 suspected_credential_failures_ = 0; 468 } 469 470 void WiFiService::InitializeCustomMetrics() const { 471 SLOG(Metrics, this, 2) << __func__ << " for " << unique_name(); 472 string histogram = metrics()->GetFullMetricName( 473 Metrics::kMetricTimeToJoinMillisecondsSuffix, 474 technology()); 475 metrics()->AddServiceStateTransitionTimer(*this, 476 histogram, 477 Service::kStateAssociating, 478 Service::kStateConfiguring); 479 } 480 481 void WiFiService::SendPostReadyStateMetrics( 482 int64_t time_resume_to_ready_milliseconds) const { 483 metrics()->SendEnumToUMA( 484 metrics()->GetFullMetricName(Metrics::kMetricNetworkChannelSuffix, 485 technology()), 486 Metrics::WiFiFrequencyToChannel(frequency_), 487 Metrics::kMetricNetworkChannelMax); 488 489 DCHECK(physical_mode_ < Metrics::kWiFiNetworkPhyModeMax); 490 metrics()->SendEnumToUMA( 491 metrics()->GetFullMetricName(Metrics::kMetricNetworkPhyModeSuffix, 492 technology()), 493 static_cast<Metrics::WiFiNetworkPhyMode>(physical_mode_), 494 Metrics::kWiFiNetworkPhyModeMax); 495 496 string security_mode = security_; 497 if (current_endpoint_) { 498 security_mode = current_endpoint_->security_mode(); 499 } 500 Metrics::WiFiSecurity security_uma = 501 Metrics::WiFiSecurityStringToEnum(security_mode); 502 DCHECK(security_uma != Metrics::kWiFiSecurityUnknown); 503 metrics()->SendEnumToUMA( 504 metrics()->GetFullMetricName(Metrics::kMetricNetworkSecuritySuffix, 505 technology()), 506 security_uma, 507 Metrics::kMetricNetworkSecurityMax); 508 509 if (Is8021x()) { 510 eap()->OutputConnectionMetrics(metrics(), technology()); 511 } 512 513 // We invert the sign of the signal strength value, since UMA histograms 514 // cannot represent negative numbers (it stores them but cannot display 515 // them), and dBm values of interest start at 0 and go negative from there. 516 metrics()->SendToUMA( 517 metrics()->GetFullMetricName(Metrics::kMetricNetworkSignalStrengthSuffix, 518 technology()), 519 -raw_signal_strength_, 520 Metrics::kMetricNetworkSignalStrengthMin, 521 Metrics::kMetricNetworkSignalStrengthMax, 522 Metrics::kMetricNetworkSignalStrengthNumBuckets); 523 524 if (time_resume_to_ready_milliseconds > 0) { 525 metrics()->SendToUMA( 526 metrics()->GetFullMetricName( 527 Metrics::kMetricTimeResumeToReadyMillisecondsSuffix, technology()), 528 time_resume_to_ready_milliseconds, 529 Metrics::kTimerHistogramMillisecondsMin, 530 Metrics::kTimerHistogramMillisecondsMax, 531 Metrics::kTimerHistogramNumBuckets); 532 } 533 534 Metrics::WiFiApMode ap_mode_uma = Metrics::WiFiApModeStringToEnum(mode_); 535 metrics()->SendEnumToUMA( 536 metrics()->GetFullMetricName(Metrics::kMetricNetworkApModeSuffix, 537 technology()), 538 ap_mode_uma, 539 Metrics::kWiFiApModeMax); 540 } 541 542 // private methods 543 void WiFiService::HelpRegisterConstDerivedString( 544 const string& name, 545 string(WiFiService::*get)(Error*)) { 546 mutable_store()->RegisterDerivedString( 547 name, 548 StringAccessor( 549 new CustomAccessor<WiFiService, string>(this, get, nullptr))); 550 } 551 552 void WiFiService::HelpRegisterDerivedString( 553 const string& name, 554 string(WiFiService::*get)(Error* error), 555 bool(WiFiService::*set)(const string&, Error*)) { 556 mutable_store()->RegisterDerivedString( 557 name, 558 StringAccessor(new CustomAccessor<WiFiService, string>(this, get, set))); 559 } 560 561 void WiFiService::HelpRegisterWriteOnlyDerivedString( 562 const string& name, 563 bool(WiFiService::*set)(const string&, Error*), 564 void(WiFiService::*clear)(Error* error), 565 const string* default_value) { 566 mutable_store()->RegisterDerivedString( 567 name, 568 StringAccessor( 569 new CustomWriteOnlyAccessor<WiFiService, string>( 570 this, set, clear, default_value))); 571 } 572 573 void WiFiService::HelpRegisterDerivedUint16( 574 const string& name, 575 uint16_t(WiFiService::*get)(Error* error), 576 bool(WiFiService::*set)(const uint16_t& value, Error* error), 577 void(WiFiService::*clear)(Error* error)) { 578 mutable_store()->RegisterDerivedUint16( 579 name, Uint16Accessor(new CustomAccessor<WiFiService, uint16_t>( 580 this, get, set, clear))); 581 } 582 583 void WiFiService::Connect(Error* error, const char* reason) { 584 if (!connectable()) { 585 LOG(ERROR) << "Can't connect. Service " << unique_name() 586 << " is not connectable."; 587 Error::PopulateAndLog(FROM_HERE, 588 error, 589 Error::kOperationFailed, 590 Error::GetDefaultMessage(Error::kOperationFailed)); 591 return; 592 } 593 if (IsConnecting() || IsConnected()) { 594 LOG(WARNING) << "Can't connect. Service " << unique_name() 595 << " is already connecting or connected."; 596 Error::PopulateAndLog(FROM_HERE, 597 error, 598 Error::kAlreadyConnected, 599 Error::GetDefaultMessage(Error::kAlreadyConnected)); 600 return; 601 } 602 603 WiFiRefPtr wifi = wifi_; 604 if (!wifi) { 605 // If this is a hidden service before it has been found in a scan, we 606 // may need to late-bind to any available WiFi Device. We don't actually 607 // set |wifi_| in this case since we do not yet see any endpoints. This 608 // will mean this service is not disconnectable until an endpoint is 609 // found. 610 wifi = ChooseDevice(); 611 if (!wifi) { 612 LOG(ERROR) << "Can't connect. Service " << unique_name() 613 << " cannot find a WiFi device."; 614 Error::PopulateAndLog(FROM_HERE, 615 error, 616 Error::kOperationFailed, 617 Error::GetDefaultMessage(Error::kOperationFailed)); 618 return; 619 } 620 } 621 622 if (wifi->IsCurrentService(this)) { 623 LOG(WARNING) << "Can't connect. Service " << unique_name() 624 << " is the current service (but, in " << GetStateString() 625 << " state, not connected)."; 626 Error::PopulateAndLog(FROM_HERE, 627 error, 628 Error::kInProgress, 629 Error::GetDefaultMessage(Error::kInProgress)); 630 return; 631 } 632 633 // Report number of BSSes available for this service. 634 metrics()->NotifyWifiAvailableBSSes(endpoints_.size()); 635 636 if (Is8021x()) { 637 // If EAP key management is not set, set to a default. 638 if (GetEAPKeyManagement().empty()) 639 SetEAPKeyManagement("WPA-EAP"); 640 ClearEAPCertification(); 641 } 642 643 expecting_disconnect_ = false; 644 Service::Connect(error, reason); 645 wifi->ConnectTo(this); 646 } 647 648 KeyValueStore WiFiService::GetSupplicantConfigurationParameters() const { 649 KeyValueStore params; 650 651 params.SetUint(WPASupplicant::kNetworkPropertyMode, 652 WiFiEndpoint::ModeStringToUint(mode_)); 653 654 if (mode_ == kModeAdhoc && frequency_ != 0) { 655 // Frequency is required in order to successfully connect to an IBSS 656 // with wpa_supplicant. If we have one from our endpoint, insert it 657 // here. 658 params.SetInt(WPASupplicant::kNetworkPropertyFrequency, frequency_); 659 } 660 661 if (Is8021x()) { 662 eap()->PopulateSupplicantProperties(certificate_file_.get(), ¶ms); 663 } else if (security_ == kSecurityPsk || 664 security_ == kSecurityRsn || 665 security_ == kSecurityWpa) { 666 const string psk_proto = 667 base::StringPrintf("%s %s", 668 WPASupplicant::kSecurityModeWPA, 669 WPASupplicant::kSecurityModeRSN); 670 params.SetString(WPASupplicant::kPropertySecurityProtocol, psk_proto); 671 params.SetString(WPASupplicant::kPropertyPreSharedKey, passphrase_); 672 } else if (security_ == kSecurityWep) { 673 params.SetString(WPASupplicant::kPropertyAuthAlg, 674 WPASupplicant::kSecurityAuthAlg); 675 Error unused_error; 676 int key_index; 677 std::vector<uint8_t> password_bytes; 678 ParseWEPPassphrase(passphrase_, &key_index, &password_bytes, &unused_error); 679 params.SetUint8s(WPASupplicant::kPropertyWEPKey + 680 base::IntToString(key_index), 681 password_bytes); 682 params.SetUint(WPASupplicant::kPropertyWEPTxKeyIndex, key_index); 683 } else if (security_ == kSecurityNone) { 684 // Nothing special to do here. 685 } else { 686 NOTIMPLEMENTED() << "Unsupported security method " << security_; 687 } 688 689 params.SetString(WPASupplicant::kNetworkPropertyEapKeyManagement, 690 key_management()); 691 692 if (ieee80211w_required_) { 693 // TODO(pstew): We should also enable IEEE 802.11w if the user 694 // explicitly enables support for this through a service / device 695 // property. crbug.com/219950 696 params.SetUint(WPASupplicant::kNetworkPropertyIeee80211w, 697 WPASupplicant::kNetworkIeee80211wEnabled); 698 } 699 700 params.SetUint8s(WPASupplicant::kNetworkPropertySSID, ssid_); 701 702 return params; 703 } 704 705 706 void WiFiService::Disconnect(Error* error, const char* reason) { 707 Service::Disconnect(error, reason); 708 if (!wifi_) { 709 // If we are connecting to a hidden service, but have not yet found 710 // any endpoints, we could end up with a disconnect request without 711 // a wifi_ reference. This is not a fatal error. 712 LOG_IF(ERROR, IsConnecting()) 713 << "WiFi endpoints do not (yet) exist. Cannot disconnect service " 714 << unique_name(); 715 LOG_IF(FATAL, IsConnected()) 716 << "WiFi device does not exist. Cannot disconnect service " 717 << unique_name(); 718 error->Populate(Error::kOperationFailed); 719 return; 720 } 721 wifi_->DisconnectFromIfActive(this); 722 } 723 724 string WiFiService::GetDeviceRpcId(Error* error) const { 725 if (!wifi_) { 726 error->Populate(Error::kNotFound, "Not associated with a device"); 727 return control_interface()->NullRPCIdentifier(); 728 } 729 return wifi_->GetRpcIdentifier(); 730 } 731 732 void WiFiService::UpdateConnectable() { 733 bool is_connectable = false; 734 if (security_ == kSecurityNone) { 735 DCHECK(passphrase_.empty()); 736 need_passphrase_ = false; 737 is_connectable = true; 738 } else if (Is8021x()) { 739 is_connectable = Is8021xConnectable(); 740 } else if (security_ == kSecurityWep || 741 security_ == kSecurityWpa || 742 security_ == kSecurityPsk || 743 security_ == kSecurityRsn) { 744 need_passphrase_ = passphrase_.empty(); 745 is_connectable = !need_passphrase_; 746 } 747 SetConnectable(is_connectable); 748 } 749 750 void WiFiService::UpdateFromEndpoints() { 751 const WiFiEndpoint* representative_endpoint = nullptr; 752 753 if (current_endpoint_) { 754 representative_endpoint = current_endpoint_.get(); 755 } else { 756 int16_t best_signal = std::numeric_limits<int16_t>::min(); 757 bool preferred_device_found = false; 758 // This will set the representative_endpoint to the best endpoint associated 759 // with the preferred_device_ if it exist, otherwise the best overall 760 // endpoint. 761 for (const auto& endpoint : endpoints_) { 762 if (preferred_device_found) { 763 // Skip endpoints associated with non-preferred device. 764 if (endpoint->device()->link_name() != preferred_device_) { 765 continue; 766 } 767 } else if (endpoint->device() && 768 endpoint->device()->link_name() == preferred_device_) { 769 // Found first endpoint associated with preferred device. 770 preferred_device_found = true; 771 best_signal = std::numeric_limits<int16_t>::min(); 772 } 773 774 if (endpoint->signal_strength() >= best_signal) { 775 best_signal = endpoint->signal_strength(); 776 representative_endpoint = endpoint.get(); 777 } 778 } 779 } 780 781 WiFiRefPtr wifi; 782 if (representative_endpoint) { 783 wifi = representative_endpoint->device(); 784 if (bssid_ != representative_endpoint->bssid_string() || 785 raw_signal_strength_ != representative_endpoint->signal_strength() || 786 frequency_ != representative_endpoint->frequency()) { 787 LOG(INFO) 788 << "Representative endpoint updated for service " << unique_name() 789 << ". " 790 << WiFi::LogSSID(representative_endpoint->ssid_string()) << ", " 791 << "bssid: " << representative_endpoint->bssid_string() << ", " 792 << "signal: " << representative_endpoint->signal_strength() << ", " 793 << "security: " << representative_endpoint->security_mode() << ", " 794 << "frequency: " << representative_endpoint->frequency(); 795 } 796 } else if (IsConnected() || IsConnecting()) { 797 LOG(WARNING) << "Service " << unique_name() 798 << " will disconnect due to no remaining endpoints."; 799 } 800 801 SetWiFi(wifi); 802 803 for (const auto& endpoint : endpoints_) { 804 if (endpoint->ieee80211w_required()) { 805 // Never reset ieee80211w_required_ to false, so we track whether we have 806 // ever seen an AP that requires 802.11w. 807 ieee80211w_required_ = true; 808 } 809 } 810 811 set<uint16_t> frequency_set; 812 for (const auto& endpoint : endpoints_) { 813 frequency_set.insert(endpoint->frequency()); 814 } 815 frequency_list_.assign(frequency_set.begin(), frequency_set.end()); 816 817 if (Is8021x()) 818 cipher_8021x_ = ComputeCipher8021x(endpoints_); 819 820 uint16_t frequency = 0; 821 int16_t signal = std::numeric_limits<int16_t>::min(); 822 string bssid; 823 string country_code; 824 Stringmap vendor_information; 825 uint16_t physical_mode = Metrics::kWiFiNetworkPhyModeUndef; 826 // Represent "unknown raw signal strength" as 0. 827 raw_signal_strength_ = 0; 828 if (representative_endpoint) { 829 frequency = representative_endpoint->frequency(); 830 signal = representative_endpoint->signal_strength(); 831 raw_signal_strength_ = signal; 832 bssid = representative_endpoint->bssid_string(); 833 country_code = representative_endpoint->country_code(); 834 vendor_information = representative_endpoint->GetVendorInformation(); 835 physical_mode = representative_endpoint->physical_mode(); 836 } 837 838 if (frequency_ != frequency) { 839 frequency_ = frequency; 840 adaptor()->EmitUint16Changed(kWifiFrequency, frequency_); 841 } 842 if (bssid_ != bssid) { 843 bssid_ = bssid; 844 adaptor()->EmitStringChanged(kWifiBSsid, bssid_); 845 } 846 if (country_code_ != country_code) { 847 country_code_ = country_code; 848 adaptor()->EmitStringChanged(kCountryProperty, country_code_); 849 } 850 if (vendor_information_ != vendor_information) { 851 vendor_information_ = vendor_information; 852 adaptor()->EmitStringmapChanged(kWifiVendorInformationProperty, 853 vendor_information_); 854 } 855 if (physical_mode_ != physical_mode) { 856 physical_mode_ = physical_mode; 857 adaptor()->EmitUint16Changed(kWifiPhyMode, physical_mode_); 858 } 859 adaptor()->EmitUint16sChanged(kWifiFrequencyListProperty, frequency_list_); 860 SetStrength(SignalToStrength(signal)); 861 UpdateSecurity(); 862 NotifyPropertyChanges(); 863 } 864 865 void WiFiService::UpdateSecurity() { 866 CryptoAlgorithm algorithm = kCryptoNone; 867 bool key_rotation = false; 868 bool endpoint_auth = false; 869 870 if (security_ == kSecurityNone) { 871 // initial values apply 872 } else if (security_ == kSecurityWep) { 873 algorithm = kCryptoRc4; 874 key_rotation = Is8021x(); 875 endpoint_auth = Is8021x(); 876 } else if (security_ == kSecurityPsk || 877 security_ == kSecurityWpa) { 878 algorithm = kCryptoRc4; 879 key_rotation = true; 880 endpoint_auth = false; 881 } else if (security_ == kSecurityRsn) { 882 algorithm = kCryptoAes; 883 key_rotation = true; 884 endpoint_auth = false; 885 } else if (security_ == kSecurity8021x) { 886 algorithm = cipher_8021x_; 887 key_rotation = true; 888 endpoint_auth = true; 889 } 890 SetSecurity(algorithm, key_rotation, endpoint_auth); 891 } 892 893 // static 894 Service::CryptoAlgorithm WiFiService::ComputeCipher8021x( 895 const set<WiFiEndpointConstRefPtr>& endpoints) { 896 897 if (endpoints.empty()) 898 return kCryptoNone; // Will update after scan results. 899 900 // Find weakest cipher (across endpoints) of the strongest ciphers 901 // (per endpoint). 902 Service::CryptoAlgorithm cipher = Service::kCryptoAes; 903 for (const auto& endpoint : endpoints) { 904 Service::CryptoAlgorithm endpoint_cipher; 905 if (endpoint->has_rsn_property()) { 906 endpoint_cipher = Service::kCryptoAes; 907 } else if (endpoint->has_wpa_property()) { 908 endpoint_cipher = Service::kCryptoRc4; 909 } else { 910 // We could be in the Dynamic WEP case here. But that's okay, 911 // because |cipher_8021x_| is not defined in that case. 912 endpoint_cipher = Service::kCryptoNone; 913 } 914 cipher = std::min(cipher, endpoint_cipher); 915 } 916 return cipher; 917 } 918 919 // static 920 void WiFiService::ValidateWEPPassphrase(const std::string& passphrase, 921 Error* error) { 922 ParseWEPPassphrase(passphrase, nullptr, nullptr, error); 923 } 924 925 // static 926 void WiFiService::ValidateWPAPassphrase(const std::string& passphrase, 927 Error* error) { 928 unsigned int length = passphrase.length(); 929 vector<uint8_t> passphrase_bytes; 930 931 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) { 932 if (length != IEEE_80211::kWPAHexLen && 933 (length < IEEE_80211::kWPAAsciiMinLen || 934 length > IEEE_80211::kWPAAsciiMaxLen)) { 935 error->Populate(Error::kInvalidPassphrase); 936 } 937 } else { 938 if (length < IEEE_80211::kWPAAsciiMinLen || 939 length > IEEE_80211::kWPAAsciiMaxLen) { 940 error->Populate(Error::kInvalidPassphrase); 941 } 942 } 943 } 944 945 // static 946 void WiFiService::ParseWEPPassphrase(const string& passphrase, 947 int* key_index, 948 std::vector<uint8_t>* password_bytes, 949 Error* error) { 950 unsigned int length = passphrase.length(); 951 int key_index_local; 952 std::string password_text; 953 bool is_hex = false; 954 955 switch (length) { 956 case IEEE_80211::kWEP40AsciiLen: 957 case IEEE_80211::kWEP104AsciiLen: 958 key_index_local = 0; 959 password_text = passphrase; 960 break; 961 case IEEE_80211::kWEP40AsciiLen + 2: 962 case IEEE_80211::kWEP104AsciiLen + 2: 963 if (CheckWEPKeyIndex(passphrase, error)) { 964 base::StringToInt(passphrase.substr(0, 1), &key_index_local); 965 password_text = passphrase.substr(2); 966 } 967 break; 968 case IEEE_80211::kWEP40HexLen: 969 case IEEE_80211::kWEP104HexLen: 970 if (CheckWEPIsHex(passphrase, error)) { 971 key_index_local = 0; 972 password_text = passphrase; 973 is_hex = true; 974 } 975 break; 976 case IEEE_80211::kWEP40HexLen + 2: 977 case IEEE_80211::kWEP104HexLen + 2: 978 if (CheckWEPKeyIndex(passphrase, error) && 979 CheckWEPIsHex(passphrase.substr(2), error)) { 980 base::StringToInt(passphrase.substr(0, 1), &key_index_local); 981 password_text = passphrase.substr(2); 982 is_hex = true; 983 } else if (CheckWEPPrefix(passphrase, error) && 984 CheckWEPIsHex(passphrase.substr(2), error)) { 985 key_index_local = 0; 986 password_text = passphrase.substr(2); 987 is_hex = true; 988 } 989 break; 990 case IEEE_80211::kWEP40HexLen + 4: 991 case IEEE_80211::kWEP104HexLen + 4: 992 if (CheckWEPKeyIndex(passphrase, error) && 993 CheckWEPPrefix(passphrase.substr(2), error) && 994 CheckWEPIsHex(passphrase.substr(4), error)) { 995 base::StringToInt(passphrase.substr(0, 1), &key_index_local); 996 password_text = passphrase.substr(4); 997 is_hex = true; 998 } 999 break; 1000 default: 1001 error->Populate(Error::kInvalidPassphrase); 1002 break; 1003 } 1004 1005 if (error->IsSuccess()) { 1006 if (key_index) 1007 *key_index = key_index_local; 1008 if (password_bytes) { 1009 if (is_hex) 1010 base::HexStringToBytes(password_text, password_bytes); 1011 else 1012 password_bytes->insert(password_bytes->end(), 1013 password_text.begin(), 1014 password_text.end()); 1015 } 1016 } 1017 } 1018 1019 // static 1020 bool WiFiService::CheckWEPIsHex(const string& passphrase, Error* error) { 1021 vector<uint8_t> passphrase_bytes; 1022 if (base::HexStringToBytes(passphrase, &passphrase_bytes)) { 1023 return true; 1024 } else { 1025 error->Populate(Error::kInvalidPassphrase); 1026 return false; 1027 } 1028 } 1029 1030 // static 1031 bool WiFiService::CheckWEPKeyIndex(const string& passphrase, Error* error) { 1032 const auto kCaseInsensitive = base::CompareCase::INSENSITIVE_ASCII; 1033 if (base::StartsWith(passphrase, "0:", kCaseInsensitive) || 1034 base::StartsWith(passphrase, "1:", kCaseInsensitive) || 1035 base::StartsWith(passphrase, "2:", kCaseInsensitive) || 1036 base::StartsWith(passphrase, "3:", kCaseInsensitive)) { 1037 return true; 1038 } else { 1039 error->Populate(Error::kInvalidPassphrase); 1040 return false; 1041 } 1042 } 1043 1044 // static 1045 bool WiFiService::CheckWEPPrefix(const string& passphrase, Error* error) { 1046 if (base::StartsWith(passphrase, "0x", 1047 base::CompareCase::INSENSITIVE_ASCII)) { 1048 return true; 1049 } else { 1050 error->Populate(Error::kInvalidPassphrase); 1051 return false; 1052 } 1053 } 1054 1055 // static 1056 string WiFiService::ComputeSecurityClass(const string& security) { 1057 if (security == kSecurityRsn || 1058 security == kSecurityWpa) { 1059 return kSecurityPsk; 1060 } else { 1061 return security; 1062 } 1063 } 1064 1065 1066 int16_t WiFiService::SignalLevel() const { 1067 return current_endpoint_ ? current_endpoint_->signal_strength() : 1068 std::numeric_limits<int16_t>::min(); 1069 } 1070 1071 // static 1072 bool WiFiService::ParseStorageIdentifier(const string& storage_name, 1073 string* address, 1074 string* mode, 1075 string* security) { 1076 vector<string> wifi_parts = base::SplitString( 1077 storage_name, "_", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 1078 if ((wifi_parts.size() != 5 && wifi_parts.size() != 6) || 1079 wifi_parts[0] != kTypeWifi) { 1080 return false; 1081 } 1082 *address = wifi_parts[1]; 1083 *mode = wifi_parts[3]; 1084 if (wifi_parts.size() == 5) { 1085 *security = wifi_parts[4]; 1086 } else { 1087 // Account for security type "802_1x" which got split up above. 1088 *security = wifi_parts[4] + "_" + wifi_parts[5]; 1089 } 1090 return true; 1091 } 1092 1093 // static 1094 bool WiFiService::FixupServiceEntries(StoreInterface* storage) { 1095 bool fixed_entry = false; 1096 set<string> groups = storage->GetGroups(); 1097 for (const auto& id : groups) { 1098 string device_address, network_mode, security; 1099 if (!ParseStorageIdentifier(id, &device_address, 1100 &network_mode, &security)) { 1101 continue; 1102 } 1103 if (!storage->GetString(id, kStorageType, nullptr)) { 1104 storage->SetString(id, kStorageType, kTypeWifi); 1105 fixed_entry = true; 1106 } 1107 if (!storage->GetString(id, kStorageMode, nullptr)) { 1108 storage->SetString(id, kStorageMode, network_mode); 1109 fixed_entry = true; 1110 } 1111 if (!storage->GetString(id, kStorageSecurity, nullptr)) { 1112 storage->SetString(id, kStorageSecurity, security); 1113 fixed_entry = true; 1114 } 1115 if (!storage->GetString(id, kStorageSecurityClass, nullptr)) { 1116 storage->SetString(id, kStorageSecurityClass, 1117 ComputeSecurityClass(security)); 1118 fixed_entry = true; 1119 } 1120 } 1121 return fixed_entry; 1122 } 1123 1124 // static 1125 bool WiFiService::IsValidMode(const string& mode) { 1126 return mode == kModeManaged || mode == kModeAdhoc; 1127 } 1128 1129 // static 1130 bool WiFiService::IsValidSecurityMethod(const string& method) { 1131 return method == kSecurityNone || 1132 method == kSecurityWep || 1133 method == kSecurityPsk || 1134 method == kSecurityWpa || 1135 method == kSecurityRsn || 1136 method == kSecurity8021x; 1137 } 1138 1139 // static 1140 bool WiFiService::IsValidSecurityClass(const string& security_class) { 1141 return IsValidSecurityMethod(security_class) && 1142 ComputeSecurityClass(security_class) == security_class; 1143 } 1144 1145 // static 1146 uint8_t WiFiService::SignalToStrength(int16_t signal_dbm) { 1147 int16_t strength; 1148 if (signal_dbm > 0) { 1149 if (!logged_signal_warning) { 1150 LOG(WARNING) << "Signal strength is suspiciously high. " 1151 << "Assuming value " << signal_dbm << " is not in dBm."; 1152 logged_signal_warning = true; 1153 } 1154 strength = signal_dbm; 1155 } else { 1156 strength = 120 + signal_dbm; // Call -20dBm "perfect". 1157 } 1158 1159 if (strength > kStrengthMax) { 1160 strength = kStrengthMax; 1161 } else if (strength < kStrengthMin) { 1162 strength = kStrengthMin; 1163 } 1164 return strength; 1165 } 1166 1167 KeyValueStore WiFiService::GetStorageProperties() const { 1168 KeyValueStore args; 1169 args.SetString(kStorageType, kTypeWifi); 1170 args.SetString(kStorageSSID, hex_ssid_); 1171 args.SetString(kStorageMode, mode_); 1172 args.SetString(kStorageSecurityClass, ComputeSecurityClass(security_)); 1173 return args; 1174 } 1175 1176 string WiFiService::GetDefaultStorageIdentifier() const { 1177 string security = ComputeSecurityClass(security_); 1178 return base::ToLowerASCII(base::StringPrintf("%s_%s_%s_%s_%s", 1179 kTypeWifi, 1180 kAnyDeviceAddress, 1181 hex_ssid_.c_str(), 1182 mode_.c_str(), 1183 security.c_str())); 1184 } 1185 1186 string WiFiService::GetSecurity(Error* /*error*/) { 1187 if (current_endpoint_) { 1188 return current_endpoint_->security_mode(); 1189 } 1190 return security_; 1191 } 1192 1193 string WiFiService::GetSecurityClass(Error* error) { 1194 return ComputeSecurityClass(GetSecurity(error)); 1195 } 1196 1197 void WiFiService::ClearCachedCredentials() { 1198 if (wifi_) { 1199 wifi_->ClearCachedCredentials(this); 1200 } 1201 } 1202 1203 void WiFiService::OnEapCredentialsChanged( 1204 Service::UpdateCredentialsReason reason) { 1205 if (Is8021x()) { 1206 OnCredentialChange(reason); 1207 } 1208 } 1209 1210 void WiFiService::OnCredentialChange(Service::UpdateCredentialsReason reason) { 1211 ClearCachedCredentials(); 1212 // Credential changes due to a property update are new and have not 1213 // necessarily been used for a successful connection. 1214 if (reason == kReasonPropertyUpdate) 1215 SetHasEverConnected(false); 1216 UpdateConnectable(); 1217 ResetSuspectedCredentialFailures(); 1218 } 1219 1220 void WiFiService::OnProfileConfigured() { 1221 if (profile() || !hidden_ssid()) { 1222 return; 1223 } 1224 // This situation occurs when a hidden WiFi service created via GetService 1225 // has been persisted to a profile in Manager::ConfigureService(). Now 1226 // that configuration is saved, we must join the service with its profile, 1227 // which will make this SSID eligible for directed probes during scans. 1228 manager()->RegisterService(this); 1229 } 1230 1231 bool WiFiService::Is8021x() const { 1232 if (security_ == kSecurity8021x) 1233 return true; 1234 1235 // Dynamic WEP + 802.1x. 1236 if (security_ == kSecurityWep && 1237 GetEAPKeyManagement() == WPASupplicant::kKeyManagementIeee8021X) 1238 return true; 1239 return false; 1240 } 1241 1242 WiFiRefPtr WiFiService::ChooseDevice() { 1243 DeviceRefPtr device = nullptr; 1244 if (!preferred_device_.empty()) { 1245 device = manager()->GetEnabledDeviceByLinkName(preferred_device_); 1246 if (device->technology() != Technology::kWifi) { 1247 device = nullptr; 1248 } 1249 } 1250 if (!device) { 1251 device = manager()->GetEnabledDeviceWithTechnology(Technology::kWifi); 1252 } 1253 // If we have a valid device here, it's better be a WiFi device. 1254 CHECK(!device || device->technology() == Technology::kWifi) 1255 << "Unexpected device technology: " << device->technology(); 1256 return static_cast<WiFi*>(device.get()); 1257 } 1258 1259 void WiFiService::ResetWiFi() { 1260 SetWiFi(nullptr); 1261 } 1262 1263 void WiFiService::SetWiFi(const WiFiRefPtr& new_wifi) { 1264 if (wifi_ == new_wifi) { 1265 return; 1266 } 1267 ClearCachedCredentials(); 1268 if (wifi_) { 1269 wifi_->DisassociateFromService(this); 1270 } 1271 if (new_wifi) { 1272 adaptor()->EmitRpcIdentifierChanged(kDeviceProperty, 1273 new_wifi->GetRpcIdentifier()); 1274 } else { 1275 adaptor()->EmitRpcIdentifierChanged( 1276 kDeviceProperty, control_interface()->NullRPCIdentifier()); 1277 } 1278 wifi_ = new_wifi; 1279 } 1280 1281 uint16_t WiFiService::GetRoamThreshold(Error* /*error*/) { 1282 return roam_threshold_db_; 1283 } 1284 1285 bool WiFiService::SetRoamThreshold(const uint16_t& threshold, 1286 Error* /*error*/) { 1287 roam_threshold_db_ = threshold; 1288 roam_threshold_db_set_ = true; 1289 return true; 1290 } 1291 1292 void WiFiService::ClearRoamThreshold(Error* /*error*/) { 1293 roam_threshold_db_ = 0; 1294 roam_threshold_db_set_ = false; 1295 } 1296 1297 } // namespace shill 1298