blob: 4aea8c3c571601ca0b59b2c8f02f98d5fe282ef6 [file] [log] [blame]
// Copyright (c) 2012 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_state_handler.h"
#include <stddef.h>
#include <limits>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/format_macros.h"
#include "base/guid.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_handler_callbacks.h"
#include "chromeos/network/network_state_handler_observer.h"
#include "chromeos/network/tether_constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
// Constants used for logging.
constexpr char kReasonStateChange[] = "State Change";
constexpr char kReasonChange[] = "New Network";
constexpr char kReasonUpdate[] = "Update";
constexpr char kReasonUpdateIPConfig[] = "UpdateIPConfig";
constexpr char kReasonUpdateDeviceIPConfig[] = "UpdateDeviceIPConfig";
constexpr char kReasonTether[] = "Tether Change";
bool ConnectionStateChanged(const NetworkState* network,
const std::string& prev_connection_state,
NetworkState::PortalState prev_portal_state) {
if (network->portal_state() != prev_portal_state)
return true;
std::string connection_state = network->connection_state();
bool prev_idle = prev_connection_state.empty() ||
prev_connection_state == shill::kStateIdle;
bool cur_idle = connection_state == shill::kStateIdle;
if (prev_idle || cur_idle)
return prev_idle != cur_idle;
return connection_state != prev_connection_state;
}
std::string GetManagedStateLogType(const ManagedState* state) {
switch (state->managed_type()) {
case ManagedState::MANAGED_TYPE_NETWORK:
return "Network";
case ManagedState::MANAGED_TYPE_DEVICE:
return "Device";
}
NOTREACHED();
return "";
}
std::string GetLogName(const ManagedState* state) {
if (!state)
return "None";
const NetworkState* network = state->AsNetworkState();
if (network)
return NetworkId(network);
return state->path();
}
bool ShouldIncludeNetworkInList(const NetworkState* network_state,
bool configured_only,
bool visible_only) {
if (configured_only && !network_state->IsInProfile())
return false;
if (visible_only && !network_state->visible())
return false;
if (network_state->type() == shill::kTypeWifi &&
!network_state->tether_guid().empty()) {
// Wi-Fi networks which are actually underlying Wi-Fi hotspots for a
// Tether network should not be included since they should only be shown
// to the user as Tether networks.
return false;
}
return true;
}
// ManagedState entries may not have |type| set when the network is initially
// added to a list (i.e. before the initial properties are received). Use this
// wrapper anyplace where |managed| might be uninitialized.
bool TypeMatches(const ManagedState* managed, const NetworkTypePattern& type) {
return !managed->type().empty() && managed->Matches(type);
}
} // namespace
// Class for tracking properties that affect whether a NetworkState is active.
class NetworkStateHandler::ActiveNetworkState {
public:
explicit ActiveNetworkState(const NetworkState* network)
: guid_(network->guid()),
connection_state_(network->connection_state()),
activation_state_(network->activation_state()),
connect_requested_(network->connect_requested()),
signal_strength_(network->signal_strength()),
network_technology_(network->network_technology()) {}
bool MatchesNetworkState(const NetworkState* network) {
return guid_ == network->guid() &&
connection_state_ == network->connection_state() &&
activation_state_ == network->activation_state() &&
connect_requested_ == network->connect_requested() &&
(abs(signal_strength_ - network->signal_strength()) <
NetworkState::kSignalStrengthChangeThreshold) &&
network_technology_ == network->network_technology();
}
private:
// Unique network identifier.
const std::string guid_;
// Active networks have a connected or connecting |connection_state_|, see
// NetworkState::Is{Connected|Connecting}State.
const std::string connection_state_;
// Activating Cellular networks are frequently treated like connecting
// networks in the UI, so we also track changes to Cellular activation state.
const std::string activation_state_;
// The connect_requested state affects 'connecting' in the UI.
const bool connect_requested_;
// We care about signal strength changes to active networks.
const int signal_strength_;
// Network technology is indicated in network icons in the UI, so we need to
// track changes to this value.
const std::string network_technology_;
};
const char NetworkStateHandler::kDefaultCheckPortalList[] =
"ethernet,wifi,cellular";
NetworkStateHandler::NetworkStateHandler() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
NetworkStateHandler::~NetworkStateHandler() {
// Normally Shutdown() will get called in ~NetworkHandler, however unit
// tests do not use that class so this needs to call Shutdown when we
// destroy the class.
if (!did_shutdown_)
Shutdown();
}
void NetworkStateHandler::Shutdown() {
if (did_shutdown_)
return; // May get called twice in tests.
did_shutdown_ = true;
for (auto& observer : observers_)
observer.OnShuttingDown();
}
void NetworkStateHandler::InitShillPropertyHandler() {
shill_property_handler_ =
std::make_unique<internal::ShillPropertyHandler>(this);
shill_property_handler_->Init();
}
void NetworkStateHandler::UpdateBlockedWifiNetworks(
bool only_managed,
bool available_only,
const std::vector<std::string>& blocked_hex_ssids) {
if (allow_only_policy_networks_to_connect_ == only_managed &&
allow_only_policy_networks_to_connect_if_available_ == available_only &&
blocked_hex_ssids_ == blocked_hex_ssids) {
return;
}
allow_only_policy_networks_to_connect_ = only_managed;
allow_only_policy_networks_to_connect_if_available_ = available_only;
blocked_hex_ssids_ = blocked_hex_ssids;
UpdateBlockedWifiNetworksInternal();
}
const NetworkState* NetworkStateHandler::GetAvailableManagedWifiNetwork()
const {
DeviceState* device =
GetModifiableDeviceStateByType(NetworkTypePattern::WiFi());
const std::string& available_managed_network_path =
device->available_managed_network_path();
if (available_managed_network_path.empty())
return nullptr;
return GetNetworkState(available_managed_network_path);
}
bool NetworkStateHandler::IsProfileNetworksLoaded() {
return is_profile_networks_loaded_;
}
bool NetworkStateHandler::OnlyManagedWifiNetworksAllowed() const {
return allow_only_policy_networks_to_connect_ ||
(allow_only_policy_networks_to_connect_if_available_ &&
GetAvailableManagedWifiNetwork());
}
// static
std::unique_ptr<NetworkStateHandler> NetworkStateHandler::InitializeForTest() {
auto handler = base::WrapUnique(new NetworkStateHandler());
handler->InitShillPropertyHandler();
return handler;
}
void NetworkStateHandler::AddObserver(NetworkStateHandlerObserver* observer,
const base::Location& from_here) {
observers_.AddObserver(observer);
device_event_log::AddEntry(
from_here.file_name(), from_here.line_number(),
device_event_log::LOG_TYPE_NETWORK, device_event_log::LOG_LEVEL_DEBUG,
base::StringPrintf("NetworkStateHandler::AddObserver: 0x%p", observer));
}
void NetworkStateHandler::RemoveObserver(NetworkStateHandlerObserver* observer,
const base::Location& from_here) {
observers_.RemoveObserver(observer);
device_event_log::AddEntry(
from_here.file_name(), from_here.line_number(),
device_event_log::LOG_TYPE_NETWORK, device_event_log::LOG_LEVEL_DEBUG,
base::StringPrintf("NetworkStateHandler::RemoveObserver: 0x%p",
observer));
}
bool NetworkStateHandler::HasObserver(NetworkStateHandlerObserver* observer) {
return observers_.HasObserver(observer);
}
NetworkStateHandler::TechnologyState NetworkStateHandler::GetTechnologyState(
const NetworkTypePattern& type) const {
std::string technology = GetTechnologyForType(type);
if (technology == kTypeTether) {
return tether_technology_state_;
}
// If a technology is not in Shill's 'AvailableTechnologies' list, it is
// always unavailable.
if (!shill_property_handler_->IsTechnologyAvailable(technology))
return TECHNOLOGY_UNAVAILABLE;
// Prohibited should take precedence over other states.
if (shill_property_handler_->IsTechnologyProhibited(technology))
return TECHNOLOGY_PROHIBITED;
// Disabling is a pseudostate used by the UI and takes precedence over
// enabled.
if (shill_property_handler_->IsTechnologyDisabling(technology)) {
DCHECK(shill_property_handler_->IsTechnologyEnabled(technology));
return TECHNOLOGY_DISABLING;
}
// Enabled and Uninitialized should be mutually exclusive. 'Enabling', which
// is a pseudo state used by the UI, takes precedence over 'Uninitialized',
// but not 'Enabled'.
if (shill_property_handler_->IsTechnologyEnabled(technology))
return TECHNOLOGY_ENABLED;
if (shill_property_handler_->IsTechnologyEnabling(technology))
return TECHNOLOGY_ENABLING;
if (shill_property_handler_->IsTechnologyUninitialized(technology))
return TECHNOLOGY_UNINITIALIZED;
// Default state is 'Available', which is equivalent to 'Initialized but not
// enabled'.
return TECHNOLOGY_AVAILABLE;
}
void NetworkStateHandler::SetTechnologyEnabled(
const NetworkTypePattern& type,
bool enabled,
network_handler::ErrorCallback error_callback) {
std::vector<std::string> technologies = GetTechnologiesForType(type);
for (const std::string& technology : technologies) {
if (technology == kTypeTether) {
if (tether_technology_state_ != TECHNOLOGY_ENABLED &&
tether_technology_state_ != TECHNOLOGY_AVAILABLE) {
NET_LOG(ERROR) << "SetTechnologyEnabled() called for the Tether "
<< "DeviceState, but the current state was: "
<< tether_technology_state_;
network_handler::RunErrorCallback(
std::move(error_callback), kTetherDevicePath,
NetworkConnectionHandler::kErrorEnabledOrDisabledWhenNotAvailable,
"");
continue;
}
// Tether does not exist in Shill, so set |tether_technology_state_| and
// skip the below interactions with |shill_property_handler_|.
tether_technology_state_ =
enabled ? TECHNOLOGY_ENABLED : TECHNOLOGY_AVAILABLE;
continue;
}
if (!shill_property_handler_->IsTechnologyAvailable(technology))
continue;
NET_LOG(USER) << "SetTechnologyEnabled " << technology << ":" << enabled;
shill_property_handler_->SetTechnologyEnabled(technology, enabled,
std::move(error_callback));
}
// Signal Device/Technology state changed.
NotifyDeviceListChanged();
}
void NetworkStateHandler::SetTetherTechnologyState(
TechnologyState technology_state) {
if (tether_technology_state_ == technology_state)
return;
tether_technology_state_ = technology_state;
EnsureTetherDeviceState();
// Signal Device/Technology state changed.
NotifyDeviceListChanged();
}
void NetworkStateHandler::SetTetherScanState(bool is_scanning) {
DeviceState* tether_device_state =
GetModifiableDeviceState(kTetherDevicePath);
if (!tether_device_state) {
NET_LOG(ERROR) << "SetTetherScanState() called when Tether TechnologyState "
<< "is UNAVAILABLE; cannot set scanning state.";
return;
}
bool was_scanning = tether_device_state->scanning();
tether_device_state->set_scanning(is_scanning);
if (was_scanning && !is_scanning) {
// If a scan was in progress but has completed, notify observers.
NotifyScanCompleted(tether_device_state);
} else if (!was_scanning && is_scanning) {
// If a scan was started, notify observers.
NotifyScanStarted(tether_device_state);
}
}
void NetworkStateHandler::SetProhibitedTechnologies(
const std::vector<std::string>& prohibited_technologies) {
// Make a copy of |prohibited_technologies| since the list may be edited
// within this function.
std::vector<std::string> prohibited_technologies_copy =
prohibited_technologies;
auto it = prohibited_technologies_copy.begin();
while (it != prohibited_technologies_copy.end()) {
if (*it == kTypeTether) {
// If Tether networks are prohibited, set |tether_technology_state_| and
// remove |kTypeTether| from the list before passing it to
// |shill_property_handler_| below. Shill does not have a concept of
// Tether networks, so it cannot prohibit that technology type.
tether_technology_state_ = TECHNOLOGY_PROHIBITED;
it = prohibited_technologies_copy.erase(it);
} else {
++it;
}
}
shill_property_handler_->SetProhibitedTechnologies(
prohibited_technologies_copy);
// Signal Device/Technology state changed.
NotifyDeviceListChanged();
}
const DeviceState* NetworkStateHandler::GetDeviceState(
const std::string& device_path) const {
const DeviceState* device = GetModifiableDeviceState(device_path);
if (device && !device->update_received()) {
NET_LOG(DEBUG) << "Device exists but update not received: " << device_path;
return nullptr;
}
return device;
}
const DeviceState* NetworkStateHandler::GetDeviceStateByType(
const NetworkTypePattern& type) const {
const DeviceState* device = GetModifiableDeviceStateByType(type);
if (device && !device->update_received())
return nullptr;
return device;
}
bool NetworkStateHandler::GetScanningByType(
const NetworkTypePattern& type) const {
for (auto iter = device_list_.begin(); iter != device_list_.end(); ++iter) {
const DeviceState* device = (*iter)->AsDeviceState();
DCHECK(device);
if (!device->update_received())
continue;
if (device->Matches(type) && device->scanning())
return true;
}
return false;
}
const NetworkState* NetworkStateHandler::GetNetworkState(
const std::string& service_path) const {
const NetworkState* network = GetModifiableNetworkState(service_path);
if (network && !network->update_received())
return nullptr;
return network;
}
const NetworkState* NetworkStateHandler::DefaultNetwork() const {
if (default_network_path_.empty())
return nullptr;
return GetNetworkState(default_network_path_);
}
const NetworkState* NetworkStateHandler::ConnectedNetworkByType(
const NetworkTypePattern& type) {
NetworkStateList active_networks;
GetActiveNetworkListByType(type, &active_networks);
for (auto* network : active_networks) {
if (network->IsConnectedState())
return network;
}
return nullptr;
}
const NetworkState* NetworkStateHandler::ConnectingNetworkByType(
const NetworkTypePattern& type) {
NetworkStateList active_networks;
GetActiveNetworkListByType(type, &active_networks);
for (auto* network : active_networks) {
if (network->IsConnectingState())
return network;
}
return nullptr;
}
const NetworkState* NetworkStateHandler::ActiveNetworkByType(
const NetworkTypePattern& type) {
NetworkStateList active_networks;
GetActiveNetworkListByType(type, &active_networks);
if (active_networks.size() > 0)
return active_networks.front();
return nullptr;
}
const NetworkState* NetworkStateHandler::FirstNetworkByType(
const NetworkTypePattern& type) {
// Sort to ensure visible networks are listed first.
if (!network_list_sorted_)
SortNetworkList(false /* ensure_cellular */);
const NetworkState* first_network = nullptr;
for (auto iter = network_list_.begin(); iter != network_list_.end(); ++iter) {
const NetworkState* network = (*iter)->AsNetworkState();
DCHECK(network);
if (!network->update_received())
continue;
if (!network->visible())
break;
if (network->Matches(type)) {
first_network = network;
break;
}
}
// Active Ethernet networks are the highest priority.
if (first_network && first_network->type() == shill::kTypeEthernet)
return first_network;
const NetworkState* first_tether_network =
type.MatchesPattern(NetworkTypePattern::Tether()) &&
!tether_network_list_.empty()
? tether_network_list_[0]->AsNetworkState()
: nullptr;
// Active Tether networks are next.
if (first_tether_network && first_tether_network->IsConnectingOrConnected())
return first_tether_network;
// Other active networks are next.
if (first_network && first_network->IsConnectingOrConnected())
return first_network;
// Non-active Tether networks are next.
if (first_tether_network)
return first_tether_network;
// Other networks are last.
return first_network;
}
void NetworkStateHandler::SetNetworkConnectRequested(
const std::string& service_path,
bool connect_requested) {
NetworkState* network = GetModifiableNetworkState(service_path);
if (!network)
return;
network->connect_requested_ = connect_requested;
network->shill_connect_error_.clear();
network_list_sorted_ = false;
OnNetworkConnectionStateChanged(network);
}
void NetworkStateHandler::SetShillConnectError(
const std::string& service_path,
const std::string& shill_connect_error) {
NetworkState* network = GetModifiableNetworkState(service_path);
if (!network)
return;
network->shill_connect_error_ = shill_connect_error;
}
void NetworkStateHandler::SetNetworkChromePortalDetected(
const std::string& service_path,
bool portal_detected) {
NetworkState* network = GetModifiableNetworkState(service_path);
if (!network || network->is_chrome_captive_portal_ == portal_detected)
return;
bool was_captive_portal = network->IsCaptivePortal();
network->is_chrome_captive_portal_ = portal_detected;
// Only notify a connection state change if IsCaptivePortal() changed.
if (was_captive_portal == network->IsCaptivePortal())
return;
network_list_sorted_ = false;
OnNetworkConnectionStateChanged(network);
}
std::string NetworkStateHandler::FormattedHardwareAddressForType(
const NetworkTypePattern& type) {
const NetworkState* network = ConnectedNetworkByType(type);
if (network && network->type() == kTypeTether) {
// If this is a Tether network, get the MAC address corresponding to that
// network instead.
network = GetNetworkStateFromGuid(network->tether_guid());
}
const DeviceState* device = network ? GetDeviceState(network->device_path())
: GetDeviceStateByType(type);
if (!device || device->mac_address().empty())
return std::string();
return network_util::FormattedMacAddress(device->mac_address());
}
void NetworkStateHandler::GetVisibleNetworkListByType(
const NetworkTypePattern& type,
NetworkStateList* list) {
GetNetworkListByType(type, false /* configured_only */,
true /* visible_only */, 0 /* no limit */, list);
}
void NetworkStateHandler::GetVisibleNetworkList(NetworkStateList* list) {
GetVisibleNetworkListByType(NetworkTypePattern::Default(), list);
}
void NetworkStateHandler::GetNetworkListByType(const NetworkTypePattern& type,
bool configured_only,
bool visible_only,
size_t limit,
NetworkStateList* list) {
GetNetworkListByTypeImpl(type, configured_only, visible_only,
false /* active_only */, limit, list);
}
void NetworkStateHandler::GetActiveNetworkListByType(
const NetworkTypePattern& type,
NetworkStateList* list) {
GetNetworkListByTypeImpl(type, false /* configured_only */,
false /* visible_only */, true /* active_only */,
0 /* no limit */, list);
}
void NetworkStateHandler::GetNetworkListByTypeImpl(
const NetworkTypePattern& type,
bool configured_only,
bool visible_only,
bool active_only,
size_t limit,
NetworkStateList* list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(list);
list->clear();
// If |limit| is 0, there is no limit. Simplify the calculations below by
// setting it to the maximum size_t value.
if (limit == 0)
limit = std::numeric_limits<size_t>::max();
if (!network_list_sorted_)
SortNetworkList(false /* ensure_cellular */);
// First, add active Tether networks.
if (type.MatchesPattern(NetworkTypePattern::Tether()))
AppendTetherNetworksToList(true /* get_active */, limit, list);
// Second, add active non-Tether networks.
for (const auto& managed : network_list_) {
const NetworkState* network = managed.get()->AsNetworkState();
DCHECK(network);
if (!network->update_received() || !network->Matches(type))
continue;
if (!network->IsActive())
break; // Active networks are listed first.
if (!ShouldIncludeNetworkInList(network, configured_only, visible_only))
continue;
if (network->type() == shill::kTypeEthernet) {
// Ethernet networks should always be in front.
list->insert(list->begin(), network);
} else {
list->push_back(network);
}
if (list->size() >= limit)
return;
}
if (active_only)
return;
// Third, add inactive Tether networks.
if (type.MatchesPattern(NetworkTypePattern::Tether()))
AppendTetherNetworksToList(false /* get_active */, limit, list);
if (list->size() >= limit)
return;
// Fourth, add inactive non-Tether networks.
for (const auto& managed : network_list_) {
const NetworkState* network = managed.get()->AsNetworkState();
DCHECK(network);
if (!network->update_received() || !network->Matches(type))
continue;
if (network->IsActive())
continue;
if (!ShouldIncludeNetworkInList(network, configured_only, visible_only))
continue;
list->push_back(network);
if (list->size() >= limit)
return;
}
}
void NetworkStateHandler::AppendTetherNetworksToList(bool get_active,
size_t limit,
NetworkStateList* list) {
DCHECK(list);
DCHECK_NE(0U, limit);
if (!IsTechnologyEnabled(NetworkTypePattern::Tether()))
return;
for (auto iter = tether_network_list_.begin();
iter != tether_network_list_.end() && list->size() < limit; ++iter) {
const NetworkState* network = (*iter)->AsNetworkState();
DCHECK(network);
if (network->IsConnectingOrConnected() != get_active)
continue;
if (!ShouldIncludeNetworkInList(network, false /* configured_only */,
false /* visible_only */)) {
continue;
}
list->push_back(network);
}
}
const NetworkState* NetworkStateHandler::GetNetworkStateFromServicePath(
const std::string& service_path,
bool configured_only) const {
ManagedState* managed =
GetModifiableManagedState(&network_list_, service_path);
if (!managed) {
managed = GetModifiableManagedState(&tether_network_list_, service_path);
if (!managed)
return nullptr;
}
const NetworkState* network = managed->AsNetworkState();
DCHECK(network);
if (!network->update_received() ||
(configured_only && !network->IsInProfile())) {
return nullptr;
}
return network;
}
const NetworkState* NetworkStateHandler::GetNetworkStateFromGuid(
const std::string& guid) const {
DCHECK(!guid.empty());
return GetModifiableNetworkStateFromGuid(guid);
}
void NetworkStateHandler::AddTetherNetworkState(const std::string& guid,
const std::string& name,
const std::string& carrier,
int battery_percentage,
int signal_strength,
bool has_connected_to_host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!guid.empty());
DCHECK(battery_percentage >= 0 && battery_percentage <= 100);
DCHECK(signal_strength >= 0 && signal_strength <= 100);
if (tether_technology_state_ != TECHNOLOGY_ENABLED) {
NET_LOG(ERROR) << "AddTetherNetworkState() called when Tether networks "
<< "are not enabled. Cannot add NetworkState.";
return;
}
// If the network already exists, do nothing.
if (GetNetworkStateFromGuid(guid)) {
NET_LOG(ERROR) << "AddTetherNetworkState: " << name
<< " called with existing guid:" << guid;
return;
}
// Use the GUID as the network's service path.
std::unique_ptr<NetworkState> tether_network_state =
std::make_unique<NetworkState>(guid /* path */);
tether_network_state->set_name(name);
tether_network_state->set_type(kTypeTether);
tether_network_state->SetGuid(guid);
tether_network_state->set_visible(true);
tether_network_state->set_update_received();
tether_network_state->set_update_requested(false);
tether_network_state->set_connectable(true);
tether_network_state->set_tether_carrier(carrier);
tether_network_state->set_battery_percentage(battery_percentage);
tether_network_state->set_tether_has_connected_to_host(has_connected_to_host);
tether_network_state->set_signal_strength(signal_strength);
tether_network_list_.push_back(std::move(tether_network_state));
network_list_sorted_ = false;
NotifyNetworkListChanged();
}
bool NetworkStateHandler::UpdateTetherNetworkProperties(
const std::string& guid,
const std::string& carrier,
int battery_percentage,
int signal_strength) {
if (tether_technology_state_ != TECHNOLOGY_ENABLED) {
NET_LOG(ERROR) << "UpdateTetherNetworkProperties() called when Tether "
<< "networks are not enabled. Cannot update.";
return false;
}
NetworkState* tether_network_state = GetModifiableNetworkStateFromGuid(guid);
if (!tether_network_state) {
NET_LOG(ERROR) << "UpdateTetherNetworkProperties(): No NetworkState for "
<< "Tether network with GUID \"" << guid << "\".";
return false;
}
tether_network_state->set_tether_carrier(carrier);
tether_network_state->set_battery_percentage(battery_percentage);
tether_network_state->set_signal_strength(signal_strength);
network_list_sorted_ = false;
NotifyNetworkPropertiesUpdated(tether_network_state);
return true;
}
bool NetworkStateHandler::SetTetherNetworkHasConnectedToHost(
const std::string& guid) {
NetworkState* tether_network_state = GetModifiableNetworkStateFromGuid(guid);
if (!tether_network_state) {
NET_LOG(ERROR) << "SetTetherNetworkHasConnectedToHost(): No NetworkState "
<< "for Tether network with GUID \"" << guid << "\".";
return false;
}
if (tether_network_state->tether_has_connected_to_host()) {
return false;
}
tether_network_state->set_tether_has_connected_to_host(true);
network_list_sorted_ = false;
NotifyNetworkPropertiesUpdated(tether_network_state);
return true;
}
bool NetworkStateHandler::RemoveTetherNetworkState(const std::string& guid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!notifying_network_observers_);
for (auto iter = tether_network_list_.begin();
iter != tether_network_list_.end(); ++iter) {
if (iter->get()->AsNetworkState()->guid() == guid) {
const NetworkState* tether_network = iter->get()->AsNetworkState();
bool was_active = tether_network->IsConnectingOrConnected();
NetworkState* wifi_network =
GetModifiableNetworkStateFromGuid(tether_network->tether_guid());
if (wifi_network)
wifi_network->set_tether_guid(std::string());
tether_network_list_.erase(iter);
if (was_active)
NotifyIfActiveNetworksChanged();
NotifyNetworkListChanged();
return true;
}
}
return false;
}
bool NetworkStateHandler::DisassociateTetherNetworkStateFromWifiNetwork(
const std::string& tether_network_guid) {
NetworkState* tether_network_state =
GetModifiableNetworkStateFromGuid(tether_network_guid);
if (!tether_network_state) {
NET_LOG(ERROR) << "DisassociateTetherNetworkStateWithWifiNetwork(): Tether "
<< "network with ID " << tether_network_guid
<< " not registered; could not remove association.";
return false;
}
std::string wifi_network_guid = tether_network_state->tether_guid();
NetworkState* wifi_network_state =
GetModifiableNetworkStateFromGuid(wifi_network_guid);
if (!wifi_network_state) {
NET_LOG(ERROR) << "DisassociateTetherNetworkStateWithWifiNetwork(): Wi-Fi "
<< "network with ID " << wifi_network_guid
<< " not registered; could not remove association.";
return false;
}
if (wifi_network_state->tether_guid().empty() &&
tether_network_state->tether_guid().empty()) {
return true;
}
wifi_network_state->set_tether_guid(std::string());
tether_network_state->set_tether_guid(std::string());
network_list_sorted_ = false;
NotifyNetworkPropertiesUpdated(wifi_network_state);
NotifyNetworkPropertiesUpdated(tether_network_state);
return true;
}
bool NetworkStateHandler::AssociateTetherNetworkStateWithWifiNetwork(
const std::string& tether_network_guid,
const std::string& wifi_network_guid) {
if (tether_technology_state_ != TECHNOLOGY_ENABLED) {
NET_LOG(ERROR) << "AssociateTetherNetworkStateWithWifiNetwork() called "
<< "when Tether networks are not enabled. Cannot "
<< "associate.";
return false;
}
NetworkState* tether_network_state =
GetModifiableNetworkStateFromGuid(tether_network_guid);
if (!tether_network_state) {
NET_LOG(ERROR) << "Tether network does not exist: " << tether_network_guid;
return false;
}
if (!NetworkTypePattern::Tether().MatchesType(tether_network_state->type())) {
NET_LOG(ERROR) << "Network is not a Tether network: "
<< tether_network_guid;
return false;
}
NetworkState* wifi_network_state =
GetModifiableNetworkStateFromGuid(wifi_network_guid);
if (!wifi_network_state) {
NET_LOG(ERROR) << "Wi-Fi Network does not exist: " << wifi_network_guid;
return false;
}
if (!NetworkTypePattern::WiFi().MatchesType(wifi_network_state->type())) {
NET_LOG(ERROR) << "Network is not a W-Fi network: "
<< NetworkId(wifi_network_state);
return false;
}
if (wifi_network_state->tether_guid() == tether_network_guid &&
tether_network_state->tether_guid() == wifi_network_guid) {
return true;
}
tether_network_state->set_tether_guid(wifi_network_guid);
wifi_network_state->set_tether_guid(tether_network_guid);
network_list_sorted_ = false;
NotifyNetworkPropertiesUpdated(wifi_network_state);
NotifyNetworkPropertiesUpdated(tether_network_state);
return true;
}
void NetworkStateHandler::SetTetherNetworkStateDisconnected(
const std::string& guid) {
SetTetherNetworkStateConnectionState(guid, shill::kStateIdle);
}
void NetworkStateHandler::SetTetherNetworkStateConnecting(
const std::string& guid) {
// The default network should only be set if there currently is no default
// network. Otherwise, the default network should not change unless the
// connection completes successfully and the newly-connected network is
// prioritized higher than the existing default network. Note that, in
// general, a connected Ethernet network is still considered the default
// network even if a Tether or Wi-Fi network becomes connected.
if (default_network_path_.empty()) {
NET_LOG(EVENT) << "Connecting to Tether network when there is currently no "
<< "default network; setting as new default network. GUID: "
<< guid;
SetDefaultNetworkValues(guid, /*metered=*/true);
}
SetTetherNetworkStateConnectionState(guid, shill::kStateConfiguration);
}
void NetworkStateHandler::SetTetherNetworkStateConnected(
const std::string& guid) {
// Being connected implies that AssociateTetherNetworkStateWithWifiNetwork()
// was already called, so ensure that the association is still intact.
DCHECK(GetNetworkStateFromGuid(GetNetworkStateFromGuid(guid)->tether_guid())
->tether_guid() == guid);
// At this point, there should be a default network set.
DCHECK(!default_network_path_.empty());
SetTetherNetworkStateConnectionState(guid, shill::kStateOnline);
}
void NetworkStateHandler::SetTetherNetworkStateConnectionState(
const std::string& guid,
const std::string& connection_state) {
NetworkState* tether_network_state = GetModifiableNetworkStateFromGuid(guid);
if (!tether_network_state) {
NET_LOG(ERROR) << "SetTetherNetworkStateConnectionState: Tether network "
<< "not found: " << guid;
return;
}
DCHECK(
NetworkTypePattern::Tether().MatchesType(tether_network_state->type()));
std::string prev_connection_state = tether_network_state->connection_state();
tether_network_state->SetConnectionState(connection_state);
network_list_sorted_ = false;
if (ConnectionStateChanged(tether_network_state, prev_connection_state,
tether_network_state->portal_state())) {
NET_LOG(EVENT) << "Changing connection state for Tether network with GUID "
<< guid << ". Old state: " << prev_connection_state << ", "
<< "New state: " << connection_state;
if (!tether_network_state->IsConnectingOrConnected() &&
tether_network_state->path() == default_network_path_) {
SetDefaultNetworkValues(/*path=*/std::string(), /*metered=*/false);
NotifyDefaultNetworkChanged(kReasonTether);
}
OnNetworkConnectionStateChanged(tether_network_state);
NotifyNetworkPropertiesUpdated(tether_network_state);
}
}
void NetworkStateHandler::EnsureTetherDeviceState() {
bool should_be_present =
tether_technology_state_ != TechnologyState::TECHNOLOGY_UNAVAILABLE;
for (auto it = device_list_.begin(); it < device_list_.end(); ++it) {
std::string path = (*it)->path();
if (path == kTetherDevicePath) {
// If the Tether DeviceState is in the list and it should not be, remove
// it and return. If it is in the list and it should be, the list is
// already valid, so return without removing it.
if (!should_be_present)
device_list_.erase(it);
return;
}
}
if (!should_be_present) {
// If the Tether DeviceState was not in the list and it should not be, the
// list is already valid, so return.
return;
}
// The Tether DeviceState is not present in the list, but it should be. Since
// Tether networks are not recognized by Shill, they will never receive an
// update, so set properties on the state here.
std::unique_ptr<ManagedState> tether_device_state = ManagedState::Create(
ManagedState::ManagedType::MANAGED_TYPE_DEVICE, kTetherDevicePath);
tether_device_state->set_update_received();
tether_device_state->set_update_requested(false);
tether_device_state->set_name(kTetherDeviceName);
tether_device_state->set_type(kTypeTether);
device_list_.push_back(std::move(tether_device_state));
}
bool NetworkStateHandler::UpdateBlockedByPolicy(NetworkState* network) const {
if (!TypeMatches(network, NetworkTypePattern::WiFi()))
return false;
bool prev_blocked_by_policy = network->blocked_by_policy();
bool blocked_by_policy =
!network->IsManagedByPolicy() &&
(OnlyManagedWifiNetworksAllowed() ||
base::Contains(blocked_hex_ssids_, network->GetHexSsid()));
network->set_blocked_by_policy(blocked_by_policy);
return prev_blocked_by_policy != blocked_by_policy;
}
void NetworkStateHandler::UpdateManagedWifiNetworkAvailable() {
DeviceState* device =
GetModifiableDeviceStateByType(NetworkTypePattern::WiFi());
if (!device || !device->update_received())
return; // May be null in tests.
const std::string prev_available_managed_network_path =
device->available_managed_network_path();
std::string available_managed_network_path;
NetworkStateHandler::NetworkStateList networks;
GetNetworkListByType(NetworkTypePattern::WiFi(), true, true, 0, &networks);
for (const NetworkState* network : networks) {
if (network->IsManagedByPolicy()) {
available_managed_network_path = network->path();
break;
}
}
if (prev_available_managed_network_path != available_managed_network_path) {
device->set_available_managed_network_path(available_managed_network_path);
UpdateBlockedWifiNetworksInternal();
NotifyDevicePropertiesUpdated(device);
}
}
void NetworkStateHandler::UpdateBlockedWifiNetworksInternal() {
for (auto iter = network_list_.begin(); iter != network_list_.end(); ++iter) {
NetworkState* network = (*iter)->AsNetworkState();
if (!network->Matches(NetworkTypePattern::WiFi()))
continue;
if (UpdateBlockedByPolicy(network))
NotifyNetworkPropertiesUpdated(network);
}
}
void NetworkStateHandler::GetDeviceList(DeviceStateList* list) const {
GetDeviceListByType(NetworkTypePattern::Default(), list);
}
void NetworkStateHandler::GetDeviceListByType(const NetworkTypePattern& type,
DeviceStateList* list) const {
DCHECK(list);
list->clear();
for (auto iter = device_list_.begin(); iter != device_list_.end(); ++iter) {
const DeviceState* device = (*iter)->AsDeviceState();
DCHECK(device);
if (device->update_received() && device->Matches(type))
list->push_back(device);
}
}
void NetworkStateHandler::RequestScan(const NetworkTypePattern& type) {
NET_LOG(USER) << "RequestScan: " << type.ToDebugString();
if (type.MatchesPattern(NetworkTypePattern::WiFi())) {
if (IsTechnologyEnabled(NetworkTypePattern::WiFi()))
shill_property_handler_->RequestScanByType(shill::kTypeWifi);
else if (type.Equals(NetworkTypePattern::WiFi()))
return; // Skip notify if disabled and wifi only requested.
}
if (type.Equals(NetworkTypePattern::Cellular())) {
// Only request a Cellular scan if Cellular is requested explicitly.
if (IsTechnologyEnabled(NetworkTypePattern::Cellular()))
shill_property_handler_->RequestScanByType(shill::kTypeCellular);
else
return; // Skip notify if disabled and cellular only requested.
}
// Note: for Tether we initiate the scan in the observer.
NotifyScanRequested(type);
}
void NetworkStateHandler::RequestUpdateForNetwork(
const std::string& service_path) {
NetworkState* network = GetModifiableNetworkState(service_path);
if (network) {
// Tether networks are not managed by Shill; do not request properties.
if (network->type() == kTypeTether)
return;
// Do not request properties if a condition has already triggered a request.
if (network->update_requested())
return;
network->set_update_requested(true);
NET_LOG(EVENT) << "RequestUpdate for: " << NetworkId(network);
} else {
NET_LOG(EVENT) << "RequestUpdate for: " << NetworkPathId(service_path);
}
shill_property_handler_->RequestProperties(ManagedState::MANAGED_TYPE_NETWORK,
service_path);
}
void NetworkStateHandler::SendUpdateNotificationForNetwork(
const std::string& service_path) {
const NetworkState* network = GetNetworkState(service_path);
if (!network)
return;
NotifyNetworkPropertiesUpdated(network);
}
void NetworkStateHandler::ClearLastErrorForNetwork(
const std::string& service_path) {
NetworkState* network = GetModifiableNetworkState(service_path);
if (network)
network->ClearError();
}
void NetworkStateHandler::SetCheckPortalList(
const std::string& check_portal_list) {
NET_LOG(EVENT) << "SetCheckPortalList: " << check_portal_list;
shill_property_handler_->SetCheckPortalList(check_portal_list);
}
void NetworkStateHandler::SetWakeOnLanEnabled(bool enabled) {
NET_LOG(EVENT) << "SetWakeOnLanEnabled: " << enabled;
shill_property_handler_->SetWakeOnLanEnabled(enabled);
}
void NetworkStateHandler::SetHostname(const std::string& hostname) {
NET_LOG(EVENT) << "SetHostname: " << hostname;
shill_property_handler_->SetHostname(hostname);
}
void NetworkStateHandler::SetNetworkThrottlingStatus(
bool enabled,
uint32_t upload_rate_kbits,
uint32_t download_rate_kbits) {
if (enabled) {
NET_LOG(EVENT) << "SetNetworkThrottlingStatus: Enabled: "
<< upload_rate_kbits << ", " << download_rate_kbits;
} else {
NET_LOG(EVENT) << "SetNetworkThrottlingStatus: Disabled.";
}
shill_property_handler_->SetNetworkThrottlingStatus(
enabled, upload_rate_kbits, download_rate_kbits);
}
void NetworkStateHandler::SetFastTransitionStatus(bool enabled) {
NET_LOG(USER) << "SetFastTransitionStatus: " << enabled;
shill_property_handler_->SetFastTransitionStatus(enabled);
}
const NetworkState* NetworkStateHandler::GetEAPForEthernet(
const std::string& service_path,
bool connected_only) {
const NetworkState* network = GetNetworkState(service_path);
if (!network) {
NET_LOG(ERROR) << "GetEAPForEthernet: Unknown service: "
<< NetworkPathId(service_path);
return nullptr;
}
if (network->type() != shill::kTypeEthernet) {
NET_LOG(ERROR) << "GetEAPForEthernet: Not Ethernet: " << NetworkId(network);
return nullptr;
}
if (connected_only) {
if (!network->IsConnectedState()) {
NET_LOG(DEBUG) << "GetEAPForEthernet: Not connected.";
return nullptr;
}
// The same EAP service is shared for all ethernet services/devices.
// However EAP is used/enabled per device and only if the connection was
// successfully established.
const DeviceState* device = GetDeviceState(network->device_path());
if (!device) {
NET_LOG(ERROR) << "GetEAPForEthernet: Unknown device "
<< network->device_path()
<< " for connected ethernet service: "
<< NetworkId(network);
return nullptr;
}
if (!device->eap_authentication_completed()) {
NET_LOG(DEBUG) << "GetEAPForEthernet: EAP Authenticaiton not completed.";
return nullptr;
}
}
NetworkStateList list;
GetNetworkListByType(NetworkTypePattern::Primitive(shill::kTypeEthernetEap),
true /* configured_only */, false /* visible_only */,
1 /* limit */, &list);
if (list.empty()) {
if (connected_only) {
NET_LOG(ERROR)
<< "GetEAPForEthernet: Connected using EAP but no EAP service found: "
<< NetworkId(network);
}
return nullptr;
}
return list.front();
}
void NetworkStateHandler::SetErrorForTest(const std::string& service_path,
const std::string& error) {
NetworkState* network_state = GetModifiableNetworkState(service_path);
if (!network_state) {
NET_LOG(ERROR) << "No matching NetworkState for: "
<< NetworkPathId(service_path);
return;
}
network_state->last_error_ = error;
}
void NetworkStateHandler::SetDeviceStateUpdatedForTest(
const std::string& device_path) {
DeviceState* device = GetModifiableDeviceState(device_path);
DCHECK(device);
device->set_update_received();
}
//------------------------------------------------------------------------------
// ShillPropertyHandler::Delegate overrides
void NetworkStateHandler::UpdateManagedList(ManagedState::ManagedType type,
const base::ListValue& entries) {
CHECK(!notifying_network_observers_);
ManagedStateList* managed_list = GetManagedList(type);
NET_LOG(DEBUG) << "UpdateManagedList: " << ManagedState::TypeToString(type)
<< ": " << entries.GetSize();
// Create a map of existing entries. Assumes all entries in |managed_list|
// are unique.
std::map<std::string, std::unique_ptr<ManagedState>> managed_map;
for (auto& item : *managed_list) {
std::string path = item->path();
DCHECK(!base::Contains(managed_map, path));
managed_map[path] = std::move(item);
}
// Clear the list (objects are temporarily owned by managed_map).
managed_list->clear();
// Updates managed_list and request updates for new entries.
std::set<std::string> list_entries;
for (auto& iter : entries) {
std::string path;
iter.GetAsString(&path);
if (path.empty() || path == shill::kFlimflamServicePath) {
NET_LOG(ERROR) << "Bad path in type " << type << " Path: " << path;
continue;
}
auto found = managed_map.find(path);
if (found == managed_map.end()) {
if (list_entries.count(path) != 0) {
NET_LOG(ERROR) << "Duplicate entry in list for " << path;
continue;
}
managed_list->push_back(ManagedState::Create(type, path));
} else {
managed_list->push_back(std::move(found->second));
managed_map.erase(found);
}
list_entries.insert(path);
}
if (type == ManagedState::ManagedType::MANAGED_TYPE_DEVICE) {
// Also move the Tether DeviceState if it exists. This will not happen as
// part of the loop above since |entries| will never contain the Tether
// path.
auto iter = managed_map.find(kTetherDevicePath);
if (iter != managed_map.end()) {
managed_list->push_back(std::move(iter->second));
managed_map.erase(iter);
}
}
UpdateManagedWifiNetworkAvailable();
if (type != ManagedState::ManagedType::MANAGED_TYPE_NETWORK)
return;
// Remove associations Tether NetworkStates had with now removed Wi-Fi
// NetworkStates.
for (auto& iter : managed_map) {
ManagedState* managed = iter.second.get();
if (!TypeMatches(managed, NetworkTypePattern::WiFi()))
continue;
NetworkState* tether_network = GetModifiableNetworkStateFromGuid(
managed->AsNetworkState()->tether_guid());
if (tether_network)
tether_network->set_tether_guid(std::string());
}
}
void NetworkStateHandler::ProfileListChanged(const base::Value& profile_list) {
NET_LOG(EVENT) << "ProfileListChanged. Re-Requesting Network Properties";
ProcessIsUserLoggedIn(profile_list);
for (ManagedStateList::iterator iter = network_list_.begin();
iter != network_list_.end(); ++iter) {
const NetworkState* network = (*iter)->AsNetworkState();
DCHECK(network);
shill_property_handler_->RequestProperties(
ManagedState::MANAGED_TYPE_NETWORK, network->path());
}
}
void NetworkStateHandler::UpdateManagedStateProperties(
ManagedState::ManagedType type,
const std::string& path,
const base::Value& properties) {
ManagedStateList* managed_list = GetManagedList(type);
ManagedState* managed = GetModifiableManagedState(managed_list, path);
if (!managed) {
// The network has been removed from the list of networks.
NET_LOG(DEBUG) << "UpdateManagedStateProperties: Not found: " << path;
return;
}
managed->set_update_received();
NET_LOG(DEBUG) << GetManagedStateLogType(managed)
<< " Properties Received: " << GetLogName(managed);
if (type == ManagedState::MANAGED_TYPE_NETWORK) {
UpdateNetworkStateProperties(managed->AsNetworkState(), properties);
} else {
// Device
for (const auto iter : properties.DictItems())
managed->PropertyChanged(iter.first, iter.second);
managed->InitialPropertiesReceived(properties);
}
managed->set_update_requested(false);
}
void NetworkStateHandler::UpdateNetworkStateProperties(
NetworkState* network,
const base::Value& properties) {
DCHECK(network);
bool network_property_updated = false;
std::string prev_connection_state = network->connection_state();
NetworkState::PortalState prev_portal_state = network->portal_state();
bool metered = false;
for (const auto iter : properties.DictItems()) {
if (network->PropertyChanged(iter.first, iter.second))
network_property_updated = true;
if (iter.first == shill::kMeteredProperty)
metered = iter.second.is_bool() && iter.second.GetBool();
}
if (network->path() == default_network_path_)
default_network_is_metered_ = metered && network->IsConnectedState();
if (network->Matches(NetworkTypePattern::WiFi()))
network_property_updated |= UpdateBlockedByPolicy(network);
network_property_updated |= network->InitialPropertiesReceived(properties);
UpdateGuid(network);
if (network->Matches(NetworkTypePattern::Cellular()))
UpdateCellularStateFromDevice(network);
network_list_sorted_ = false;
// Notify observers of NetworkState changes.
if (network_property_updated || network->update_requested()) {
// Signal connection state changed after all properties have been updated.
if (ConnectionStateChanged(network, prev_connection_state,
prev_portal_state)) {
// Also notifies that the default network changed if this is the default.
OnNetworkConnectionStateChanged(network);
} else if (network->path() == default_network_path_ &&
network->IsActive()) {
// Always notify that the default network changed for a complete update.
NET_LOG(DEBUG) << "UpdateNetworkStateProperties for default: "
<< NetworkId(network);
NotifyDefaultNetworkChanged(kReasonUpdate);
}
NotifyNetworkPropertiesUpdated(network);
}
}
void NetworkStateHandler::UpdateNetworkServiceProperty(
const std::string& service_path,
const std::string& key,
const base::Value& value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
SCOPED_NET_LOG_IF_SLOW();
bool changed = false;
NetworkState* network = GetModifiableNetworkState(service_path);
if (!network || !network->update_received()) {
// Shill may send a service property update before processing Chrome's
// initial GetProperties request. If this occurs, the initial request will
// include the changed property value so we can ignore this update.
return;
}
std::string prev_connection_state = network->connection_state();
NetworkState::PortalState prev_portal_state = network->portal_state();
std::string prev_profile_path = network->profile_path();
changed |= network->PropertyChanged(key, value);
changed |= UpdateBlockedByPolicy(network);
if (!changed)
return;
// If added or removed from a Profile, request a full update so that a
// NetworkState gets created.
bool request_update = prev_profile_path != network->profile_path();
bool sort_networks = false;
bool notify_default = network->path() == default_network_path_;
bool notify_connection_state = false;
bool notify_active = false;
if (key == shill::kStateProperty || key == shill::kVisibleProperty) {
network_list_sorted_ = false;
if (ConnectionStateChanged(network, prev_connection_state,
prev_portal_state)) {
notify_connection_state = true;
notify_active = true;
if (notify_default)
notify_default = VerifyDefaultNetworkConnectionStateChange(network);
// If the default network connection state changed, sort networks now
// and ensure that a default cellular network exists.
if (notify_default)
sort_networks = true;
// If the connection state changes, other properties such as IPConfig
// may have changed, so request a full update.
request_update = true;
}
} else if (key == shill::kActivationStateProperty) {
// Activation state may affect "connecting" state in the UI.
notify_connection_state = true;
network_list_sorted_ = false;
}
if (request_update) {
RequestUpdateForNetwork(service_path);
notify_default = false; // Notify will occur when properties are received.
}
std::string value_str;
value.GetAsString(&value_str);
if (key == shill::kSignalStrengthProperty || key == shill::kWifiBSsid ||
key == shill::kWifiFrequency ||
key == shill::kWifiFrequencyListProperty ||
key == shill::kNetworkTechnologyProperty ||
(key == shill::kDeviceProperty && value_str == "/")) {
// Uninteresting update. This includes 'Device' property changes to "/"
// (occurs just before a service is removed).
// For non active networks do not log or send any notifications.
if (!network->IsActive())
return;
// Otherwise do not trigger 'default network changed'.
notify_default = false;
// Notify signal strength and network technology changes for active
// networks.
if (key == shill::kSignalStrengthProperty ||
key == shill::kNetworkTechnologyProperty) {
notify_active = true;
}
}
LogPropertyUpdated(network, key, value);
if (notify_connection_state)
NotifyNetworkConnectionStateChanged(network);
if (notify_default) {
std::stringstream logstream;
logstream << std::string(kReasonUpdate) << ":" << key << "=" << value;
NotifyDefaultNetworkChanged(logstream.str());
}
if (notify_active)
NotifyIfActiveNetworksChanged();
NotifyNetworkPropertiesUpdated(network);
if (sort_networks)
SortNetworkList(true /* ensure_cellular */);
}
void NetworkStateHandler::UpdateDeviceProperty(const std::string& device_path,
const std::string& key,
const base::Value& value) {
SCOPED_NET_LOG_IF_SLOW();
DeviceState* device = GetModifiableDeviceState(device_path);
if (!device || !device->update_received()) {
// Shill may send a device property update before processing Chrome's
// initial GetProperties request. If this occurs, the initial request will
// include the changed property value so we can ignore this update.
return;
}
if (!device->PropertyChanged(key, value))
return;
LogPropertyUpdated(device, key, value);
NotifyDevicePropertiesUpdated(device);
if (key == shill::kScanningProperty && device->scanning() == false) {
if (device->type() == shill::kTypeWifi)
UpdateManagedWifiNetworkAvailable();
NotifyScanCompleted(device);
}
if (key == shill::kEapAuthenticationCompletedProperty) {
// Notify a change for each Ethernet service using this device.
NetworkStateList ethernet_services;
GetNetworkListByType(NetworkTypePattern::Ethernet(),
false /* configured_only */, false /* visible_only */,
0 /* no limit */, &ethernet_services);
for (NetworkStateList::const_iterator it = ethernet_services.begin();
it != ethernet_services.end(); ++it) {
const NetworkState* ethernet_service = *it;
if (ethernet_service->update_received() ||
ethernet_service->device_path() != device->path()) {
continue;
}
RequestUpdateForNetwork(ethernet_service->path());
}
}
}
void NetworkStateHandler::UpdateIPConfigProperties(
ManagedState::ManagedType type,
const std::string& path,
const std::string& ip_config_path,
const base::Value& properties) {
if (type == ManagedState::MANAGED_TYPE_NETWORK) {
NetworkState* network = GetModifiableNetworkState(path);
if (!network)
return;
network->IPConfigPropertiesChanged(properties);
NotifyNetworkPropertiesUpdated(network);
if (network->path() == default_network_path_)
NotifyDefaultNetworkChanged(kReasonUpdateIPConfig);
if (network->IsActive())
NotifyIfActiveNetworksChanged();
} else if (type == ManagedState::MANAGED_TYPE_DEVICE) {
DeviceState* device = GetModifiableDeviceState(path);
if (!device)
return;
device->IPConfigPropertiesChanged(ip_config_path, properties);
NotifyDevicePropertiesUpdated(device);
if (!default_network_path_.empty()) {
const NetworkState* default_network =
GetNetworkState(default_network_path_);
if (default_network && default_network->device_path() == path) {
NotifyNetworkPropertiesUpdated(default_network);
NotifyDefaultNetworkChanged(kReasonUpdateDeviceIPConfig);
}
}
}
}
void NetworkStateHandler::CheckPortalListChanged(
const std::string& check_portal_list) {
check_portal_list_ = check_portal_list;
}
void NetworkStateHandler::HostnameChanged(const std::string& hostname) {
NET_LOG(EVENT) << "HostnameChanged: " << hostname;
hostname_ = hostname;
for (auto& observer : observers_)
observer.HostnameChanged(hostname);
}
void NetworkStateHandler::TechnologyListChanged() {
// Eventually we would like to replace Technology state with Device state.
// For now, treat technology state changes as device list changes.
NotifyDeviceListChanged();
}
void NetworkStateHandler::ManagedStateListChanged(
ManagedState::ManagedType type) {
SCOPED_NET_LOG_IF_SLOW();
switch (type) {
case ManagedState::MANAGED_TYPE_NETWORK:
SortNetworkList(true /* ensure_cellular */);
UpdateNetworkStats();
NotifyIfActiveNetworksChanged();
NotifyNetworkListChanged();
UpdateManagedWifiNetworkAvailable();
// ManagedStateListChanged only gets executed if all pending updates have
// completed. Profile networks are loaded if a user is logged in and all
// pending updates are complete.
is_profile_networks_loaded_ = is_user_logged_in_;
return;
case ManagedState::MANAGED_TYPE_DEVICE:
std::string devices;
for (auto iter = device_list_.begin(); iter != device_list_.end();
++iter) {
if (iter != device_list_.begin())
devices += ", ";
devices += (*iter)->name();
}
NET_LOG(EVENT) << "DeviceList: " << devices;
// A change to the device list may affect the default Cellular network, so
// call SortNetworkList here.
SortNetworkList(true /* ensure_cellular */);
NotifyDeviceListChanged();
return;
}
NOTREACHED();
}
void NetworkStateHandler::SortNetworkList(bool ensure_cellular) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (tether_sort_delegate_)
tether_sort_delegate_->SortTetherNetworkList(&tether_network_list_);
// Note: usually active networks will precede inactive networks, however
// this may briefly be untrue during state transitions (e.g. a network may
// transition to idle before the list is updated). Also separate inactive
// Mobile and VPN networks (see below).
ManagedStateList active, non_wifi_visible, wifi_visible, hidden, new_networks;
int cellular_count = 0;
bool have_default_cellular = false;
for (ManagedStateList::iterator iter = network_list_.begin();
iter != network_list_.end(); ++iter) {
NetworkState* network = (*iter)->AsNetworkState();
// NetworkState entries are created when they appear in the list, but the
// details are not populated until an update is received.
if (!network->update_received()) {
new_networks.push_back(std::move(*iter));
continue;
}
if (NetworkTypePattern::Cellular().MatchesType(network->type())) {
++cellular_count;
if ((*iter)->AsNetworkState()->IsDefaultCellular())
have_default_cellular = true;
}
if (network->IsActive()) {
active.push_back(std::move(*iter));
continue;
}
if (!network->visible()) {
hidden.push_back(std::move(*iter));
continue;
}
if (NetworkTypePattern::WiFi().MatchesType(network->type()))
wifi_visible.push_back(std::move(*iter));
else
non_wifi_visible.push_back(std::move(*iter));
}
// List active networks first (will always include Ethernet).
network_list_ = std::move(active);
// If a default Cellular network is required, add it next.
if (ensure_cellular && cellular_count == 0) {
std::unique_ptr<NetworkState> default_cellular =
MaybeCreateDefaultCellularNetwork();
if (default_cellular)
network_list_.push_back(std::move(default_cellular));
}
// List non wifi visible networks next (Mobile and VPN).
std::move(non_wifi_visible.begin(), non_wifi_visible.end(),
std::back_inserter(network_list_));
// List WiFi networks last.
std::move(wifi_visible.begin(), wifi_visible.end(),
std::back_inserter(network_list_));
// Include hidden and new networks in the list at the end; they should not
// be shown by the UI.
std::move(hidden.begin(), hidden.end(), std::back_inserter(network_list_));
std::move(new_networks.begin(), new_networks.end(),
std::back_inserter(network_list_));
network_list_sorted_ = true;
if (ensure_cellular && have_default_cellular) {
// If we have created a default Cellular NetworkState, and we have > 1
// Cellular NetworkState or no Cellular device, remove it.
if (cellular_count > 1 ||
!GetDeviceStateByType(NetworkTypePattern::Cellular())) {
RemoveDefaultCellularNetwork();
}
}
}
void NetworkStateHandler::UpdateNetworkStats() {
size_t shared = 0, unshared = 0, visible = 0;
for (ManagedStateList::iterator iter = network_list_.begin();
iter != network_list_.end(); ++iter) {
const NetworkState* network = (*iter)->AsNetworkState();
if (network->visible())
++visible;
if (network->IsInProfile()) {
if (network->IsPrivate())
++unshared;
else
++shared;
}
}
UMA_HISTOGRAM_COUNTS_100("Networks.Visible", visible);
UMA_HISTOGRAM_COUNTS_100("Networks.RememberedShared", shared);
UMA_HISTOGRAM_COUNTS_100("Networks.RememberedUnshared", unshared);
}
void NetworkStateHandler::DefaultNetworkServiceChanged(
const std::string& service_path) {
// Shill uses '/' for empty service path values; check explicitly for that.
const char kEmptyServicePath[] = "/";
std::string new_service_path =
(service_path != kEmptyServicePath) ? service_path : "";
if (new_service_path == default_network_path_)
return;
if (new_service_path.empty()) {
// If Shill reports that there is no longer a default network but there is
// still an active Tether connection corresponding to the default network,
// return early without changing |default_network_path_|. Observers will be
// notified of the default network change due to a subsequent call to
// SetTetherNetworkStateDisconnected().
const NetworkState* old_default_network = DefaultNetwork();
if (old_default_network && old_default_network->type() == kTypeTether)
return;
}
NET_LOG(EVENT) << "DefaultNetworkServiceChanged: "
<< NetworkPathId(service_path);
if (new_service_path.empty()) {
// Notify that there is no default network.
SetDefaultNetworkValues(/*path=*/std::string(), /*metered=*/false);
NotifyDefaultNetworkChanged(kReasonChange);
return;
}
const NetworkState* network = GetNetworkState(service_path);
if (!network) {
// If NetworkState is not available yet, do not notify observers here,
// they will be notified when the state is received.
NET_LOG(EVENT) << "Default NetworkState not available: "
<< NetworkPathId(service_path);
// Metered will be updated to the correct value when properties arrive.
SetDefaultNetworkValues(service_path, /*metered=*/false);
return;
}
if (!network->tether_guid().empty()) {
DCHECK(network->type() == shill::kTypeWifi);
// If the new default network from Shill's point of view is a Wi-Fi
// network which corresponds to a hotspot for a Tether network, set the
// default network to be the associated Tether network instead.
network = GetNetworkStateFromGuid(network->tether_guid());
if (default_network_path_ != network->path()) {
NET_LOG(DEBUG) << "Tether network is default: " << NetworkId(network);
SetDefaultNetworkValues(network->path(), /*metered=*/true);
NotifyDefaultNetworkChanged(kReasonChange);
}
return;
}
// Request the updated default network properties which will trigger
// NotifyDefaultNetworkChanged().
// Metered will be updated to the correct value when properties arrive.
SetDefaultNetworkValues(service_path, /*metered=*/false);
RequestUpdateForNetwork(service_path);
}
//------------------------------------------------------------------------------
// Private methods
void NetworkStateHandler::UpdateGuid(NetworkState* network) {
std::string specifier = network->GetSpecifier();
DCHECK(!specifier.empty());
if (!network->guid().empty()) {
// If the network is saved in a profile, remove the entry from the map.
// Otherwise ensure that the entry matches the specified GUID. (e.g. in
// case a visible network with a specified guid gets configured with a
// new guid). Exception: Ethernet and Cellular expect to have a single
// network and a consistent GUID.
if (network->type() != shill::kTypeEthernet &&
network->type() != shill::kTypeCellular && network->IsInProfile()) {
specifier_guid_map_.erase(specifier);
} else {
specifier_guid_map_[specifier] = network->guid();
}
return;
}
// Ensure that the NetworkState has a valid GUID.
std::string guid;
SpecifierGuidMap::iterator guid_iter = specifier_guid_map_.find(specifier);
if (guid_iter != specifier_guid_map_.end()) {
guid = guid_iter->second;
} else {
guid = base::GenerateGUID();
specifier_guid_map_[specifier] = guid;
}
network->SetGuid(guid);
}
void NetworkStateHandler::UpdateCellularStateFromDevice(NetworkState* network) {
const DeviceState* device = GetDeviceState(network->device_path());
if (!device)
return;
network->provider_requires_roaming_ = device->provider_requires_roaming();
}
std::unique_ptr<NetworkState>
NetworkStateHandler::MaybeCreateDefaultCellularNetwork() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(!notifying_network_observers_);
const DeviceState* device =
GetDeviceStateByType(NetworkTypePattern::Cellular());
// If no SIM is present there will not be useful user facing Device
// information, so do not create a default Cellular network.
if (!device || device->IsSimAbsent())
return nullptr;
// Create a default Cellular network. Properties from the associated Device
// will be provided to the UI. Note that the network's name is left empty; UI
// surfaces which attempt to show the network name will fall back to showing
// the network type (i.e., "Cellular") instead.
std::unique_ptr<NetworkState> network =
NetworkState::CreateDefaultCellular(device->path());
UpdateGuid(network.get());
return network;
}
void NetworkStateHandler::RemoveDefaultCellularNetwork() {
for (auto iter = network_list_.begin(); iter != network_list_.end(); ++iter) {
if ((*iter)->AsNetworkState()->IsDefaultCellular()) {
network_list_.erase(iter);
return; // There will only ever be one default Cellular network.
}
}
}
void NetworkStateHandler::NotifyNetworkListChanged() {
NET_LOG(EVENT) << "NOTIFY: NetworkListChanged. Size: "
<< network_list_.size();
for (auto& observer : observers_)
observer.NetworkListChanged();
}
void NetworkStateHandler::NotifyDeviceListChanged() {
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: DeviceListChanged. Size: " << device_list_.size();
for (auto& observer : observers_)
observer.DeviceListChanged();
}
DeviceState* NetworkStateHandler::GetModifiableDeviceState(
const std::string& device_path) const {
ManagedState* managed = GetModifiableManagedState(&device_list_, device_path);
if (!managed)
return nullptr;
return managed->AsDeviceState();
}
DeviceState* NetworkStateHandler::GetModifiableDeviceStateByType(
const NetworkTypePattern& type) const {
for (const auto& device : device_list_) {
if (TypeMatches(device.get(), type))
return device->AsDeviceState();
}
return nullptr;
}
NetworkState* NetworkStateHandler::GetModifiableNetworkState(
const std::string& service_path) const {
ManagedState* managed =
GetModifiableManagedState(&network_list_, service_path);
if (!managed) {
managed = GetModifiableManagedState(&tether_network_list_, service_path);
if (!managed)
return nullptr;
}
return managed->AsNetworkState();
}
NetworkState* NetworkStateHandler::GetModifiableNetworkStateFromGuid(
const std::string& guid) const {
for (auto iter = tether_network_list_.begin();
iter != tether_network_list_.end(); ++iter) {
NetworkState* tether_network = (*iter)->AsNetworkState();
if (tether_network->guid() == guid)
return tether_network;
}
for (auto iter = network_list_.begin(); iter != network_list_.end(); ++iter) {
NetworkState* network = (*iter)->AsNetworkState();
if (network->guid() == guid)
return network;
}
return nullptr;
}
ManagedState* NetworkStateHandler::GetModifiableManagedState(
const ManagedStateList* managed_list,
const std::string& path) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto iter = managed_list->begin(); iter != managed_list->end(); ++iter) {
ManagedState* managed = iter->get();
if (managed->path() == path)
return managed;
}
return nullptr;
}
NetworkStateHandler::ManagedStateList* NetworkStateHandler::GetManagedList(
ManagedState::ManagedType type) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (type) {
case ManagedState::MANAGED_TYPE_NETWORK:
return &network_list_;
case ManagedState::MANAGED_TYPE_DEVICE:
return &device_list_;
}
NOTREACHED();
return nullptr;
}
void NetworkStateHandler::OnNetworkConnectionStateChanged(
NetworkState* network) {
DCHECK(network);
bool default_changed = false;
if (network->path() == default_network_path_)
default_changed = VerifyDefaultNetworkConnectionStateChange(network);
NotifyNetworkConnectionStateChanged(network);
if (default_changed)
NotifyDefaultNetworkChanged(kReasonStateChange);
}
bool NetworkStateHandler::VerifyDefaultNetworkConnectionStateChange(
NetworkState* network) {
DCHECK(network->path() == default_network_path_);
if (network->IsConnectedState() ||
NetworkState::StateIsPortalled(network->connection_state())) {
return true;
}
if (network->IsConnectingState()) {
// Wait until the network is actually connected to notify that the default
// network changed.
NET_LOG(DEBUG) << "Default network is connecting: " << NetworkId(network)
<< "State: " << network->connection_state();
return false;
}
NET_LOG(DEBUG) << "Default network not connected: " << NetworkId(network);
return false;
}
void NetworkStateHandler::NotifyNetworkConnectionStateChanged(
NetworkState* network) {
DCHECK(network);
SCOPED_NET_LOG_IF_SLOW();
std::string desc = "NetworkConnectionStateChanged";
if (network->path() == default_network_path_)
desc = "Default" + desc;
NET_LOG(EVENT) << "NOTIFY: " << desc << ": " << NetworkId(network) << ": "
<< network->connection_state();
notifying_network_observers_ = true;
for (auto& observer : observers_)
observer.NetworkConnectionStateChanged(network);
notifying_network_observers_ = false;
NotifyIfActiveNetworksChanged();
}
void NetworkStateHandler::NotifyDefaultNetworkChanged(
const std::string& log_reason) {
SCOPED_NET_LOG_IF_SLOW();
// If the default network is in an invalid state, |default_network_path_|
// will be cleared; call DefaultNetworkChanged(nullptr).
const NetworkState* default_network;
if (default_network_path_.empty()) {
default_network = nullptr;
} else {
default_network = GetModifiableNetworkState(default_network_path_);
DCHECK(default_network) << "No default network: " << default_network_path_;
}
NET_LOG(EVENT) << "NOTIFY: DefaultNetworkChanged: "
<< NetworkId(default_network) << ": " << log_reason;
notifying_network_observers_ = true;
for (auto& observer : observers_)
observer.DefaultNetworkChanged(default_network);
if (default_network &&
(default_network->portal_state() != default_network_portal_state_ ||
default_network->proxy_config() != default_network_proxy_config_)) {
default_network_portal_state_ = default_network->portal_state();
default_network_proxy_config_ = default_network->proxy_config().Clone();
for (auto& observer : observers_) {
observer.PortalStateChanged(default_network,
default_network_portal_state_);
}
} else if (!default_network && (default_network_portal_state_ !=
NetworkState::PortalState::kUnknown ||
!default_network_proxy_config_.is_none())) {
default_network_portal_state_ = NetworkState::PortalState::kUnknown;
default_network_proxy_config_ = base::Value();
for (auto& observer : observers_)
observer.PortalStateChanged(nullptr, NetworkState::PortalState::kUnknown);
}
notifying_network_observers_ = false;
}
bool NetworkStateHandler::ActiveNetworksChanged(
const NetworkStateList& active_networks) {
if (active_networks.size() != active_network_list_.size())
return true;
for (size_t i = 0; i < active_network_list_.size(); ++i) {
if (!active_network_list_[i].MatchesNetworkState(active_networks[i]))
return true;
}
return false;
}
void NetworkStateHandler::NotifyIfActiveNetworksChanged() {
SCOPED_NET_LOG_IF_SLOW();
NetworkStateList active_networks;
GetActiveNetworkListByType(NetworkTypePattern::Default(), &active_networks);
if (!ActiveNetworksChanged(active_networks))
return;
NET_LOG(EVENT) << "NOTIFY:ActiveNetworksChanged";
active_network_list_.clear();
active_network_list_.reserve(active_networks.size());
for (const NetworkState* network : active_networks)
active_network_list_.emplace_back(network);
notifying_network_observers_ = true;
for (auto& observer : observers_)
observer.ActiveNetworksChanged(active_networks);
notifying_network_observers_ = false;
}
void NetworkStateHandler::NotifyNetworkPropertiesUpdated(
const NetworkState* network) {
// Skip property updates before NetworkState::InitialPropertiesReceived.
if (network->type().empty())
return;
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: NetworkPropertiesUpdated: " << NetworkId(network);
notifying_network_observers_ = true;
for (auto& observer : observers_)
observer.NetworkPropertiesUpdated(network);
notifying_network_observers_ = false;
}
void NetworkStateHandler::NotifyDevicePropertiesUpdated(
const DeviceState* device) {
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: DevicePropertiesUpdated: " << device->path();
for (auto& observer : observers_)
observer.DevicePropertiesUpdated(device);
}
void NetworkStateHandler::NotifyScanRequested(const NetworkTypePattern& type) {
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: ScanRequested";
for (auto& observer : observers_)
observer.ScanRequested(type);
}
void NetworkStateHandler::NotifyScanCompleted(const DeviceState* device) {
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: ScanCompleted for: " << device->path();
for (auto& observer : observers_)
observer.ScanCompleted(device);
}
void NetworkStateHandler::NotifyScanStarted(const DeviceState* device) {
SCOPED_NET_LOG_IF_SLOW();
NET_LOG(EVENT) << "NOTIFY: ScanStarted for: " << device->path();
for (auto& observer : observers_)
observer.ScanStarted(device);
}
void NetworkStateHandler::LogPropertyUpdated(const ManagedState* state,
const std::string& key,
const base::Value& value) {
std::string type_str =
state->managed_type() == ManagedState::MANAGED_TYPE_DEVICE
? "Device"
: state->path() == default_network_path_ ? "DefaultNetwork"
: "Network";
device_event_log::LogLevel log_level = device_event_log::LOG_LEVEL_EVENT;
if (key == shill::kSignalStrengthProperty && !state->IsActive())
log_level = device_event_log::LOG_LEVEL_DEBUG;
DEVICE_LOG(::device_event_log::LOG_TYPE_NETWORK, log_level)
<< type_str << "PropertyUpdated: " << GetLogName(state) << ", " << key
<< " = " << value;
}
std::string NetworkStateHandler::GetTechnologyForType(
const NetworkTypePattern& type) const {
if (type.MatchesType(shill::kTypeEthernet))
return shill::kTypeEthernet;
if (type.MatchesType(shill::kTypeWifi))
return shill::kTypeWifi;
if (type.MatchesType(shill::kTypeCellular))
return shill::kTypeCellular;
if (type.MatchesType(kTypeTether))
return kTypeTether;
NET_LOG(ERROR) << "Unexpected Type for technology: " << type.ToDebugString();
return std::string();
}
std::vector<std::string> NetworkStateHandler::GetTechnologiesForType(
const NetworkTypePattern& type) const {
std::vector<std::string> technologies;
if (type.MatchesType(shill::kTypeEthernet))
technologies.emplace_back(shill::kTypeEthernet);
if (type.MatchesType(shill::kTypeWifi))
technologies.emplace_back(shill::kTypeWifi);
if (type.MatchesType(shill::kTypeCellular))
technologies.emplace_back(shill::kTypeCellular);
if (type.MatchesType(shill::kTypeVPN))
technologies.emplace_back(shill::kTypeVPN);
if (type.MatchesType(kTypeTether))
technologies.emplace_back(kTypeTether);
CHECK_GT(technologies.size(), 0ul);
return technologies;
}
void NetworkStateHandler::SetDefaultNetworkValues(const std::string& path,
bool metered) {
default_network_path_ = path;
default_network_is_metered_ = metered;
}
void NetworkStateHandler::ProcessIsUserLoggedIn(
const base::Value& profile_list) {
if (!profile_list.is_list()) {
return;
}
// The profile list contains the shared profile on the login screen. Once the
// user is logged in there is more than one profile in the profile list.
is_user_logged_in_ = profile_list.GetList().size() > 1;
}
} // namespace chromeos