// 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 #include "base/basictypes.h" #include "base/bind.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/network/network_change_notifier_chromeos.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" #include "net/base/network_change_notifier.h" #include "net/dns/dns_config_service_posix.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { // DNS config services on Chrome OS are signalled by the network state handler // rather than relying on watching files in /etc. class NetworkChangeNotifierChromeos::DnsConfigService : public net::internal::DnsConfigServicePosix { public: DnsConfigService(); virtual ~DnsConfigService(); // net::internal::DnsConfigService() overrides. virtual bool StartWatching() override; virtual void OnNetworkChange(); }; NetworkChangeNotifierChromeos::DnsConfigService::DnsConfigService() { } NetworkChangeNotifierChromeos::DnsConfigService::~DnsConfigService() { } bool NetworkChangeNotifierChromeos::DnsConfigService::StartWatching() { // DNS config changes are handled and notified by the network state handlers. return true; } void NetworkChangeNotifierChromeos::DnsConfigService::OnNetworkChange() { InvalidateConfig(); InvalidateHosts(); ReadNow(); } NetworkChangeNotifierChromeos::NetworkChangeNotifierChromeos() : NetworkChangeNotifier(NetworkChangeCalculatorParamsChromeos()), connection_type_(CONNECTION_NONE), message_loop_(base::MessageLoopProxy::current()), weak_ptr_factory_(this) { poll_callback_ = base::Bind(&NetworkChangeNotifierChromeos::PollForState, weak_ptr_factory_.GetWeakPtr()); } NetworkChangeNotifierChromeos::~NetworkChangeNotifierChromeos() { } void NetworkChangeNotifierChromeos::Initialize() { DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this); NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE); dns_config_service_.reset(new DnsConfigService()); dns_config_service_->WatchConfig( base::Bind(net::NetworkChangeNotifier::SetDnsConfig)); PollForState(); } void NetworkChangeNotifierChromeos::PollForState() { // Update initial connection state. DefaultNetworkChanged( NetworkHandler::Get()->network_state_handler()->DefaultNetwork()); } void NetworkChangeNotifierChromeos::Shutdown() { dns_config_service_.reset(); NetworkHandler::Get()->network_state_handler()->RemoveObserver( this, FROM_HERE); DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this); } net::NetworkChangeNotifier::ConnectionType NetworkChangeNotifierChromeos::GetCurrentConnectionType() const { // Re-evaluate connection state if we are offline since there is little // cost to doing so. Since we are in the context of a const method, // this is done through a closure that holds a non-const reference to // |this|, to allow PollForState() to modify our cached state. // TODO(gauravsh): Figure out why we would have missed this notification. if (connection_type_ == CONNECTION_NONE) message_loop_->PostTask(FROM_HERE, poll_callback_); return connection_type_; } void NetworkChangeNotifierChromeos::SuspendDone( const base::TimeDelta& sleep_duration) { // Force invalidation of network resources on resume. NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); } void NetworkChangeNotifierChromeos::DefaultNetworkChanged( const chromeos::NetworkState* default_network) { bool connection_type_changed = false; bool ip_address_changed = false; bool dns_changed = false; UpdateState(default_network, &connection_type_changed, &ip_address_changed, &dns_changed); if (connection_type_changed) NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange(); if (ip_address_changed) NetworkChangeNotifier::NotifyObserversOfIPAddressChange(); if (dns_changed) dns_config_service_->OnNetworkChange(); } void NetworkChangeNotifierChromeos::UpdateState( const chromeos::NetworkState* default_network, bool* connection_type_changed, bool* ip_address_changed, bool* dns_changed) { *connection_type_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_ != CONNECTION_NONE) { NET_LOG_EVENT("NCNDefaultNetworkLost", service_path_); *ip_address_changed = true; *dns_changed = true; *connection_type_changed = true; connection_type_ = CONNECTION_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", ConnectionTypeToString(connection_type_), 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; } if (default_network->ip_address() != ip_address_) { // Is this a state update with an online->online transition? bool stayed_online = (!*connection_type_changed && connection_type_ != 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(), default_network->ip_address().c_str())); } if (default_network->dns_servers() != dns_servers_) { NET_LOG_EVENT( "NCNDefaultDNSServerChanged", base::StringPrintf( "%s -> %s", JoinString(dns_servers_, ",").c_str(), JoinString(default_network->dns_servers(), ",").c_str())); *dns_changed = true; } connection_type_ = new_connection_type; service_path_ = default_network->path(); ip_address_ = default_network->ip_address(); dns_servers_ = default_network->dns_servers(); } // static net::NetworkChangeNotifier::ConnectionType NetworkChangeNotifierChromeos::ConnectionTypeFromShill( const std::string& type, const std::string& technology) { if (NetworkTypePattern::Ethernet().MatchesType(type)) return CONNECTION_ETHERNET; else if (type == shill::kTypeWifi) return CONNECTION_WIFI; else if (type == shill::kTypeWimax) return CONNECTION_4G; else if (type == shill::kTypeBluetooth) return CONNECTION_BLUETOOTH; if (type != shill::kTypeCellular) return CONNECTION_UNKNOWN; // For cellular types, mapping depends on the technology. if (technology == shill::kNetworkTechnologyEvdo || technology == shill::kNetworkTechnologyGsm || technology == shill::kNetworkTechnologyUmts || technology == shill::kNetworkTechnologyHspa) { return CONNECTION_3G; } else if (technology == shill::kNetworkTechnologyHspaPlus || technology == shill::kNetworkTechnologyLte || technology == shill::kNetworkTechnologyLteAdvanced) { return CONNECTION_4G; } else { return CONNECTION_2G; // Default cellular type is 2G. } } // static net::NetworkChangeNotifier::NetworkChangeCalculatorParams NetworkChangeNotifierChromeos::NetworkChangeCalculatorParamsChromeos() { NetworkChangeCalculatorParams params; // Delay values arrived at by simple experimentation and adjusted so as to // produce a single signal when switching between network connections. params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(4000); params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1000); params.connection_type_offline_delay_ = base::TimeDelta::FromMilliseconds(500); params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500); return params; } } // namespace chromeos