// 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 "chrome/browser/chromeos/net/network_change_notifier_chromeos.h" #include "base/bind.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "content/public/browser/browser_thread.h" #include "net/dns/dns_config_service_posix.h" using content::BrowserThread; namespace { // Delay for online change notification reporting. const int kOnlineNotificationDelayMS = 500; const int kInitialNotificationCheckDelayMS = 1000; bool IsOnline(chromeos::ConnectionState state) { return state == chromeos::STATE_ONLINE || state == chromeos::STATE_PORTAL; } } namespace chromeos { class NetworkChangeNotifierChromeos::DnsConfigServiceChromeos : public net::internal::DnsConfigServicePosix { public: DnsConfigServiceChromeos() {} virtual ~DnsConfigServiceChromeos() {} // net::DnsConfigServicePosix: virtual bool StartWatching() OVERRIDE { // Notifications from NetworkLibrary are sent to // NetworkChangeNotifierChromeos. return true; } void OnNetworkChange() { InvalidateConfig(); InvalidateHosts(); ReadNow(); } }; NetworkChangeNotifierChromeos::NetworkChangeNotifierChromeos() : has_active_network_(false), connection_state_(chromeos::STATE_UNKNOWN), connection_type_(CONNECTION_NONE), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind( &NetworkChangeNotifierChromeos::UpdateInitialState, this), base::TimeDelta::FromMilliseconds(kInitialNotificationCheckDelayMS)); } NetworkChangeNotifierChromeos::~NetworkChangeNotifierChromeos() { } void NetworkChangeNotifierChromeos::Init() { chromeos::NetworkLibrary* network_library = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); network_library->AddNetworkManagerObserver(this); DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this); dns_config_service_.reset(new DnsConfigServiceChromeos()); dns_config_service_->WatchConfig( base::Bind(NetworkChangeNotifier::SetDnsConfig)); UpdateNetworkState(network_library); } void NetworkChangeNotifierChromeos::Shutdown() { weak_factory_.InvalidateWeakPtrs(); dns_config_service_.reset(); if (!chromeos::CrosLibrary::Get()) return; chromeos::NetworkLibrary* lib = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); lib->RemoveNetworkManagerObserver(this); lib->RemoveObserverForAllNetworks(this); DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this); } void NetworkChangeNotifierChromeos::PowerChanged( const PowerSupplyStatus& status) { } void NetworkChangeNotifierChromeos::SystemResumed() { // Force invalidation of various net resources on system resume. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &NetworkChangeNotifier::NotifyObserversOfIPAddressChange)); } void NetworkChangeNotifierChromeos::OnNetworkManagerChanged( chromeos::NetworkLibrary* cros) { UpdateNetworkState(cros); } net::NetworkChangeNotifier::ConnectionType NetworkChangeNotifierChromeos::GetCurrentConnectionType() const { return connection_type_; } void NetworkChangeNotifierChromeos::OnNetworkChanged( chromeos::NetworkLibrary* cros, const chromeos::Network* network) { CHECK(network); // Active network changed? if (network->service_path() != service_path_) UpdateNetworkState(cros); else UpdateConnectivityState(network); } void NetworkChangeNotifierChromeos::UpdateNetworkState( chromeos::NetworkLibrary* lib) { const chromeos::Network* network = lib->active_network(); if (network) { VLOG(1) << "UpdateNetworkState: " << network->name() << ", type= " << network->type() << ", device= " << network->device_path() << ", state= " << network->state(); } // Check if active network was added, removed or changed. if ((!network && has_active_network_) || (network && (!has_active_network_ || network->service_path() != service_path_ || network->ip_address() != ip_address_))) { if (has_active_network_) lib->RemoveObserverForAllNetworks(this); if (!network) { has_active_network_ = false; service_path_.clear(); ip_address_.clear(); } else { has_active_network_ = true; service_path_ = network->service_path(); ip_address_ = network->ip_address(); } // TODO(szym): detect user DNS changes. http://crbug.com/148394 dns_config_service_->OnNetworkChange(); UpdateConnectivityState(network); // If there is an active network, add observer to track its changes. if (network) lib->AddNetworkObserver(network->service_path(), this); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &NetworkChangeNotifier::NotifyObserversOfIPAddressChange)); } } void NetworkChangeNotifierChromeos::UpdateConnectivityState( const chromeos::Network* network) { if (network) { VLOG(1) << "UpdateConnectivityState: " << network->name() << ", type= " << network->type() << ", device= " << network->device_path() << ", state= " << network->state() << ", connect= " << connection_state_ << ", type= " << connection_type_; } // We don't care about all transitions of ConnectionState. OnlineStateChange // notification should trigger if ConnectionType is changed. chromeos::ConnectionState new_connection_state = network ? network->connection_state() : chromeos::STATE_UNKNOWN; ConnectionType prev_connection_type = connection_type_; ConnectionType new_connection_type = GetNetworkConnectionType(network); bool is_online = (new_connection_state == chromeos::STATE_ONLINE); bool was_online = (connection_state_ == chromeos::STATE_ONLINE); bool is_portal = (new_connection_state == chromeos::STATE_PORTAL); bool was_portal = (connection_state_ == chromeos::STATE_PORTAL); VLOG(2) << " UpdateConnectivityState2: " << "new_cs = " << new_connection_state << ", is_online = " << is_online << ", was_online = " << was_online << ", is_portal = " << is_portal << ", was_portal = " << was_portal; connection_state_ = new_connection_state; connection_type_ = new_connection_type; if (new_connection_type != prev_connection_type) { VLOG(1) << "UpdateConnectivityState3: " << "prev_connection_type = " << prev_connection_type << ", new_connection_type = " << new_connection_type; ReportConnectionChange(); } VLOG(2) << " UpdateConnectivityState4: " << "new_cs = " << new_connection_state << ", end_cs_ = " << connection_state_ << "prev_type = " << prev_connection_type << ", new_type_ = " << new_connection_type; } void NetworkChangeNotifierChromeos::ReportConnectionChange() { if (weak_factory_.HasWeakPtrs()) { // If we have a pending task, cancel it. DVLOG(1) << "ReportConnectionChange: has pending task"; weak_factory_.InvalidateWeakPtrs(); DVLOG(1) << "ReportConnectionChange: canceled pending task"; } // Posting task with delay allows us to cancel it when connection type is // changed frequently. This should help us avoid transient edge reporting // while switching between connection types (e.g. ethernet->wifi). BrowserThread::PostDelayedTask( BrowserThread::UI, FROM_HERE, base::Bind( &NetworkChangeNotifierChromeos::ReportConnectionChangeOnUIThread, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kOnlineNotificationDelayMS)); } void NetworkChangeNotifierChromeos::ReportConnectionChangeOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &NetworkChangeNotifierChromeos:: NotifyObserversOfConnectionTypeChange)); } // static void NetworkChangeNotifierChromeos::UpdateInitialState( NetworkChangeNotifierChromeos* self) { chromeos::NetworkLibrary* net = chromeos::CrosLibrary::Get()->GetNetworkLibrary(); self->UpdateNetworkState(net); } // static net::NetworkChangeNotifier::ConnectionType NetworkChangeNotifierChromeos::GetNetworkConnectionType( const chromeos::Network* network) { if (!network || !IsOnline(network->connection_state())) return net::NetworkChangeNotifier::CONNECTION_NONE; switch (network->type()) { case chromeos::TYPE_ETHERNET: return CONNECTION_ETHERNET; case chromeos::TYPE_WIFI: return CONNECTION_WIFI; case chromeos::TYPE_WIMAX: return CONNECTION_4G; case chromeos::TYPE_CELLULAR: switch (static_cast( network)->network_technology()) { case chromeos::NETWORK_TECHNOLOGY_UNKNOWN: case chromeos::NETWORK_TECHNOLOGY_1XRTT: case chromeos::NETWORK_TECHNOLOGY_GPRS: case chromeos::NETWORK_TECHNOLOGY_EDGE: return CONNECTION_2G; case chromeos::NETWORK_TECHNOLOGY_GSM: case chromeos::NETWORK_TECHNOLOGY_UMTS: case chromeos::NETWORK_TECHNOLOGY_EVDO: case chromeos::NETWORK_TECHNOLOGY_HSPA: return CONNECTION_3G; case chromeos::NETWORK_TECHNOLOGY_HSPA_PLUS: case chromeos::NETWORK_TECHNOLOGY_LTE: case chromeos::NETWORK_TECHNOLOGY_LTE_ADVANCED: return CONNECTION_4G; } case chromeos::TYPE_BLUETOOTH: case chromeos::TYPE_VPN: case chromeos::TYPE_UNKNOWN: break; } return net::NetworkChangeNotifier::CONNECTION_UNKNOWN; } } // namespace chromeos