network_connection_handler.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chromeos/network/network_connection_handler.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/json/json_reader.h" 10#include "chromeos/chromeos_switches.h" 11#include "chromeos/dbus/dbus_thread_manager.h" 12#include "chromeos/dbus/shill_manager_client.h" 13#include "chromeos/dbus/shill_service_client.h" 14#include "chromeos/network/client_cert_util.h" 15#include "chromeos/network/network_configuration_handler.h" 16#include "chromeos/network/network_event_log.h" 17#include "chromeos/network/network_handler_callbacks.h" 18#include "chromeos/network/network_state.h" 19#include "chromeos/network/network_state_handler.h" 20#include "chromeos/network/network_ui_data.h" 21#include "chromeos/network/shill_property_util.h" 22#include "dbus/object_path.h" 23#include "net/cert/x509_certificate.h" 24#include "third_party/cros_system_api/dbus/service_constants.h" 25 26namespace chromeos { 27 28namespace { 29 30void InvokeErrorCallback(const std::string& service_path, 31 const network_handler::ErrorCallback& error_callback, 32 const std::string& error_name) { 33 std::string error_msg = "Connect Error: " + error_name; 34 NET_LOG_ERROR(error_msg, service_path); 35 if (error_callback.is_null()) 36 return; 37 scoped_ptr<base::DictionaryValue> error_data( 38 network_handler::CreateErrorData(service_path, error_name, error_msg)); 39 error_callback.Run(error_name, error_data.Pass()); 40} 41 42bool IsAuthenticationError(const std::string& error) { 43 return (error == flimflam::kErrorBadWEPKey || 44 error == flimflam::kErrorPppAuthFailed || 45 error == shill::kErrorEapLocalTlsFailed || 46 error == shill::kErrorEapRemoteTlsFailed || 47 error == shill::kErrorEapAuthenticationFailed); 48} 49 50bool VPNIsConfigured(const std::string& service_path, 51 const std::string& provider_type, 52 const base::DictionaryValue& provider_properties) { 53 if (provider_type == flimflam::kProviderOpenVpn) { 54 std::string hostname; 55 provider_properties.GetStringWithoutPathExpansion( 56 flimflam::kHostProperty, &hostname); 57 if (hostname.empty()) { 58 NET_LOG_EVENT("OpenVPN: No hostname", service_path); 59 return false; 60 } 61 std::string username; 62 provider_properties.GetStringWithoutPathExpansion( 63 flimflam::kOpenVPNUserProperty, &username); 64 if (username.empty()) { 65 NET_LOG_EVENT("OpenVPN: No username", service_path); 66 return false; 67 } 68 bool passphrase_required = false; 69 provider_properties.GetBooleanWithoutPathExpansion( 70 flimflam::kPassphraseRequiredProperty, &passphrase_required); 71 if (passphrase_required) { 72 NET_LOG_EVENT("OpenVPN: Passphrase Required", service_path); 73 return false; 74 } 75 NET_LOG_EVENT("OpenVPN Is Configured", service_path); 76 } else { 77 bool passphrase_required = false; 78 std::string passphrase; 79 provider_properties.GetBooleanWithoutPathExpansion( 80 flimflam::kL2tpIpsecPskRequiredProperty, &passphrase_required); 81 if (passphrase_required) { 82 NET_LOG_EVENT("VPN: PSK Required", service_path); 83 return false; 84 } 85 NET_LOG_EVENT("VPN Is Configured", service_path); 86 } 87 return true; 88} 89 90} // namespace 91 92const char NetworkConnectionHandler::kErrorNotFound[] = "not-found"; 93const char NetworkConnectionHandler::kErrorConnected[] = "connected"; 94const char NetworkConnectionHandler::kErrorConnecting[] = "connecting"; 95const char NetworkConnectionHandler::kErrorNotConnected[] = "not-connected"; 96const char NetworkConnectionHandler::kErrorPassphraseRequired[] = 97 "passphrase-required"; 98const char NetworkConnectionHandler::kErrorActivationRequired[] = 99 "activation-required"; 100const char NetworkConnectionHandler::kErrorCertificateRequired[] = 101 "certificate-required"; 102const char NetworkConnectionHandler::kErrorConfigurationRequired[] = 103 "configuration-required"; 104const char NetworkConnectionHandler::kErrorAuthenticationRequired[] = 105 "authentication-required"; 106const char NetworkConnectionHandler::kErrorShillError[] = "shill-error"; 107const char NetworkConnectionHandler::kErrorConfigureFailed[] = 108 "configure-failed"; 109const char NetworkConnectionHandler::kErrorConnectCanceled[] = 110 "connect-canceled"; 111 112struct NetworkConnectionHandler::ConnectRequest { 113 ConnectRequest(const std::string& service_path, 114 const base::Closure& success, 115 const network_handler::ErrorCallback& error) 116 : service_path(service_path), 117 connect_state(CONNECT_REQUESTED), 118 success_callback(success), 119 error_callback(error) { 120 } 121 enum ConnectState { 122 CONNECT_REQUESTED = 0, 123 CONNECT_STARTED = 1, 124 CONNECT_CONNECTING = 2 125 }; 126 std::string service_path; 127 ConnectState connect_state; 128 base::Closure success_callback; 129 network_handler::ErrorCallback error_callback; 130}; 131 132NetworkConnectionHandler::NetworkConnectionHandler() 133 : cert_loader_(NULL), 134 network_state_handler_(NULL), 135 network_configuration_handler_(NULL), 136 logged_in_(false), 137 certificates_loaded_(false) { 138} 139 140NetworkConnectionHandler::~NetworkConnectionHandler() { 141 if (network_state_handler_) 142 network_state_handler_->RemoveObserver(this, FROM_HERE); 143 if (cert_loader_) 144 cert_loader_->RemoveObserver(this); 145 if (LoginState::IsInitialized()) 146 LoginState::Get()->RemoveObserver(this); 147} 148 149void NetworkConnectionHandler::Init( 150 NetworkStateHandler* network_state_handler, 151 NetworkConfigurationHandler* network_configuration_handler) { 152 if (LoginState::IsInitialized()) { 153 LoginState::Get()->AddObserver(this); 154 logged_in_ = LoginState::Get()->IsUserLoggedIn(); 155 } 156 if (CertLoader::IsInitialized()) { 157 cert_loader_ = CertLoader::Get(); 158 cert_loader_->AddObserver(this); 159 certificates_loaded_ = cert_loader_->certificates_loaded(); 160 } else { 161 // TODO(stevenjb): Require a mock or stub cert_loader in tests. 162 certificates_loaded_ = true; 163 } 164 if (network_state_handler) { 165 network_state_handler_ = network_state_handler; 166 network_state_handler_->AddObserver(this, FROM_HERE); 167 } 168 network_configuration_handler_ = network_configuration_handler; 169} 170 171void NetworkConnectionHandler::LoggedInStateChanged() { 172 if (LoginState::Get()->IsUserLoggedIn()) { 173 logged_in_ = true; 174 NET_LOG_EVENT("Logged In", ""); 175 } 176} 177 178void NetworkConnectionHandler::OnCertificatesLoaded( 179 const net::CertificateList& cert_list, 180 bool initial_load) { 181 certificates_loaded_ = true; 182 NET_LOG_EVENT("Certificates Loaded", ""); 183 if (queued_connect_) { 184 NET_LOG_EVENT("Connecting to Queued Network", 185 queued_connect_->service_path); 186 ConnectToNetwork(queued_connect_->service_path, 187 queued_connect_->success_callback, 188 queued_connect_->error_callback, 189 false /* check_error_state */); 190 } else if (initial_load) { 191 // Once certificates have loaded, connect to the "best" available network. 192 network_state_handler_->ConnectToBestWifiNetwork(); 193 } 194} 195 196void NetworkConnectionHandler::ConnectToNetwork( 197 const std::string& service_path, 198 const base::Closure& success_callback, 199 const network_handler::ErrorCallback& error_callback, 200 bool check_error_state) { 201 NET_LOG_USER("ConnectToNetwork", service_path); 202 // Clear any existing queued connect request. 203 queued_connect_.reset(); 204 if (HasConnectingNetwork(service_path)) { 205 NET_LOG_USER("Connect Request While Pending", service_path); 206 InvokeErrorCallback(service_path, error_callback, kErrorConnecting); 207 return; 208 } 209 210 // Check cached network state for connected, connecting, or unactivated 211 // networks. These states will not be affected by a recent configuration. 212 // Note: NetworkState may not exist for a network that was recently 213 // configured, in which case these checks do not apply anyway. 214 const NetworkState* network = 215 network_state_handler_->GetNetworkState(service_path); 216 217 if (network) { 218 // For existing networks, perform some immediate consistency checks. 219 if (network->IsConnectedState()) { 220 InvokeErrorCallback(service_path, error_callback, kErrorConnected); 221 return; 222 } 223 if (network->IsConnectingState()) { 224 InvokeErrorCallback(service_path, error_callback, kErrorConnecting); 225 return; 226 } 227 if (network->RequiresActivation()) { 228 InvokeErrorCallback(service_path, error_callback, 229 kErrorActivationRequired); 230 return; 231 } 232 233 if (check_error_state) { 234 const std::string& error = network->error(); 235 if (error == flimflam::kErrorBadPassphrase) { 236 InvokeErrorCallback(service_path, error_callback, error); 237 return; 238 } 239 if (IsAuthenticationError(error)) { 240 InvokeErrorCallback( 241 service_path, error_callback, kErrorAuthenticationRequired); 242 return; 243 } 244 } 245 } 246 247 // All synchronous checks passed, add |service_path| to connecting list. 248 pending_requests_.insert(std::make_pair( 249 service_path, 250 ConnectRequest(service_path, success_callback, error_callback))); 251 252 // Connect immediately to 'connectable' networks. 253 // TODO(stevenjb): Shill needs to properly set Connectable for VPN. 254 if (network && 255 network->connectable() && network->type() != flimflam::kTypeVPN) { 256 CallShillConnect(service_path); 257 return; 258 } 259 260 // Request additional properties to check. VerifyConfiguredAndConnect will 261 // use only these properties, not cached properties, to ensure that they 262 // are up to date after any recent configuration. 263 network_configuration_handler_->GetProperties( 264 service_path, 265 base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect, 266 AsWeakPtr(), check_error_state), 267 base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure, 268 AsWeakPtr(), service_path)); 269} 270 271void NetworkConnectionHandler::DisconnectNetwork( 272 const std::string& service_path, 273 const base::Closure& success_callback, 274 const network_handler::ErrorCallback& error_callback) { 275 NET_LOG_USER("DisconnectNetwork", service_path); 276 const NetworkState* network = 277 network_state_handler_->GetNetworkState(service_path); 278 if (!network) { 279 InvokeErrorCallback(service_path, error_callback, kErrorNotFound); 280 return; 281 } 282 if (!network->IsConnectedState()) { 283 InvokeErrorCallback(service_path, error_callback, kErrorNotConnected); 284 return; 285 } 286 CallShillDisconnect(service_path, success_callback, error_callback); 287} 288 289bool NetworkConnectionHandler::HasConnectingNetwork( 290 const std::string& service_path) { 291 return pending_requests_.count(service_path) != 0; 292} 293 294bool NetworkConnectionHandler::HasPendingConnectRequest() { 295 return pending_requests_.size() > 0; 296} 297 298void NetworkConnectionHandler::NetworkListChanged() { 299 CheckAllPendingRequests(); 300} 301 302void NetworkConnectionHandler::NetworkPropertiesUpdated( 303 const NetworkState* network) { 304 if (HasConnectingNetwork(network->path())) 305 CheckPendingRequest(network->path()); 306} 307 308NetworkConnectionHandler::ConnectRequest* 309NetworkConnectionHandler::GetPendingRequest(const std::string& service_path) { 310 std::map<std::string, ConnectRequest>::iterator iter = 311 pending_requests_.find(service_path); 312 return iter != pending_requests_.end() ? &(iter->second) : NULL; 313} 314 315// ConnectToNetwork implementation 316 317void NetworkConnectionHandler::VerifyConfiguredAndConnect( 318 bool check_error_state, 319 const std::string& service_path, 320 const base::DictionaryValue& service_properties) { 321 NET_LOG_EVENT("VerifyConfiguredAndConnect", service_path); 322 323 // If 'passphrase_required' is still true, then the 'Passphrase' property 324 // has not been set to a minimum length value. 325 bool passphrase_required = false; 326 service_properties.GetBooleanWithoutPathExpansion( 327 flimflam::kPassphraseRequiredProperty, &passphrase_required); 328 if (passphrase_required) { 329 ErrorCallbackForPendingRequest(service_path, kErrorPassphraseRequired); 330 return; 331 } 332 333 std::string type, security; 334 service_properties.GetStringWithoutPathExpansion( 335 flimflam::kTypeProperty, &type); 336 service_properties.GetStringWithoutPathExpansion( 337 flimflam::kSecurityProperty, &security); 338 bool connectable = false; 339 service_properties.GetBooleanWithoutPathExpansion( 340 flimflam::kConnectableProperty, &connectable); 341 342 // In case NetworkState was not available in ConnectToNetwork (e.g. it had 343 // been recently configured), we need to check Connectable again. 344 if (connectable && type != flimflam::kTypeVPN) { 345 // TODO(stevenjb): Shill needs to properly set Connectable for VPN. 346 CallShillConnect(service_path); 347 return; 348 } 349 350 // Get VPN provider type and host (required for configuration) and ensure 351 // that required VPN non-cert properties are set. 352 std::string vpn_provider_type, vpn_provider_host; 353 if (type == flimflam::kTypeVPN) { 354 // VPN Provider values are read from the "Provider" dictionary, not the 355 // "Provider.Type", etc keys (which are used only to set the values). 356 const base::DictionaryValue* provider_properties; 357 if (service_properties.GetDictionaryWithoutPathExpansion( 358 flimflam::kProviderProperty, &provider_properties)) { 359 provider_properties->GetStringWithoutPathExpansion( 360 flimflam::kTypeProperty, &vpn_provider_type); 361 provider_properties->GetStringWithoutPathExpansion( 362 flimflam::kHostProperty, &vpn_provider_host); 363 } 364 if (vpn_provider_type.empty() || vpn_provider_host.empty()) { 365 ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); 366 return; 367 } 368 // VPN requires a host and username to be set. 369 if (!VPNIsConfigured( 370 service_path, vpn_provider_type, *provider_properties)) { 371 NET_LOG_ERROR("VPN Not Configured", service_path); 372 ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); 373 return; 374 } 375 } 376 377 client_cert::ConfigType client_cert_type = client_cert::CONFIG_TYPE_NONE; 378 if (type == flimflam::kTypeVPN) { 379 if (vpn_provider_type == flimflam::kProviderOpenVpn) 380 client_cert_type = client_cert::CONFIG_TYPE_OPENVPN; 381 else 382 client_cert_type = client_cert::CONFIG_TYPE_IPSEC; 383 } else if (type == flimflam::kTypeWifi && 384 security == flimflam::kSecurity8021x) { 385 client_cert_type = client_cert::CONFIG_TYPE_EAP; 386 } 387 388 base::DictionaryValue config_properties; 389 if (client_cert_type != client_cert::CONFIG_TYPE_NONE) { 390 // If the client certificate must be configured, this will be set to a 391 // non-empty string. 392 std::string pkcs11_id; 393 394 // Check certificate properties in kUIDataProperty if configured. 395 // Note: Wifi/VPNConfigView set these properties explicitly, in which case 396 // only the TPM must be configured. 397 scoped_ptr<NetworkUIData> ui_data = 398 shill_property_util::GetUIDataFromProperties(service_properties); 399 if (ui_data && ui_data->certificate_type() == CLIENT_CERT_TYPE_PATTERN) { 400 // User must be logged in to connect to a network requiring a certificate. 401 if (!logged_in_ || !cert_loader_) { 402 ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired); 403 return; 404 } 405 406 // If certificates have not been loaded yet, queue the connect request. 407 if (!certificates_loaded_) { 408 ConnectRequest* request = GetPendingRequest(service_path); 409 if (!request) { 410 NET_LOG_ERROR("No pending request to queue", service_path); 411 return; 412 } 413 NET_LOG_EVENT("Connect Request Queued", service_path); 414 queued_connect_.reset(new ConnectRequest( 415 service_path, request->success_callback, request->error_callback)); 416 pending_requests_.erase(service_path); 417 return; 418 } 419 420 pkcs11_id = CertificateIsConfigured(ui_data.get()); 421 // Ensure the certificate is available and configured. 422 if (!cert_loader_->IsHardwareBacked() || pkcs11_id.empty()) { 423 ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired); 424 return; 425 } 426 } else if (check_error_state && 427 !client_cert::IsCertificateConfigured(client_cert_type, 428 service_properties)) { 429 // Network may not be configured. 430 ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); 431 return; 432 } 433 434 // The network may not be 'Connectable' because the TPM properties are not 435 // set up, so configure tpm slot/pin before connecting. 436 if (cert_loader_ && cert_loader_->IsHardwareBacked()) { 437 // Pass NULL if pkcs11_id is empty, so that it doesn't clear any 438 // previously configured client cert. 439 client_cert::SetShillProperties(client_cert_type, 440 cert_loader_->tpm_token_slot(), 441 cert_loader_->tpm_user_pin(), 442 pkcs11_id.empty() ? NULL : &pkcs11_id, 443 &config_properties); 444 } 445 } 446 447 if (!config_properties.empty()) { 448 NET_LOG_EVENT("Configuring Network", service_path); 449 network_configuration_handler_->SetProperties( 450 service_path, 451 config_properties, 452 base::Bind(&NetworkConnectionHandler::CallShillConnect, 453 AsWeakPtr(), 454 service_path), 455 base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure, 456 AsWeakPtr(), 457 service_path)); 458 return; 459 } 460 461 // Otherwise, we probably still need to configure the network since 462 // 'Connectable' is false. If |check_error_state| is true, signal an 463 // error, otherwise attempt to connect to possibly gain additional error 464 // state from Shill (or in case 'Connectable' is improperly unset). 465 if (check_error_state) 466 ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); 467 else 468 CallShillConnect(service_path); 469} 470 471void NetworkConnectionHandler::CallShillConnect( 472 const std::string& service_path) { 473 NET_LOG_EVENT("Sending Connect Request to Shill", service_path); 474 DBusThreadManager::Get()->GetShillServiceClient()->Connect( 475 dbus::ObjectPath(service_path), 476 base::Bind(&NetworkConnectionHandler::HandleShillConnectSuccess, 477 AsWeakPtr(), service_path), 478 base::Bind(&NetworkConnectionHandler::HandleShillConnectFailure, 479 AsWeakPtr(), service_path)); 480} 481 482void NetworkConnectionHandler::HandleConfigurationFailure( 483 const std::string& service_path, 484 const std::string& error_name, 485 scoped_ptr<base::DictionaryValue> error_data) { 486 ConnectRequest* request = GetPendingRequest(service_path); 487 if (!request) { 488 NET_LOG_ERROR("HandleConfigurationFailure called with no pending request.", 489 service_path); 490 return; 491 } 492 network_handler::ErrorCallback error_callback = request->error_callback; 493 pending_requests_.erase(service_path); 494 if (!error_callback.is_null()) 495 error_callback.Run(kErrorConfigureFailed, error_data.Pass()); 496} 497 498void NetworkConnectionHandler::HandleShillConnectSuccess( 499 const std::string& service_path) { 500 ConnectRequest* request = GetPendingRequest(service_path); 501 if (!request) { 502 NET_LOG_ERROR("HandleShillConnectSuccess called with no pending request.", 503 service_path); 504 return; 505 } 506 request->connect_state = ConnectRequest::CONNECT_STARTED; 507 NET_LOG_EVENT("Connect Request Acknowledged", service_path); 508 // Do not call success_callback here, wait for one of the following 509 // conditions: 510 // * State transitions to a non connecting state indicating succes or failure 511 // * Network is no longer in the visible list, indicating failure 512 CheckPendingRequest(service_path); 513} 514 515void NetworkConnectionHandler::HandleShillConnectFailure( 516 const std::string& service_path, 517 const std::string& dbus_error_name, 518 const std::string& dbus_error_message) { 519 ConnectRequest* request = GetPendingRequest(service_path); 520 if (!request) { 521 NET_LOG_ERROR("HandleShillConnectFailure called with no pending request.", 522 service_path); 523 return; 524 } 525 network_handler::ErrorCallback error_callback = request->error_callback; 526 pending_requests_.erase(service_path); 527 network_handler::ShillErrorCallbackFunction( 528 flimflam::kErrorConnectFailed, service_path, error_callback, 529 dbus_error_name, dbus_error_message); 530} 531 532void NetworkConnectionHandler::CheckPendingRequest( 533 const std::string service_path) { 534 ConnectRequest* request = GetPendingRequest(service_path); 535 DCHECK(request); 536 if (request->connect_state == ConnectRequest::CONNECT_REQUESTED) 537 return; // Request has not started, ignore update 538 const NetworkState* network = 539 network_state_handler_->GetNetworkState(service_path); 540 if (!network) 541 return; // NetworkState may not be be updated yet. 542 543 if (network->IsConnectingState()) { 544 request->connect_state = ConnectRequest::CONNECT_CONNECTING; 545 return; 546 } 547 if (network->IsConnectedState()) { 548 NET_LOG_EVENT("Connect Request Succeeded", service_path); 549 if (!request->success_callback.is_null()) 550 request->success_callback.Run(); 551 pending_requests_.erase(service_path); 552 return; 553 } 554 if (network->connection_state() == flimflam::kStateIdle && 555 request->connect_state != ConnectRequest::CONNECT_CONNECTING) { 556 // Connection hasn't started yet, keep waiting. 557 return; 558 } 559 560 // Network is neither connecting or connected; an error occurred. 561 std::string error_name, error_detail; 562 if (network->connection_state() == flimflam::kStateIdle && 563 pending_requests_.size() > 1) { 564 // Another connect request canceled this one. 565 error_name = kErrorConnectCanceled; 566 error_detail = ""; 567 } else { 568 error_name = flimflam::kErrorConnectFailed; 569 error_detail = network->error(); 570 if (error_detail.empty()) { 571 if (network->connection_state() == flimflam::kStateFailure) 572 error_detail = flimflam::kUnknownString; 573 else 574 error_detail = "Unexpected State: " + network->connection_state(); 575 } 576 } 577 std::string error_msg = error_name + ": " + error_detail; 578 NET_LOG_ERROR(error_msg, service_path); 579 580 network_handler::ErrorCallback error_callback = request->error_callback; 581 pending_requests_.erase(service_path); 582 if (error_callback.is_null()) 583 return; 584 scoped_ptr<base::DictionaryValue> error_data( 585 network_handler::CreateErrorData(service_path, error_name, error_detail)); 586 error_callback.Run(error_name, error_data.Pass()); 587} 588 589void NetworkConnectionHandler::CheckAllPendingRequests() { 590 for (std::map<std::string, ConnectRequest>::iterator iter = 591 pending_requests_.begin(); iter != pending_requests_.end(); ++iter) { 592 CheckPendingRequest(iter->first); 593 } 594} 595 596std::string NetworkConnectionHandler::CertificateIsConfigured( 597 NetworkUIData* ui_data) { 598 if (ui_data->certificate_pattern().Empty()) 599 return std::string(); 600 // Find the matching certificate. 601 scoped_refptr<net::X509Certificate> matching_cert = 602 client_cert::GetCertificateMatch(ui_data->certificate_pattern()); 603 if (!matching_cert.get()) 604 return std::string(); 605 return CertLoader::GetPkcs11IdForCert(*matching_cert.get()); 606} 607 608void NetworkConnectionHandler::ErrorCallbackForPendingRequest( 609 const std::string& service_path, 610 const std::string& error_name) { 611 ConnectRequest* request = GetPendingRequest(service_path); 612 if (!request) { 613 NET_LOG_ERROR("ErrorCallbackForPendingRequest with no pending request.", 614 service_path); 615 return; 616 } 617 // Remove the entry before invoking the callback in case it triggers a retry. 618 network_handler::ErrorCallback error_callback = request->error_callback; 619 pending_requests_.erase(service_path); 620 InvokeErrorCallback(service_path, error_callback, error_name); 621} 622 623// Disconnect 624 625void NetworkConnectionHandler::CallShillDisconnect( 626 const std::string& service_path, 627 const base::Closure& success_callback, 628 const network_handler::ErrorCallback& error_callback) { 629 NET_LOG_USER("Disconnect Request", service_path); 630 DBusThreadManager::Get()->GetShillServiceClient()->Disconnect( 631 dbus::ObjectPath(service_path), 632 base::Bind(&NetworkConnectionHandler::HandleShillDisconnectSuccess, 633 AsWeakPtr(), service_path, success_callback), 634 base::Bind(&network_handler::ShillErrorCallbackFunction, 635 kErrorShillError, service_path, error_callback)); 636} 637 638void NetworkConnectionHandler::HandleShillDisconnectSuccess( 639 const std::string& service_path, 640 const base::Closure& success_callback) { 641 NET_LOG_EVENT("Disconnect Request Sent", service_path); 642 if (!success_callback.is_null()) 643 success_callback.Run(); 644} 645 646} // namespace chromeos 647