| // 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 <stddef.h> |
| #include <stdint.h> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/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_t kRequestStatusDelayMs = 500; |
| int64_t 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_t 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; |
| std::unique_ptr<base::DictionaryValue> error_data( |
| network_handler::CreateDBusErrorData(device_path, error_name, |
| error_detail, dbus_error_name, |
| dbus_error_message)); |
| error_callback.Run(error_name, std::move(error_data)); |
| } |
| |
| 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::SetMACAddressRandomizationEnabled( |
| const bool enabled) { |
| mac_addr_randomization_ = enabled; |
| ApplyMACAddressRandomizationToShill(); |
| } |
| |
| 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(); |
| ApplyMACAddressRandomizationToShill(); |
| } |
| |
| NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl() |
| : network_state_handler_(NULL), |
| cellular_allow_roaming_(false), |
| mac_addr_randomization_(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()); |
| } |
| } |
| |
| void NetworkDeviceHandlerImpl::ApplyMACAddressRandomizationToShill() { |
| const DeviceState* device_state = |
| GetWifiDeviceState(network_handler::ErrorCallback()); |
| if (!device_state) |
| return; |
| |
| SetDevicePropertyInternal(device_state->path(), |
| shill::kMACAddressRandomizationProperty, |
| base::FundamentalValue(mac_addr_randomization_), |
| 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; |
| std::unique_ptr<base::DictionaryValue> error_data( |
| new base::DictionaryValue); |
| error_data->SetString(network_handler::kErrorName, kErrorDeviceMissing); |
| error_callback.Run(kErrorDeviceMissing, std::move(error_data)); |
| return NULL; |
| } |
| |
| return device_state; |
| } |
| |
| } // namespace chromeos |