// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/network/network_connection_handler.h" #include "base/bind.h" #include "base/json/json_reader.h" #include "base/location.h" #include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_number_conversions.h" #include "chromeos/cert_loader.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_manager_client.h" #include "chromeos/dbus/shill_service_client.h" #include "chromeos/network/certificate_pattern.h" #include "chromeos/network/client_cert_resolver.h" #include "chromeos/network/client_cert_util.h" #include "chromeos/network/managed_network_configuration_handler.h" #include "chromeos/network/network_configuration_handler.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_profile_handler.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/network/shill_property_util.h" #include "dbus/object_path.h" #include "net/cert/x509_certificate.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { void InvokeErrorCallback(const std::string& service_path, const network_handler::ErrorCallback& error_callback, const std::string& error_name) { NET_LOG_ERROR("Connect Error: " + error_name, service_path); network_handler::RunErrorCallback( error_callback, service_path, error_name, ""); } bool IsAuthenticationError(const std::string& error) { return (error == shill::kErrorBadWEPKey || error == shill::kErrorPppAuthFailed || error == shill::kErrorEapLocalTlsFailed || error == shill::kErrorEapRemoteTlsFailed || error == shill::kErrorEapAuthenticationFailed); } bool VPNRequiresCredentials(const std::string& service_path, const std::string& provider_type, const base::DictionaryValue& provider_properties) { if (provider_type == shill::kProviderOpenVpn) { std::string username; provider_properties.GetStringWithoutPathExpansion( shill::kOpenVPNUserProperty, &username); if (username.empty()) { NET_LOG_EVENT("OpenVPN: No username", service_path); return true; } bool passphrase_required = false; provider_properties.GetBooleanWithoutPathExpansion( shill::kPassphraseRequiredProperty, &passphrase_required); if (passphrase_required) { NET_LOG_EVENT("OpenVPN: Passphrase Required", service_path); return true; } NET_LOG_EVENT("OpenVPN Is Configured", service_path); } else { bool passphrase_required = false; provider_properties.GetBooleanWithoutPathExpansion( shill::kL2tpIpsecPskRequiredProperty, &passphrase_required); if (passphrase_required) { NET_LOG_EVENT("VPN: PSK Required", service_path); return true; } provider_properties.GetBooleanWithoutPathExpansion( shill::kPassphraseRequiredProperty, &passphrase_required); if (passphrase_required) { NET_LOG_EVENT("VPN: Passphrase Required", service_path); return true; } NET_LOG_EVENT("VPN Is Configured", service_path); } return false; } std::string GetDefaultUserProfilePath(const NetworkState* network) { if (!NetworkHandler::IsInitialized() || (LoginState::IsInitialized() && !LoginState::Get()->UserHasNetworkProfile()) || (network && network->type() == shill::kTypeWifi && network->security() == shill::kSecurityNone)) { return NetworkProfileHandler::GetSharedProfilePath(); } const NetworkProfile* profile = NetworkHandler::Get()->network_profile_handler()->GetDefaultUserProfile(); return profile ? profile->path : NetworkProfileHandler::GetSharedProfilePath(); } } // namespace const char NetworkConnectionHandler::kErrorNotFound[] = "not-found"; const char NetworkConnectionHandler::kErrorConnected[] = "connected"; const char NetworkConnectionHandler::kErrorConnecting[] = "connecting"; const char NetworkConnectionHandler::kErrorNotConnected[] = "not-connected"; const char NetworkConnectionHandler::kErrorPassphraseRequired[] = "passphrase-required"; const char NetworkConnectionHandler::kErrorActivationRequired[] = "activation-required"; const char NetworkConnectionHandler::kErrorCertificateRequired[] = "certificate-required"; const char NetworkConnectionHandler::kErrorConfigurationRequired[] = "configuration-required"; const char NetworkConnectionHandler::kErrorAuthenticationRequired[] = "authentication-required"; const char NetworkConnectionHandler::kErrorShillError[] = "shill-error"; const char NetworkConnectionHandler::kErrorConfigureFailed[] = "configure-failed"; const char NetworkConnectionHandler::kErrorConnectCanceled[] = "connect-canceled"; const char NetworkConnectionHandler::kErrorCertLoadTimeout[] = "cert-load-timeout"; struct NetworkConnectionHandler::ConnectRequest { ConnectRequest(const std::string& service_path, const std::string& profile_path, const base::Closure& success, const network_handler::ErrorCallback& error) : service_path(service_path), profile_path(profile_path), connect_state(CONNECT_REQUESTED), success_callback(success), error_callback(error) { } enum ConnectState { CONNECT_REQUESTED = 0, CONNECT_STARTED = 1, CONNECT_CONNECTING = 2 }; std::string service_path; std::string profile_path; ConnectState connect_state; base::Closure success_callback; network_handler::ErrorCallback error_callback; }; NetworkConnectionHandler::NetworkConnectionHandler() : cert_loader_(NULL), network_state_handler_(NULL), configuration_handler_(NULL), logged_in_(false), certificates_loaded_(false) { } NetworkConnectionHandler::~NetworkConnectionHandler() { if (network_state_handler_) network_state_handler_->RemoveObserver(this, FROM_HERE); if (cert_loader_) cert_loader_->RemoveObserver(this); if (LoginState::IsInitialized()) LoginState::Get()->RemoveObserver(this); } void NetworkConnectionHandler::Init( NetworkStateHandler* network_state_handler, NetworkConfigurationHandler* network_configuration_handler, ManagedNetworkConfigurationHandler* managed_network_configuration_handler) { if (LoginState::IsInitialized()) LoginState::Get()->AddObserver(this); if (CertLoader::IsInitialized()) { cert_loader_ = CertLoader::Get(); cert_loader_->AddObserver(this); if (cert_loader_->certificates_loaded()) { NET_LOG_EVENT("Certificates Loaded", ""); certificates_loaded_ = true; } } else { // TODO(tbarzic): Require a mock or stub cert_loader in tests. NET_LOG_EVENT("Certificate Loader not initialized", ""); certificates_loaded_ = true; } if (network_state_handler) { network_state_handler_ = network_state_handler; network_state_handler_->AddObserver(this, FROM_HERE); } configuration_handler_ = network_configuration_handler; managed_configuration_handler_ = managed_network_configuration_handler; // After this point, the NetworkConnectionHandler is fully initialized (all // handler references set, observers registered, ...). if (LoginState::IsInitialized()) LoggedInStateChanged(); } void NetworkConnectionHandler::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void NetworkConnectionHandler::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } void NetworkConnectionHandler::LoggedInStateChanged() { LoginState* login_state = LoginState::Get(); if (logged_in_ || !login_state->IsUserLoggedIn()) return; logged_in_ = true; logged_in_time_ = base::TimeTicks::Now(); } void NetworkConnectionHandler::OnCertificatesLoaded( const net::CertificateList& cert_list, bool initial_load) { certificates_loaded_ = true; NET_LOG_EVENT("Certificates Loaded", ""); if (queued_connect_) ConnectToQueuedNetwork(); } void NetworkConnectionHandler::ConnectToNetwork( const std::string& service_path, const base::Closure& success_callback, const network_handler::ErrorCallback& error_callback, bool check_error_state) { NET_LOG_USER("ConnectToNetwork", service_path); FOR_EACH_OBSERVER(Observer, observers_, ConnectToNetworkRequested(service_path)); // Clear any existing queued connect request. queued_connect_.reset(); if (HasConnectingNetwork(service_path)) { NET_LOG_USER("Connect Request While Pending", service_path); InvokeErrorCallback(service_path, error_callback, kErrorConnecting); return; } // Check cached network state for connected, connecting, or unactivated // networks. These states will not be affected by a recent configuration. // Note: NetworkState may not exist for a network that was recently // configured, in which case these checks do not apply anyway. const NetworkState* network = network_state_handler_->GetNetworkState(service_path); if (network) { // For existing networks, perform some immediate consistency checks. if (network->IsConnectedState()) { InvokeErrorCallback(service_path, error_callback, kErrorConnected); return; } if (network->IsConnectingState()) { InvokeErrorCallback(service_path, error_callback, kErrorConnecting); return; } if (check_error_state) { const std::string& error = network->last_error(); if (error == shill::kErrorBadPassphrase) { InvokeErrorCallback(service_path, error_callback, error); return; } if (IsAuthenticationError(error)) { InvokeErrorCallback( service_path, error_callback, kErrorAuthenticationRequired); return; } } } // If the network does not have a profile path, specify the correct default // profile here and set it once connected. Otherwise leave it empty to // indicate that it does not need to be set. std::string profile_path; if (!network || network->profile_path().empty()) profile_path = GetDefaultUserProfilePath(network); // All synchronous checks passed, add |service_path| to connecting list. pending_requests_.insert(std::make_pair( service_path, ConnectRequest(service_path, profile_path, success_callback, error_callback))); // Connect immediately to 'connectable' networks. // TODO(stevenjb): Shill needs to properly set Connectable for VPN. if (network && network->connectable() && network->type() != shill::kTypeVPN) { CallShillConnect(service_path); return; } // Request additional properties to check. VerifyConfiguredAndConnect will // use only these properties, not cached properties, to ensure that they // are up to date after any recent configuration. configuration_handler_->GetProperties( service_path, base::Bind(&NetworkConnectionHandler::VerifyConfiguredAndConnect, AsWeakPtr(), check_error_state), base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure, AsWeakPtr(), service_path)); } void NetworkConnectionHandler::DisconnectNetwork( const std::string& service_path, const base::Closure& success_callback, const network_handler::ErrorCallback& error_callback) { NET_LOG_USER("DisconnectNetwork", service_path); const NetworkState* network = network_state_handler_->GetNetworkState(service_path); if (!network) { InvokeErrorCallback(service_path, error_callback, kErrorNotFound); return; } if (!network->IsConnectedState() && !network->IsConnectingState()) { InvokeErrorCallback(service_path, error_callback, kErrorNotConnected); return; } pending_requests_.erase(service_path); CallShillDisconnect(service_path, success_callback, error_callback); } bool NetworkConnectionHandler::HasConnectingNetwork( const std::string& service_path) { return pending_requests_.count(service_path) != 0; } bool NetworkConnectionHandler::HasPendingConnectRequest() { return pending_requests_.size() > 0; } void NetworkConnectionHandler::NetworkListChanged() { CheckAllPendingRequests(); } void NetworkConnectionHandler::NetworkPropertiesUpdated( const NetworkState* network) { if (HasConnectingNetwork(network->path())) CheckPendingRequest(network->path()); } NetworkConnectionHandler::ConnectRequest* NetworkConnectionHandler::GetPendingRequest(const std::string& service_path) { std::map::iterator iter = pending_requests_.find(service_path); return iter != pending_requests_.end() ? &(iter->second) : NULL; } // ConnectToNetwork implementation void NetworkConnectionHandler::VerifyConfiguredAndConnect( bool check_error_state, const std::string& service_path, const base::DictionaryValue& service_properties) { NET_LOG_EVENT("VerifyConfiguredAndConnect", service_path); // If 'passphrase_required' is still true, then the 'Passphrase' property // has not been set to a minimum length value. bool passphrase_required = false; service_properties.GetBooleanWithoutPathExpansion( shill::kPassphraseRequiredProperty, &passphrase_required); if (passphrase_required) { ErrorCallbackForPendingRequest(service_path, kErrorPassphraseRequired); return; } std::string type, security; service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); service_properties.GetStringWithoutPathExpansion( shill::kSecurityProperty, &security); bool connectable = false; service_properties.GetBooleanWithoutPathExpansion( shill::kConnectableProperty, &connectable); // In case NetworkState was not available in ConnectToNetwork (e.g. it had // been recently configured), we need to check Connectable again. if (connectable && type != shill::kTypeVPN) { // TODO(stevenjb): Shill needs to properly set Connectable for VPN. CallShillConnect(service_path); return; } // Get VPN provider type and host (required for configuration) and ensure // that required VPN non-cert properties are set. const base::DictionaryValue* provider_properties = NULL; std::string vpn_provider_type, vpn_provider_host, vpn_client_cert_id; if (type == shill::kTypeVPN) { // VPN Provider values are read from the "Provider" dictionary, not the // "Provider.Type", etc keys (which are used only to set the values). if (service_properties.GetDictionaryWithoutPathExpansion( shill::kProviderProperty, &provider_properties)) { provider_properties->GetStringWithoutPathExpansion( shill::kTypeProperty, &vpn_provider_type); provider_properties->GetStringWithoutPathExpansion( shill::kHostProperty, &vpn_provider_host); provider_properties->GetStringWithoutPathExpansion( shill::kL2tpIpsecClientCertIdProperty, &vpn_client_cert_id); } if (vpn_provider_type.empty() || vpn_provider_host.empty()) { ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); return; } } std::string guid; service_properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &guid); std::string profile; service_properties.GetStringWithoutPathExpansion(shill::kProfileProperty, &profile); const base::DictionaryValue* user_policy = managed_configuration_handler_->FindPolicyByGuidAndProfile(guid, profile); client_cert::ClientCertConfig cert_config_from_policy; if (user_policy) client_cert::OncToClientCertConfig(*user_policy, &cert_config_from_policy); client_cert::ConfigType client_cert_type = client_cert::CONFIG_TYPE_NONE; if (type == shill::kTypeVPN) { if (vpn_provider_type == shill::kProviderOpenVpn) { client_cert_type = client_cert::CONFIG_TYPE_OPENVPN; } else { // L2TP/IPSec only requires a certificate if one is specified in ONC // or one was configured by the UI. Otherwise it is L2TP/IPSec with // PSK and doesn't require a certificate. // // TODO(benchan): Modify shill to specify the authentication type via // the kL2tpIpsecAuthenticationType property, so that Chrome doesn't need // to deduce the authentication type based on the // kL2tpIpsecClientCertIdProperty here (and also in VPNConfigView). if (!vpn_client_cert_id.empty() || cert_config_from_policy.client_cert_type != onc::client_cert::kClientCertTypeNone) { client_cert_type = client_cert::CONFIG_TYPE_IPSEC; } } } else if (type == shill::kTypeWifi && security == shill::kSecurity8021x) { client_cert_type = client_cert::CONFIG_TYPE_EAP; } base::DictionaryValue config_properties; if (client_cert_type != client_cert::CONFIG_TYPE_NONE) { // Note: if we get here then a certificate *may* be required, so we want // to ensure that certificates have loaded successfully before attempting // to connect. // User must be logged in to connect to a network requiring a certificate. if (!logged_in_ || !cert_loader_) { NET_LOG_ERROR("User not logged in", ""); ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired); return; } // If certificates have not been loaded yet, queue the connect request. if (!certificates_loaded_) { NET_LOG_EVENT("Certificates not loaded", ""); QueueConnectRequest(service_path); return; } // Check certificate properties from policy. if (cert_config_from_policy.client_cert_type == onc::client_cert::kPattern) { if (!ClientCertResolver::ResolveCertificatePatternSync( client_cert_type, cert_config_from_policy.pattern, &config_properties)) { ErrorCallbackForPendingRequest(service_path, kErrorCertificateRequired); return; } } else if (check_error_state && !client_cert::IsCertificateConfigured(client_cert_type, service_properties)) { // Network may not be configured. ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); return; } } if (type == shill::kTypeVPN) { // VPN may require a username, and/or passphrase to be set. (Check after // ensuring that any required certificates are configured). DCHECK(provider_properties); if (VPNRequiresCredentials( service_path, vpn_provider_type, *provider_properties)) { NET_LOG_USER("VPN Requires Credentials", service_path); ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); return; } // If it's L2TP/IPsec PSK, there is no properties to configure, so proceed // to connect. if (client_cert_type == client_cert::CONFIG_TYPE_NONE) { CallShillConnect(service_path); return; } } if (!config_properties.empty()) { NET_LOG_EVENT("Configuring Network", service_path); configuration_handler_->SetProperties( service_path, config_properties, NetworkConfigurationObserver::SOURCE_USER_ACTION, base::Bind(&NetworkConnectionHandler::CallShillConnect, AsWeakPtr(), service_path), base::Bind(&NetworkConnectionHandler::HandleConfigurationFailure, AsWeakPtr(), service_path)); return; } // Otherwise, we probably still need to configure the network since // 'Connectable' is false. If |check_error_state| is true, signal an // error, otherwise attempt to connect to possibly gain additional error // state from Shill (or in case 'Connectable' is improperly unset). if (check_error_state) ErrorCallbackForPendingRequest(service_path, kErrorConfigurationRequired); else CallShillConnect(service_path); } void NetworkConnectionHandler::QueueConnectRequest( const std::string& service_path) { ConnectRequest* request = GetPendingRequest(service_path); if (!request) { NET_LOG_ERROR("No pending request to queue", service_path); return; } const int kMaxCertLoadTimeSeconds = 15; base::TimeDelta dtime = base::TimeTicks::Now() - logged_in_time_; if (dtime > base::TimeDelta::FromSeconds(kMaxCertLoadTimeSeconds)) { NET_LOG_ERROR("Certificate load timeout", service_path); InvokeErrorCallback(service_path, request->error_callback, kErrorCertLoadTimeout); return; } NET_LOG_EVENT("Connect Request Queued", service_path); queued_connect_.reset(new ConnectRequest( service_path, request->profile_path, request->success_callback, request->error_callback)); pending_requests_.erase(service_path); // Post a delayed task to check to see if certificates have loaded. If they // haven't, and queued_connect_ has not been cleared (e.g. by a successful // connect request), cancel the request and notify the user. base::MessageLoopProxy::current()->PostDelayedTask( FROM_HERE, base::Bind(&NetworkConnectionHandler::CheckCertificatesLoaded, AsWeakPtr()), base::TimeDelta::FromSeconds(kMaxCertLoadTimeSeconds) - dtime); } void NetworkConnectionHandler::CheckCertificatesLoaded() { if (certificates_loaded_) return; // If queued_connect_ has been cleared (e.g. another connect request occurred // and wasn't queued), do nothing here. if (!queued_connect_) return; // Otherwise, notify the user. NET_LOG_ERROR("Certificate load timeout", queued_connect_->service_path); InvokeErrorCallback(queued_connect_->service_path, queued_connect_->error_callback, kErrorCertLoadTimeout); queued_connect_.reset(); } void NetworkConnectionHandler::ConnectToQueuedNetwork() { DCHECK(queued_connect_); // Make a copy of |queued_connect_| parameters, because |queued_connect_| // will get reset at the beginning of |ConnectToNetwork|. std::string service_path = queued_connect_->service_path; base::Closure success_callback = queued_connect_->success_callback; network_handler::ErrorCallback error_callback = queued_connect_->error_callback; NET_LOG_EVENT("Connecting to Queued Network", service_path); ConnectToNetwork(service_path, success_callback, error_callback, false /* check_error_state */); } void NetworkConnectionHandler::CallShillConnect( const std::string& service_path) { NET_LOG_EVENT("Sending Connect Request to Shill", service_path); network_state_handler_->ClearLastErrorForNetwork(service_path); DBusThreadManager::Get()->GetShillServiceClient()->Connect( dbus::ObjectPath(service_path), base::Bind(&NetworkConnectionHandler::HandleShillConnectSuccess, AsWeakPtr(), service_path), base::Bind(&NetworkConnectionHandler::HandleShillConnectFailure, AsWeakPtr(), service_path)); } void NetworkConnectionHandler::HandleConfigurationFailure( const std::string& service_path, const std::string& error_name, scoped_ptr error_data) { ConnectRequest* request = GetPendingRequest(service_path); if (!request) { NET_LOG_ERROR("HandleConfigurationFailure called with no pending request.", service_path); return; } network_handler::ErrorCallback error_callback = request->error_callback; pending_requests_.erase(service_path); if (!error_callback.is_null()) error_callback.Run(kErrorConfigureFailed, error_data.Pass()); } void NetworkConnectionHandler::HandleShillConnectSuccess( const std::string& service_path) { ConnectRequest* request = GetPendingRequest(service_path); if (!request) { NET_LOG_ERROR("HandleShillConnectSuccess called with no pending request.", service_path); return; } request->connect_state = ConnectRequest::CONNECT_STARTED; NET_LOG_EVENT("Connect Request Acknowledged", service_path); // Do not call success_callback here, wait for one of the following // conditions: // * State transitions to a non connecting state indicating success or failure // * Network is no longer in the visible list, indicating failure CheckPendingRequest(service_path); } void NetworkConnectionHandler::HandleShillConnectFailure( const std::string& service_path, const std::string& dbus_error_name, const std::string& dbus_error_message) { ConnectRequest* request = GetPendingRequest(service_path); if (!request) { NET_LOG_ERROR("HandleShillConnectFailure called with no pending request.", service_path); return; } network_handler::ErrorCallback error_callback = request->error_callback; pending_requests_.erase(service_path); network_handler::ShillErrorCallbackFunction( shill::kErrorConnectFailed, service_path, error_callback, dbus_error_name, dbus_error_message); } void NetworkConnectionHandler::CheckPendingRequest( const std::string service_path) { ConnectRequest* request = GetPendingRequest(service_path); DCHECK(request); if (request->connect_state == ConnectRequest::CONNECT_REQUESTED) return; // Request has not started, ignore update const NetworkState* network = network_state_handler_->GetNetworkState(service_path); if (!network) return; // NetworkState may not be be updated yet. if (network->IsConnectingState()) { request->connect_state = ConnectRequest::CONNECT_CONNECTING; return; } if (network->IsConnectedState()) { NET_LOG_EVENT("Connect Request Succeeded", service_path); if (!request->profile_path.empty()) { // If a profile path was specified, set it on a successful connection. configuration_handler_->SetNetworkProfile( service_path, request->profile_path, NetworkConfigurationObserver::SOURCE_USER_ACTION, base::Bind(&base::DoNothing), chromeos::network_handler::ErrorCallback()); } if (!request->success_callback.is_null()) request->success_callback.Run(); pending_requests_.erase(service_path); return; } if (network->connection_state() == shill::kStateIdle && request->connect_state != ConnectRequest::CONNECT_CONNECTING) { // Connection hasn't started yet, keep waiting. return; } // Network is neither connecting or connected; an error occurred. std::string error_name; // 'Canceled' or 'Failed' if (network->connection_state() == shill::kStateIdle && pending_requests_.size() > 1) { // Another connect request canceled this one. error_name = kErrorConnectCanceled; } else { error_name = shill::kErrorConnectFailed; if (network->connection_state() != shill::kStateFailure) { NET_LOG_ERROR("Unexpected State: " + network->connection_state(), service_path); } } network_handler::ErrorCallback error_callback = request->error_callback; pending_requests_.erase(service_path); if (error_callback.is_null()) { NET_LOG_ERROR("Connect Error, no callback: " + error_name, service_path); return; } InvokeErrorCallback(service_path, error_callback, error_name); } void NetworkConnectionHandler::CheckAllPendingRequests() { for (std::map::iterator iter = pending_requests_.begin(); iter != pending_requests_.end(); ++iter) { CheckPendingRequest(iter->first); } } void NetworkConnectionHandler::ErrorCallbackForPendingRequest( const std::string& service_path, const std::string& error_name) { ConnectRequest* request = GetPendingRequest(service_path); if (!request) { NET_LOG_ERROR("ErrorCallbackForPendingRequest with no pending request.", service_path); return; } // Remove the entry before invoking the callback in case it triggers a retry. network_handler::ErrorCallback error_callback = request->error_callback; pending_requests_.erase(service_path); InvokeErrorCallback(service_path, error_callback, error_name); } // Disconnect void NetworkConnectionHandler::CallShillDisconnect( const std::string& service_path, const base::Closure& success_callback, const network_handler::ErrorCallback& error_callback) { NET_LOG_USER("Disconnect Request", service_path); DBusThreadManager::Get()->GetShillServiceClient()->Disconnect( dbus::ObjectPath(service_path), base::Bind(&NetworkConnectionHandler::HandleShillDisconnectSuccess, AsWeakPtr(), service_path, success_callback), base::Bind(&network_handler::ShillErrorCallbackFunction, kErrorShillError, service_path, error_callback)); } void NetworkConnectionHandler::HandleShillDisconnectSuccess( const std::string& service_path, const base::Closure& success_callback) { NET_LOG_EVENT("Disconnect Request Sent", service_path); if (!success_callback.is_null()) success_callback.Run(); } } // namespace chromeos