// Copyright 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_device_handler_impl.h" #include "base/bind.h" #include "base/location.h" #include "base/message_loop/message_loop_proxy.h" #include "base/time/time.h" #include "base/values.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_device_client.h" #include "chromeos/dbus/shill_ipconfig_client.h" #include "chromeos/network/device_state.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_state_handler.h" #include "dbus/object_path.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { std::string GetErrorNameForShillError(const std::string& shill_error_name) { if (shill_error_name == shill::kErrorResultFailure) return NetworkDeviceHandler::kErrorFailure; if (shill_error_name == shill::kErrorResultNotSupported) return NetworkDeviceHandler::kErrorNotSupported; if (shill_error_name == shill::kErrorResultIncorrectPin) return NetworkDeviceHandler::kErrorIncorrectPin; if (shill_error_name == shill::kErrorResultPinBlocked) return NetworkDeviceHandler::kErrorPinBlocked; if (shill_error_name == shill::kErrorResultPinRequired) return NetworkDeviceHandler::kErrorPinRequired; if (shill_error_name == shill::kErrorResultNotFound) return NetworkDeviceHandler::kErrorDeviceMissing; return NetworkDeviceHandler::kErrorUnknown; } void InvokeErrorCallback(const std::string& service_path, const network_handler::ErrorCallback& error_callback, const std::string& error_name) { std::string error_msg = "Device Error: " + error_name; NET_LOG_ERROR(error_msg, service_path); network_handler::RunErrorCallback( error_callback, service_path, error_name, error_msg); } void HandleShillCallFailure( const std::string& device_path, const network_handler::ErrorCallback& error_callback, const std::string& shill_error_name, const std::string& shill_error_message) { network_handler::ShillErrorCallbackFunction( GetErrorNameForShillError(shill_error_name), device_path, error_callback, shill_error_name, shill_error_message); } void IPConfigRefreshCallback(const std::string& ipconfig_path, DBusMethodCallStatus call_status) { if (call_status != DBUS_METHOD_CALL_SUCCESS) { NET_LOG_ERROR( base::StringPrintf("IPConfigs.Refresh Failed: %d", call_status), ipconfig_path); } else { NET_LOG_EVENT("IPConfigs.Refresh Succeeded", ipconfig_path); } } void RefreshIPConfigsCallback( const base::Closure& callback, const network_handler::ErrorCallback& error_callback, const std::string& device_path, const base::DictionaryValue& properties) { const base::ListValue* ip_configs; if (!properties.GetListWithoutPathExpansion( shill::kIPConfigsProperty, &ip_configs)) { NET_LOG_ERROR("RequestRefreshIPConfigs Failed", device_path); network_handler::ShillErrorCallbackFunction( "RequestRefreshIPConfigs Failed", device_path, error_callback, std::string("Missing ") + shill::kIPConfigsProperty, ""); return; } for (size_t i = 0; i < ip_configs->GetSize(); i++) { std::string ipconfig_path; if (!ip_configs->GetString(i, &ipconfig_path)) continue; DBusThreadManager::Get()->GetShillIPConfigClient()->Refresh( dbus::ObjectPath(ipconfig_path), base::Bind(&IPConfigRefreshCallback, ipconfig_path)); } // It is safe to invoke |callback| here instead of waiting for the // IPConfig.Refresh callbacks to complete because the Refresh DBus calls will // be executed in order and thus before any further DBus requests that // |callback| may issue. if (!callback.is_null()) callback.Run(); } void ProposeScanCallback( const std::string& device_path, const base::Closure& callback, const network_handler::ErrorCallback& error_callback, DBusMethodCallStatus call_status) { if (call_status != DBUS_METHOD_CALL_SUCCESS) { network_handler::ShillErrorCallbackFunction( "Device.ProposeScan Failed", device_path, error_callback, base::StringPrintf("DBus call failed: %d", call_status), ""); return; } NET_LOG_EVENT("Device.ProposeScan succeeded.", device_path); if (!callback.is_null()) callback.Run(); } void SetDevicePropertyInternal( const std::string& device_path, const std::string& property_name, const base::Value& value, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->SetProperty( dbus::ObjectPath(device_path), property_name, value, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } // Struct containing TDLS Operation parameters. struct TDLSOperationParams { TDLSOperationParams() : retry_count(0) {} std::string operation; std::string ip_or_mac_address; int retry_count; }; // Forward declare for PostDelayedTask. void CallPerformTDLSOperation( const std::string& device_path, const TDLSOperationParams& params, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback); void TDLSSuccessCallback( const std::string& device_path, const TDLSOperationParams& params, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback, const std::string& result) { std::string event_desc = "TDLSSuccessCallback: " + params.operation; if (!result.empty()) event_desc += ": " + result; NET_LOG_EVENT(event_desc, device_path); if (params.operation != shill::kTDLSSetupOperation) { if (!callback.is_null()) callback.Run(result); return; } if (!result.empty()) NET_LOG_ERROR("Unexpected TDLS result: " + result, device_path); // Send a delayed Status request after a successful Setup call. TDLSOperationParams status_params; status_params.operation = shill::kTDLSStatusOperation; status_params.ip_or_mac_address = params.ip_or_mac_address; const int64 kRequestStatusDelayMs = 500; base::TimeDelta request_delay; if (!DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()) request_delay = base::TimeDelta::FromMilliseconds(kRequestStatusDelayMs); base::MessageLoopProxy::current()->PostDelayedTask( FROM_HERE, base::Bind(&CallPerformTDLSOperation, device_path, status_params, callback, error_callback), request_delay); } void TDLSErrorCallback( const std::string& device_path, const TDLSOperationParams& params, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback, const std::string& dbus_error_name, const std::string& dbus_error_message) { // If a Setup operation receives an InProgress error, retry. const int kMaxRetries = 5; if (params.operation == shill::kTDLSSetupOperation && dbus_error_name == shill::kErrorResultInProgress && params.retry_count < kMaxRetries) { TDLSOperationParams retry_params = params; ++retry_params.retry_count; NET_LOG_EVENT(base::StringPrintf("TDLS Retry: %d", params.retry_count), device_path); const int64 kReRequestDelayMs = 1000; base::TimeDelta request_delay; if (!DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()) request_delay = base::TimeDelta::FromMilliseconds(kReRequestDelayMs); base::MessageLoopProxy::current()->PostDelayedTask( FROM_HERE, base::Bind(&CallPerformTDLSOperation, device_path, retry_params, callback, error_callback), request_delay); return; } NET_LOG_ERROR("TDLS Error:" + dbus_error_name + ":" + dbus_error_message, device_path); if (error_callback.is_null()) return; const std::string error_name = dbus_error_name == shill::kErrorResultInProgress ? NetworkDeviceHandler::kErrorTimeout : NetworkDeviceHandler::kErrorUnknown; const std::string& error_detail = params.ip_or_mac_address; scoped_ptr error_data( network_handler::CreateDBusErrorData( device_path, error_name, error_detail, dbus_error_name, dbus_error_message)); error_callback.Run(error_name, error_data.Pass()); } void CallPerformTDLSOperation( const std::string& device_path, const TDLSOperationParams& params, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback) { NET_LOG_EVENT("CallPerformTDLSOperation: " + params.operation, device_path); DBusThreadManager::Get()->GetShillDeviceClient()->PerformTDLSOperation( dbus::ObjectPath(device_path), params.operation, params.ip_or_mac_address, base::Bind(&TDLSSuccessCallback, device_path, params, callback, error_callback), base::Bind(&TDLSErrorCallback, device_path, params, callback, error_callback)); } } // namespace NetworkDeviceHandlerImpl::~NetworkDeviceHandlerImpl() { network_state_handler_->RemoveObserver(this, FROM_HERE); } void NetworkDeviceHandlerImpl::GetDeviceProperties( const std::string& device_path, const network_handler::DictionaryResultCallback& callback, const network_handler::ErrorCallback& error_callback) const { DBusThreadManager::Get()->GetShillDeviceClient()->GetProperties( dbus::ObjectPath(device_path), base::Bind(&network_handler::GetPropertiesCallback, callback, error_callback, device_path)); } void NetworkDeviceHandlerImpl::SetDeviceProperty( const std::string& device_path, const std::string& property_name, const base::Value& value, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { const char* const property_blacklist[] = { // Must only be changed by policy/owner through. shill::kCellularAllowRoamingProperty }; for (size_t i = 0; i < arraysize(property_blacklist); ++i) { if (property_name == property_blacklist[i]) { InvokeErrorCallback( device_path, error_callback, "SetDeviceProperty called on blacklisted property " + property_name); return; } } SetDevicePropertyInternal( device_path, property_name, value, callback, error_callback); } void NetworkDeviceHandlerImpl::RequestRefreshIPConfigs( const std::string& device_path, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { GetDeviceProperties(device_path, base::Bind(&RefreshIPConfigsCallback, callback, error_callback), error_callback); } void NetworkDeviceHandlerImpl::ProposeScan( const std::string& device_path, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->ProposeScan( dbus::ObjectPath(device_path), base::Bind(&ProposeScanCallback, device_path, callback, error_callback)); } void NetworkDeviceHandlerImpl::RegisterCellularNetwork( const std::string& device_path, const std::string& network_id, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->Register( dbus::ObjectPath(device_path), network_id, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::SetCarrier( const std::string& device_path, const std::string& carrier, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->SetCarrier( dbus::ObjectPath(device_path), carrier, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::RequirePin( const std::string& device_path, bool require_pin, const std::string& pin, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->RequirePin( dbus::ObjectPath(device_path), pin, require_pin, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::EnterPin( const std::string& device_path, const std::string& pin, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->EnterPin( dbus::ObjectPath(device_path), pin, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::UnblockPin( const std::string& device_path, const std::string& puk, const std::string& new_pin, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->UnblockPin( dbus::ObjectPath(device_path), puk, new_pin, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::ChangePin( const std::string& device_path, const std::string& old_pin, const std::string& new_pin, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { DBusThreadManager::Get()->GetShillDeviceClient()->ChangePin( dbus::ObjectPath(device_path), old_pin, new_pin, callback, base::Bind(&HandleShillCallFailure, device_path, error_callback)); } void NetworkDeviceHandlerImpl::SetCellularAllowRoaming( const bool allow_roaming) { cellular_allow_roaming_ = allow_roaming; ApplyCellularAllowRoamingToShill(); } void NetworkDeviceHandlerImpl::SetWifiTDLSEnabled( const std::string& ip_or_mac_address, bool enabled, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback) { const DeviceState* device_state = network_state_handler_->GetDeviceStateByType(NetworkTypePattern::WiFi()); if (!device_state) { if (error_callback.is_null()) return; scoped_ptr error_data(new base::DictionaryValue); error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing); error_callback.Run(kErrorDeviceMissing, error_data.Pass()); return; } TDLSOperationParams params; params.operation = enabled ? shill::kTDLSSetupOperation : shill::kTDLSTeardownOperation; params.ip_or_mac_address = ip_or_mac_address; CallPerformTDLSOperation( device_state->path(), params, callback, error_callback); } void NetworkDeviceHandlerImpl::GetWifiTDLSStatus( const std::string& ip_or_mac_address, const network_handler::StringResultCallback& callback, const network_handler::ErrorCallback& error_callback) { const DeviceState* device_state = network_state_handler_->GetDeviceStateByType(NetworkTypePattern::WiFi()); if (!device_state) { if (error_callback.is_null()) return; scoped_ptr error_data(new base::DictionaryValue); error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing); error_callback.Run(kErrorDeviceMissing, error_data.Pass()); return; } TDLSOperationParams params; params.operation = shill::kTDLSStatusOperation; params.ip_or_mac_address = ip_or_mac_address; CallPerformTDLSOperation( device_state->path(), params, callback, error_callback); } void NetworkDeviceHandlerImpl::DeviceListChanged() { ApplyCellularAllowRoamingToShill(); } NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl() : network_state_handler_(NULL), cellular_allow_roaming_(false) {} void NetworkDeviceHandlerImpl::Init( NetworkStateHandler* network_state_handler) { DCHECK(network_state_handler); network_state_handler_ = network_state_handler; network_state_handler_->AddObserver(this, FROM_HERE); } void NetworkDeviceHandlerImpl::ApplyCellularAllowRoamingToShill() { NetworkStateHandler::DeviceStateList list; network_state_handler_->GetDeviceListByType(NetworkTypePattern::Cellular(), &list); if (list.empty()) { NET_LOG_DEBUG("No cellular device is available", "Roaming is only supported by cellular devices."); return; } for (NetworkStateHandler::DeviceStateList::const_iterator it = list.begin(); it != list.end(); ++it) { const DeviceState* device_state = *it; bool current_allow_roaming = device_state->allow_roaming(); // If roaming is required by the provider, always try to set to true. bool new_device_value = device_state->provider_requires_roaming() || cellular_allow_roaming_; // Only set the value if the current value is different from // |new_device_value|. if (new_device_value == current_allow_roaming) continue; SetDevicePropertyInternal(device_state->path(), shill::kCellularAllowRoamingProperty, base::FundamentalValue(new_device_value), base::Bind(&base::DoNothing), network_handler::ErrorCallback()); } } } // namespace chromeos