| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/shell/browser/shell_network_controller_chromeos.h" |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chromeos/ash/components/network/network_configuration_handler.h" |
| #include "chromeos/ash/components/network/network_connection_handler.h" |
| #include "chromeos/ash/components/network/network_device_handler.h" |
| #include "chromeos/ash/components/network/network_handler.h" |
| #include "chromeos/ash/components/network/network_handler_callbacks.h" |
| #include "chromeos/ash/components/network/network_state.h" |
| #include "chromeos/ash/components/network/network_state_handler.h" |
| #include "chromeos/ash/components/network/network_type_pattern.h" |
| #include "chromeos/ash/components/network/technology_state_controller.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| // Frequency at which networks should be scanned when not connected to a network |
| // or when connected to a non-preferred network. |
| const int kScanIntervalSec = 10; |
| |
| void HandleEnableWifiError(const std::string& error_name) { |
| LOG(WARNING) << "Unable to enable wifi: " << error_name; |
| } |
| |
| // Returns a human-readable name for the network described by |network|. |
| std::string GetNetworkName(const ash::NetworkState& network) { |
| return !network.name().empty() |
| ? network.name() |
| : base::StringPrintf("[%s]", network.type().c_str()); |
| } |
| |
| // Returns true if shill is either connected or connecting to a network. |
| bool IsConnectedOrConnecting() { |
| ash::NetworkStateHandler* state_handler = |
| ash::NetworkHandler::Get()->network_state_handler(); |
| return state_handler->ConnectedNetworkByType( |
| ash::NetworkTypePattern::Default()) || |
| state_handler->ConnectingNetworkByType( |
| ash::NetworkTypePattern::Default()); |
| } |
| |
| } // namespace |
| |
| ShellNetworkController::ShellNetworkController( |
| const std::string& preferred_network_name) |
| : state_(STATE_IDLE), |
| preferred_network_name_(preferred_network_name), |
| preferred_network_is_active_(false) { |
| ash::NetworkStateHandler* state_handler = |
| ash::NetworkHandler::Get()->network_state_handler(); |
| state_handler->AddObserver(this, FROM_HERE); |
| ash::NetworkHandler::Get() |
| ->technology_state_controller() |
| ->SetTechnologiesEnabled( |
| ash::NetworkTypePattern::Primitive(shill::kTypeWifi), true, |
| base::BindRepeating(&HandleEnableWifiError)); |
| |
| // If we're unconnected, trigger a connection attempt and start scanning. |
| NetworkConnectionStateChanged(nullptr); |
| } |
| |
| ShellNetworkController::~ShellNetworkController() { |
| ash::NetworkHandler::Get()->network_state_handler()->RemoveObserver( |
| this, FROM_HERE); |
| } |
| |
| void ShellNetworkController::NetworkListChanged() { |
| VLOG(1) << "Network list changed"; |
| ConnectIfUnconnected(); |
| } |
| |
| void ShellNetworkController::NetworkConnectionStateChanged( |
| const ash::NetworkState* network) { |
| if (network) { |
| VLOG(1) << "Network connection state changed:" |
| << " name=" << GetNetworkName(*network) |
| << " type=" << network->type() << " path=" << network->path() |
| << " state=" << network->connection_state(); |
| } else { |
| VLOG(1) << "Network connection state changed: [none]"; |
| } |
| |
| const ash::NetworkState* wifi_network = GetActiveWiFiNetwork(); |
| preferred_network_is_active_ = |
| wifi_network && wifi_network->name() == preferred_network_name_; |
| VLOG(2) << "Active WiFi network is " |
| << (wifi_network ? wifi_network->name() : std::string("[none]")); |
| |
| if (preferred_network_is_active_ || |
| (preferred_network_name_.empty() && wifi_network)) { |
| SetScanningEnabled(false); |
| } else { |
| SetScanningEnabled(true); |
| ConnectIfUnconnected(); |
| } |
| } |
| |
| void ShellNetworkController::SetCellularAllowRoaming(bool allow_roaming) { |
| ash::NetworkHandler* handler = ash::NetworkHandler::Get(); |
| ash::NetworkStateHandler::NetworkStateList network_list; |
| |
| base::Value::Dict properties; |
| properties.Set(shill::kCellularAllowRoamingProperty, allow_roaming); |
| |
| handler->network_state_handler()->GetVisibleNetworkListByType( |
| ash::NetworkTypePattern::Cellular(), &network_list); |
| |
| for (const ash::NetworkState* network : network_list) { |
| handler->network_configuration_handler()->SetShillProperties( |
| network->path(), properties, base::DoNothing(), |
| ash::network_handler::ErrorCallback()); |
| } |
| } |
| |
| const ash::NetworkState* ShellNetworkController::GetActiveWiFiNetwork() { |
| ash::NetworkStateHandler* state_handler = |
| ash::NetworkHandler::Get()->network_state_handler(); |
| const ash::NetworkState* network = state_handler->FirstNetworkByType( |
| ash::NetworkTypePattern::Primitive(shill::kTypeWifi)); |
| return network && |
| (network->IsConnectedState() || network->IsConnectingState()) |
| ? network |
| : nullptr; |
| } |
| |
| void ShellNetworkController::SetScanningEnabled(bool enabled) { |
| const bool currently_enabled = scan_timer_.IsRunning(); |
| if (enabled == currently_enabled) |
| return; |
| |
| VLOG(1) << (enabled ? "Starting" : "Stopping") << " scanning"; |
| if (enabled) { |
| RequestScan(); |
| scan_timer_.Start(FROM_HERE, base::Seconds(kScanIntervalSec), this, |
| &ShellNetworkController::RequestScan); |
| } else { |
| scan_timer_.Stop(); |
| } |
| } |
| |
| void ShellNetworkController::RequestScan() { |
| VLOG(1) << "Requesting scan"; |
| ash::NetworkHandler::Get()->network_state_handler()->RequestScan( |
| ash::NetworkTypePattern::Default()); |
| } |
| |
| void ShellNetworkController::ConnectIfUnconnected() { |
| // Don't do anything if the default network is already the preferred one or if |
| // we have a pending request to connect to it. |
| if (preferred_network_is_active_ || |
| state_ == STATE_WAITING_FOR_PREFERRED_RESULT) |
| return; |
| |
| const ash::NetworkState* best_network = nullptr; |
| bool can_connect_to_preferred_network = false; |
| |
| ash::NetworkHandler* handler = ash::NetworkHandler::Get(); |
| ash::NetworkStateHandler::NetworkStateList network_list; |
| handler->network_state_handler()->GetVisibleNetworkListByType( |
| ash::NetworkTypePattern::WiFi(), &network_list); |
| for (ash::NetworkStateHandler::NetworkStateList::const_iterator it = |
| network_list.begin(); |
| it != network_list.end(); ++it) { |
| const ash::NetworkState* network = *it; |
| if (!network->connectable()) |
| continue; |
| |
| if (!preferred_network_name_.empty() && |
| network->name() == preferred_network_name_) { |
| best_network = network; |
| can_connect_to_preferred_network = true; |
| break; |
| } else if (!best_network) { |
| best_network = network; |
| } |
| } |
| |
| // Don't switch networks if we're already connecting/connected and wouldn't be |
| // switching to the preferred network. |
| if ((IsConnectedOrConnecting() || state_ != STATE_IDLE) && |
| !can_connect_to_preferred_network) |
| return; |
| |
| if (!best_network) { |
| VLOG(1) << "Didn't find any connectable networks"; |
| return; |
| } |
| |
| VLOG(1) << "Connecting to network " << GetNetworkName(*best_network) |
| << " with path " << best_network->path() << " and strength " |
| << best_network->signal_strength(); |
| state_ = can_connect_to_preferred_network |
| ? STATE_WAITING_FOR_PREFERRED_RESULT |
| : STATE_WAITING_FOR_NON_PREFERRED_RESULT; |
| handler->network_connection_handler()->ConnectToNetwork( |
| best_network->path(), |
| base::BindOnce(&ShellNetworkController::HandleConnectionSuccess, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindRepeating(&ShellNetworkController::HandleConnectionError, |
| weak_ptr_factory_.GetWeakPtr()), |
| false /* check_error_state */, ash::ConnectCallbackMode::ON_COMPLETED); |
| } |
| |
| void ShellNetworkController::HandleConnectionSuccess() { |
| VLOG(1) << "Successfully connected to network"; |
| state_ = STATE_IDLE; |
| } |
| |
| void ShellNetworkController::HandleConnectionError( |
| const std::string& error_name) { |
| LOG(WARNING) << "Unable to connect to network: " << error_name; |
| state_ = STATE_IDLE; |
| } |
| |
| } // namespace extensions |