blob: c3800e7c37f774c4849548152c19bf7cf624b380 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/chromeos/network_change_manager_client.h"
#include "base/bind.h"
#include "base/strings/stringprintf.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/common/network_service_util.h"
#include "net/base/network_change_notifier.h"
#include "net/base/network_change_notifier_posix.h"
#include "services/network/public/mojom/network_service.mojom.h"
namespace chromeos {
NetworkChangeManagerClient::NetworkChangeManagerClient(
net::NetworkChangeNotifierPosix* network_change_notifier)
: connection_type_(net::NetworkChangeNotifier::GetConnectionType()),
connection_subtype_(net::NetworkChangeNotifier::GetConnectionSubtype()),
network_change_notifier_(network_change_notifier) {
PowerManagerClient::Get()->AddObserver(this);
NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
if (content::IsOutOfProcessNetworkService())
ConnectToNetworkChangeManager();
// Update initial connection state.
DefaultNetworkChanged(
NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
}
NetworkChangeManagerClient::~NetworkChangeManagerClient() {
NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
FROM_HERE);
PowerManagerClient::Get()->RemoveObserver(this);
}
void NetworkChangeManagerClient::SuspendDone(
const base::TimeDelta& sleep_duration) {
// Force invalidation of network resources on resume.
network_change_notifier_->OnIPAddressChanged();
if (network_change_manager_) {
network_change_manager_->OnNetworkChanged(
/*dns_changed=*/false, /*ip_address_changed=*/true,
/*connection_type_changed=*/false,
network::mojom::ConnectionType::CONNECTION_UNKNOWN,
/*connection_subtype_changed=*/false,
network::mojom::ConnectionSubtype::SUBTYPE_NONE);
}
}
void NetworkChangeManagerClient::DefaultNetworkChanged(
const chromeos::NetworkState* default_network) {
bool connection_type_changed = false;
bool connection_subtype_changed = false;
bool ip_address_changed = false;
bool dns_changed = false;
UpdateState(default_network, &dns_changed, &ip_address_changed,
&connection_type_changed, &connection_subtype_changed);
if (ip_address_changed)
network_change_notifier_->OnIPAddressChanged();
if (dns_changed)
network_change_notifier_->OnDNSChanged();
if (connection_type_changed)
network_change_notifier_->OnConnectionChanged(connection_type_);
if (connection_subtype_changed || connection_type_changed)
network_change_notifier_->OnConnectionSubtypeChanged(connection_type_,
connection_subtype_);
if (network_change_manager_) {
network_change_manager_->OnNetworkChanged(
dns_changed, ip_address_changed, connection_type_changed,
network::mojom::ConnectionType(connection_type_),
connection_subtype_changed,
network::mojom::ConnectionSubtype(connection_subtype_));
}
}
void NetworkChangeManagerClient::ConnectToNetworkChangeManager() {
network::mojom::NetworkChangeManagerRequest request(
mojo::MakeRequest(&network_change_manager_));
content::GetNetworkService()->GetNetworkChangeManager(std::move(request));
network_change_manager_.set_connection_error_handler(base::BindOnce(
&NetworkChangeManagerClient::ReconnectToNetworkChangeManager,
base::Unretained(this)));
}
void NetworkChangeManagerClient::ReconnectToNetworkChangeManager() {
ConnectToNetworkChangeManager();
// Tell the restarted network service what the current connection type is.
network_change_manager_->OnNetworkChanged(
/*dns_changed=*/false, /*ip_address_changed=*/false,
/*connection_type_changed=*/true,
network::mojom::ConnectionType(connection_type_),
/*connection_subtype_changed=*/true,
network::mojom::ConnectionSubtype(connection_subtype_));
}
void NetworkChangeManagerClient::UpdateState(
const chromeos::NetworkState* default_network,
bool* dns_changed,
bool* ip_address_changed,
bool* connection_type_changed,
bool* connection_subtype_changed) {
*connection_type_changed = false;
*connection_subtype_changed = false;
*ip_address_changed = false;
*dns_changed = false;
if (!default_network || !default_network->IsConnectedState()) {
// If we lost a default network, we must update our state and notify
// observers, otherwise we have nothing to do.
if (connection_type_ != net::NetworkChangeNotifier::CONNECTION_NONE) {
NET_LOG_EVENT("NCNDefaultNetworkLost", service_path_);
*ip_address_changed = true;
*dns_changed = true;
*connection_type_changed = true;
*connection_subtype_changed = true;
connection_type_ = net::NetworkChangeNotifier::CONNECTION_NONE;
connection_subtype_ = net::NetworkChangeNotifier::SUBTYPE_NONE;
service_path_.clear();
ip_address_.clear();
dns_servers_.clear();
}
return;
}
// We do have a default network and it is connected.
net::NetworkChangeNotifier::ConnectionType new_connection_type =
ConnectionTypeFromShill(default_network->type(),
default_network->network_technology());
if (new_connection_type != connection_type_) {
NET_LOG_EVENT(
"NCNDefaultConnectionTypeChanged",
base::StringPrintf("%s -> %s",
net::NetworkChangeNotifier::ConnectionTypeToString(
connection_type_),
net::NetworkChangeNotifier::ConnectionTypeToString(
new_connection_type)));
*connection_type_changed = true;
}
if (default_network->path() != service_path_) {
NET_LOG_EVENT("NCNDefaultNetworkServicePathChanged",
base::StringPrintf("%s -> %s", service_path_.c_str(),
default_network->path().c_str()));
// If we had a default network service change, network resources
// must always be invalidated.
*ip_address_changed = true;
*dns_changed = true;
}
std::string new_ip_address = default_network->GetIpAddress();
if (new_ip_address != ip_address_) {
// Is this a state update with an online->online transition?
bool stayed_online =
(!*connection_type_changed &&
connection_type_ != net::NetworkChangeNotifier::CONNECTION_NONE);
bool is_suppressed = true;
// Suppress IP address change signalling on online->online transitions
// when getting an IP address update for the first time.
if (!(stayed_online && ip_address_.empty())) {
is_suppressed = false;
*ip_address_changed = true;
}
NET_LOG_EVENT(base::StringPrintf("%s%s", "NCNDefaultIPAddressChanged",
is_suppressed ? " (Suppressed)" : ""),
base::StringPrintf("%s -> %s", ip_address_.c_str(),
new_ip_address.c_str()));
}
std::string new_dns_servers = default_network->GetDnsServersAsString();
if (new_dns_servers != dns_servers_) {
NET_LOG_EVENT("NCNDefaultDNSServerChanged",
base::StringPrintf("%s -> %s", dns_servers_.c_str(),
new_dns_servers.c_str()));
*dns_changed = true;
}
connection_type_ = new_connection_type;
service_path_ = default_network->path();
ip_address_ = new_ip_address;
dns_servers_ = new_dns_servers;
net::NetworkChangeNotifier::ConnectionSubtype new_subtype =
GetConnectionSubtype(default_network->type(),
default_network->network_technology());
if (new_subtype != connection_subtype_) {
connection_subtype_ = new_subtype;
*connection_subtype_changed = true;
}
}
// static
net::NetworkChangeNotifier::ConnectionType
NetworkChangeManagerClient::ConnectionTypeFromShill(
const std::string& type,
const std::string& technology) {
if (NetworkTypePattern::Ethernet().MatchesType(type))
return net::NetworkChangeNotifier::CONNECTION_ETHERNET;
if (type == shill::kTypeWifi)
return net::NetworkChangeNotifier::CONNECTION_WIFI;
if (type == shill::kTypeWimax)
return net::NetworkChangeNotifier::CONNECTION_4G;
if (type == shill::kTypeBluetooth)
return net::NetworkChangeNotifier::CONNECTION_BLUETOOTH;
if (type != shill::kTypeCellular)
return net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
// For cellular types, mapping depends on the technology.
if (technology == shill::kNetworkTechnologyEvdo ||
technology == shill::kNetworkTechnologyGsm ||
technology == shill::kNetworkTechnologyUmts ||
technology == shill::kNetworkTechnologyHspa) {
return net::NetworkChangeNotifier::CONNECTION_3G;
}
if (technology == shill::kNetworkTechnologyHspaPlus ||
technology == shill::kNetworkTechnologyLte ||
technology == shill::kNetworkTechnologyLteAdvanced) {
return net::NetworkChangeNotifier::CONNECTION_4G;
}
// Default cellular type is 2G.
return net::NetworkChangeNotifier::CONNECTION_2G;
}
// static
net::NetworkChangeNotifier::ConnectionSubtype
NetworkChangeManagerClient::GetConnectionSubtype(
const std::string& type,
const std::string& technology) {
if (type != shill::kTypeCellular)
return net::NetworkChangeNotifier::SUBTYPE_UNKNOWN;
if (technology == shill::kNetworkTechnology1Xrtt)
return net::NetworkChangeNotifier::SUBTYPE_1XRTT;
if (technology == shill::kNetworkTechnologyEvdo)
return net::NetworkChangeNotifier::SUBTYPE_EVDO_REV_0;
if (technology == shill::kNetworkTechnologyGsm)
return net::NetworkChangeNotifier::SUBTYPE_GSM;
if (technology == shill::kNetworkTechnologyGprs)
return net::NetworkChangeNotifier::SUBTYPE_GPRS;
if (technology == shill::kNetworkTechnologyEdge)
return net::NetworkChangeNotifier::SUBTYPE_EDGE;
if (technology == shill::kNetworkTechnologyUmts)
return net::NetworkChangeNotifier::SUBTYPE_UMTS;
if (technology == shill::kNetworkTechnologyHspa)
return net::NetworkChangeNotifier::SUBTYPE_HSPA;
if (technology == shill::kNetworkTechnologyHspaPlus)
return net::NetworkChangeNotifier::SUBTYPE_HSPAP;
if (technology == shill::kNetworkTechnologyLte)
return net::NetworkChangeNotifier::SUBTYPE_LTE;
if (technology == shill::kNetworkTechnologyLteAdvanced)
return net::NetworkChangeNotifier::SUBTYPE_LTE_ADVANCED;
return net::NetworkChangeNotifier::SUBTYPE_UNKNOWN;
}
} // namespace chromeos