// 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/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/thread_task_runner_handle.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_state_handler.h" #include "components/device_event_log/device_event_log.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& device_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 << ": " << device_path; network_handler::RunErrorCallback(error_callback, device_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) << "IPConfigs.Refresh Failed: " << 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) { NET_LOG(USER) << "Device.SetProperty: " << property_name; 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 next_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::kTDLSStatusOperation && !result.empty()) { NET_LOG(ERROR) << "Unexpected TDLS result: " + result << ": " << device_path; } TDLSOperationParams new_params; const int64 kRequestStatusDelayMs = 500; int64 request_delay_ms = 0; if (params.operation == shill::kTDLSStatusOperation) { // If this is the last operation, or the result is 'Nonexistent', // return the result. if (params.next_operation.empty() || result == shill::kTDLSNonexistentState) { if (!callback.is_null()) callback.Run(result); return; } // Otherwise start the next operation. new_params.operation = params.next_operation; } else if (params.operation == shill::kTDLSDiscoverOperation) { // Send a delayed Status request followed by a Setup request. request_delay_ms = kRequestStatusDelayMs; new_params.operation = shill::kTDLSStatusOperation; new_params.next_operation = shill::kTDLSSetupOperation; } else if (params.operation == shill::kTDLSSetupOperation || params.operation == shill::kTDLSTeardownOperation) { // Send a delayed Status request. request_delay_ms = kRequestStatusDelayMs; new_params.operation = shill::kTDLSStatusOperation; } else { NET_LOG(ERROR) << "Unexpected TDLS operation: " + params.operation; NOTREACHED(); } new_params.ip_or_mac_address = params.ip_or_mac_address; base::TimeDelta request_delay; if (!DBusThreadManager::Get()->GetShillDeviceClient()->GetTestInterface()) request_delay = base::TimeDelta::FromMilliseconds(request_delay_ms); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&CallPerformTDLSOperation, device_path, new_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::kTDLSDiscoverOperation || params.operation == shill::kTDLSSetupOperation) && dbus_error_name == shill::kErrorResultInProgress && params.retry_count < kMaxRetries) { TDLSOperationParams retry_params = params; ++retry_params.retry_count; NET_LOG(EVENT) << "TDLS Retry: " << 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::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&CallPerformTDLSOperation, device_path, retry_params, callback, error_callback), request_delay); return; } NET_LOG(ERROR) << "TDLS Operation: " << params.operation << " 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) { LOG(ERROR) << "TDLS: " << params.operation; 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() { if (network_state_handler_) 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) { NET_LOG(USER) << "Device.RegisterCellularNetwork: " << device_path << " Id: " << network_id; 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) { NET_LOG(USER) << "Device.SetCarrier: " << device_path << " carrier: " << carrier; 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) { NET_LOG(USER) << "Device.RequirePin: " << device_path << ": " << require_pin; 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) { NET_LOG(USER) << "Device.EnterPin: " << device_path; 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) { NET_LOG(USER) << "Device.UnblockPin: " << device_path; 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) { NET_LOG(USER) << "Device.ChangePin: " << device_path; 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 = GetWifiDeviceState(error_callback); if (!device_state) return; TDLSOperationParams params; params.operation = enabled ? shill::kTDLSDiscoverOperation : 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 = GetWifiDeviceState(error_callback); if (!device_state) 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::AddWifiWakeOnPacketConnection( const net::IPEndPoint& ip_endpoint, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { const DeviceState* device_state = GetWifiDeviceState(error_callback); if (!device_state) return; NET_LOG(USER) << "Device.AddWakeOnWifi: " << device_state->path(); DBusThreadManager::Get()->GetShillDeviceClient()->AddWakeOnPacketConnection( dbus::ObjectPath(device_state->path()), ip_endpoint, callback, base::Bind(&HandleShillCallFailure, device_state->path(), error_callback)); } void NetworkDeviceHandlerImpl::RemoveWifiWakeOnPacketConnection( const net::IPEndPoint& ip_endpoint, const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { const DeviceState* device_state = GetWifiDeviceState(error_callback); if (!device_state) return; NET_LOG(USER) << "Device.RemoveWakeOnWifi: " << device_state->path(); DBusThreadManager::Get() ->GetShillDeviceClient() ->RemoveWakeOnPacketConnection(dbus::ObjectPath(device_state->path()), ip_endpoint, callback, base::Bind(&HandleShillCallFailure, device_state->path(), error_callback)); } void NetworkDeviceHandlerImpl::RemoveAllWifiWakeOnPacketConnections( const base::Closure& callback, const network_handler::ErrorCallback& error_callback) { const DeviceState* device_state = GetWifiDeviceState(error_callback); if (!device_state) return; NET_LOG(USER) << "Device.RemoveAllWakeOnWifi: " << device_state->path(); DBusThreadManager::Get() ->GetShillDeviceClient() ->RemoveAllWakeOnPacketConnections(dbus::ObjectPath(device_state->path()), callback, base::Bind(&HandleShillCallFailure, device_state->path(), 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 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()); } } const DeviceState* NetworkDeviceHandlerImpl::GetWifiDeviceState( 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 NULL; scoped_ptr error_data(new base::DictionaryValue); error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing); error_callback.Run(kErrorDeviceMissing, error_data.Pass()); return NULL; } return device_state; } } // namespace chromeos