// 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 "net/base/network_change_notifier.h" #include "base/metrics/histogram.h" #include "base/synchronization/lock.h" #include "build/build_config.h" #include "net/base/net_util.h" #include "net/base/network_change_notifier_factory.h" #include "net/dns/dns_config_service.h" #include "net/url_request/url_request.h" #include "url/gurl.h" #if defined(OS_WIN) #include "net/base/network_change_notifier_win.h" #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) #include "net/base/network_change_notifier_linux.h" #elif defined(OS_MACOSX) #include "net/base/network_change_notifier_mac.h" #endif namespace net { namespace { // The actual singleton notifier. The class contract forbids usage of the API // in ways that would require us to place locks around access to this object. // (The prohibition on global non-POD objects makes it tricky to do such a thing // anyway.) NetworkChangeNotifier* g_network_change_notifier = NULL; // Class factory singleton. NetworkChangeNotifierFactory* g_network_change_notifier_factory = NULL; class MockNetworkChangeNotifier : public NetworkChangeNotifier { public: virtual ConnectionType GetCurrentConnectionType() const OVERRIDE { return CONNECTION_UNKNOWN; } }; } // namespace // The main observer class that records UMAs for network events. class HistogramWatcher : public NetworkChangeNotifier::ConnectionTypeObserver, public NetworkChangeNotifier::IPAddressObserver, public NetworkChangeNotifier::DNSObserver, public NetworkChangeNotifier::NetworkChangeObserver { public: HistogramWatcher() : last_ip_address_change_(base::TimeTicks::Now()), last_connection_change_(base::TimeTicks::Now()), last_dns_change_(base::TimeTicks::Now()), last_network_change_(base::TimeTicks::Now()), last_connection_type_(NetworkChangeNotifier::CONNECTION_UNKNOWN), offline_packets_received_(0), bytes_read_since_last_connection_change_(0), peak_kbps_since_last_connection_change_(0) {} // Registers our three Observer implementations. This is called from the // network thread so that our Observer implementations are also called // from the network thread. This avoids multi-threaded race conditions // because the only other interface, |NotifyDataReceived| is also // only called from the network thread. void Init() { NetworkChangeNotifier::AddConnectionTypeObserver(this); NetworkChangeNotifier::AddIPAddressObserver(this); NetworkChangeNotifier::AddDNSObserver(this); NetworkChangeNotifier::AddNetworkChangeObserver(this); } virtual ~HistogramWatcher() {} // NetworkChangeNotifier::IPAddressObserver implementation. virtual void OnIPAddressChanged() OVERRIDE { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.IPAddressChange", SinceLast(&last_ip_address_change_)); UMA_HISTOGRAM_MEDIUM_TIMES( "NCN.ConnectionTypeChangeToIPAddressChange", last_ip_address_change_ - last_connection_change_); } // NetworkChangeNotifier::ConnectionTypeObserver implementation. virtual void OnConnectionTypeChanged( NetworkChangeNotifier::ConnectionType type) OVERRIDE { base::TimeTicks now = base::TimeTicks::Now(); int32 kilobytes_read = bytes_read_since_last_connection_change_ / 1000; base::TimeDelta state_duration = SinceLast(&last_connection_change_); if (bytes_read_since_last_connection_change_) { switch (last_connection_type_) { case NetworkChangeNotifier::CONNECTION_UNKNOWN: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnUnknown", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnUnknown", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_ETHERNET: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnEthernet", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnEthernet", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_WIFI: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnWifi", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnWifi", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_2G: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn2G", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn2G", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_3G: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn3G", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn3G", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_4G: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn4G", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn4G", fastest_RTT_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_NONE: UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnNone", first_byte_after_connection_change_); UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnNone", fastest_RTT_since_last_connection_change_); break; } } if (peak_kbps_since_last_connection_change_) { switch (last_connection_type_) { case NetworkChangeNotifier::CONNECTION_UNKNOWN: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnUnknown", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_ETHERNET: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnEthernet", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_WIFI: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnWifi", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_2G: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn2G", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_3G: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn3G", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_4G: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn4G", peak_kbps_since_last_connection_change_); break; case NetworkChangeNotifier::CONNECTION_NONE: UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnNone", peak_kbps_since_last_connection_change_); break; } } switch (last_connection_type_) { case NetworkChangeNotifier::CONNECTION_UNKNOWN: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnUnknown", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnUnknown", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_ETHERNET: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnEthernet", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnEthernet", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_WIFI: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnWifi", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnWifi", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_2G: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn2G", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn2G", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_3G: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn3G", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn3G", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_4G: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn4G", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn4G", kilobytes_read); break; case NetworkChangeNotifier::CONNECTION_NONE: UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnNone", state_duration); UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnNone", kilobytes_read); break; } if (type != NetworkChangeNotifier::CONNECTION_NONE) { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OnlineChange", state_duration); if (offline_packets_received_) { if ((now - last_offline_packet_received_) < base::TimeDelta::FromSeconds(5)) { // We can compare this sum with the sum of NCN.OfflineDataRecv. UMA_HISTOGRAM_COUNTS_10000( "NCN.OfflineDataRecvAny5sBeforeOnline", offline_packets_received_); } UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecvUntilOnline", now - last_offline_packet_received_); } } else { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineChange", state_duration); } UMA_HISTOGRAM_MEDIUM_TIMES( "NCN.IPAddressChangeToConnectionTypeChange", now - last_ip_address_change_); offline_packets_received_ = 0; bytes_read_since_last_connection_change_ = 0; peak_kbps_since_last_connection_change_ = 0; last_connection_type_ = type; polling_interval_ = base::TimeDelta::FromSeconds(1); } // NetworkChangeNotifier::DNSObserver implementation. virtual void OnDNSChanged() OVERRIDE { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.DNSConfigChange", SinceLast(&last_dns_change_)); } // NetworkChangeNotifier::NetworkChangeObserver implementation. virtual void OnNetworkChanged( NetworkChangeNotifier::ConnectionType type) OVERRIDE { if (type != NetworkChangeNotifier::CONNECTION_NONE) { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.NetworkOnlineChange", SinceLast(&last_network_change_)); } else { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.NetworkOfflineChange", SinceLast(&last_network_change_)); } } // Record histogram data whenever we receive a packet. Should only be called // from the network thread. void NotifyDataReceived(const URLRequest& request, int bytes_read) { if (IsLocalhost(request.url().host()) || !request.url().SchemeIsHTTPOrHTTPS()) { return; } base::TimeTicks now = base::TimeTicks::Now(); base::TimeDelta request_duration = now - request.creation_time(); if (bytes_read_since_last_connection_change_ == 0) { first_byte_after_connection_change_ = now - last_connection_change_; fastest_RTT_since_last_connection_change_ = request_duration; } bytes_read_since_last_connection_change_ += bytes_read; if (request_duration < fastest_RTT_since_last_connection_change_) fastest_RTT_since_last_connection_change_ = request_duration; // Ignore tiny transfers which will not produce accurate rates. // Ignore zero duration transfers which might cause divide by zero. if (bytes_read > 10000 && request_duration > base::TimeDelta::FromMilliseconds(1) && request.creation_time() > last_connection_change_) { int32 kbps = bytes_read * 8 / request_duration.InMilliseconds(); if (kbps > peak_kbps_since_last_connection_change_) peak_kbps_since_last_connection_change_ = kbps; } if (last_connection_type_ != NetworkChangeNotifier::CONNECTION_NONE) return; UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecv", now - last_connection_change_); offline_packets_received_++; last_offline_packet_received_ = now; if ((now - last_polled_connection_) > polling_interval_) { polling_interval_ *= 2; last_polled_connection_ = now; last_polled_connection_type_ = NetworkChangeNotifier::GetConnectionType(); } if (last_polled_connection_type_ == NetworkChangeNotifier::CONNECTION_NONE) { UMA_HISTOGRAM_MEDIUM_TIMES("NCN.PollingOfflineDataRecv", now - last_connection_change_); } } private: static base::TimeDelta SinceLast(base::TimeTicks *last_time) { base::TimeTicks current_time = base::TimeTicks::Now(); base::TimeDelta delta = current_time - *last_time; *last_time = current_time; return delta; } base::TimeTicks last_ip_address_change_; base::TimeTicks last_connection_change_; base::TimeTicks last_dns_change_; base::TimeTicks last_network_change_; base::TimeTicks last_offline_packet_received_; base::TimeTicks last_polled_connection_; // |polling_interval_| is initialized by |OnConnectionTypeChanged| on our // first transition to offline and on subsequent transitions. Once offline, // |polling_interval_| doubles as offline data is received and we poll // with |NetworkChangeNotifier::GetConnectionType| to verify the connection // state. base::TimeDelta polling_interval_; // |last_connection_type_| is the last value passed to // |OnConnectionTypeChanged|. NetworkChangeNotifier::ConnectionType last_connection_type_; // |last_polled_connection_type_| is last result from calling // |NetworkChangeNotifier::GetConnectionType| in |NotifyDataReceived|. NetworkChangeNotifier::ConnectionType last_polled_connection_type_; // Count of how many times NotifyDataReceived() has been called while the // NetworkChangeNotifier thought network connection was offline. int32 offline_packets_received_; // Number of bytes of network data received since last connectivity change. int32 bytes_read_since_last_connection_change_; // Fastest round-trip-time (RTT) since last connectivity change. RTT measured // from URLRequest creation until first byte received. base::TimeDelta fastest_RTT_since_last_connection_change_; // Time between connectivity change and first network data byte received. base::TimeDelta first_byte_after_connection_change_; // Rough measurement of peak KB/s witnessed since last connectivity change. // The accuracy is decreased by ignoring these factors: // 1) Multiple URLRequests can occur concurrently. // 2) NotifyDataReceived() may be called repeatedly for one URLRequest. // 3) The transfer time includes at least one RTT while no bytes are read. // Erring on the conservative side is hopefully offset by taking the maximum. int32 peak_kbps_since_last_connection_change_; DISALLOW_COPY_AND_ASSIGN(HistogramWatcher); }; // NetworkState is thread safe. class NetworkChangeNotifier::NetworkState { public: NetworkState() {} ~NetworkState() {} void GetDnsConfig(DnsConfig* config) const { base::AutoLock lock(lock_); *config = dns_config_; } void SetDnsConfig(const DnsConfig& dns_config) { base::AutoLock lock(lock_); dns_config_ = dns_config; } private: mutable base::Lock lock_; DnsConfig dns_config_; }; NetworkChangeNotifier::NetworkChangeCalculatorParams:: NetworkChangeCalculatorParams() { } // Calculates NetworkChange signal from IPAddress and ConnectionType signals. class NetworkChangeNotifier::NetworkChangeCalculator : public ConnectionTypeObserver, public IPAddressObserver { public: NetworkChangeCalculator(const NetworkChangeCalculatorParams& params) : params_(params), have_announced_(false), last_announced_connection_type_(CONNECTION_NONE), pending_connection_type_(CONNECTION_NONE) {} void Init() { AddConnectionTypeObserver(this); AddIPAddressObserver(this); } virtual ~NetworkChangeCalculator() { RemoveConnectionTypeObserver(this); RemoveIPAddressObserver(this); } // NetworkChangeNotifier::IPAddressObserver implementation. virtual void OnIPAddressChanged() OVERRIDE { base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE ? params_.ip_address_offline_delay_ : params_.ip_address_online_delay_; // Cancels any previous timer. timer_.Start(FROM_HERE, delay, this, &NetworkChangeCalculator::Notify); } // NetworkChangeNotifier::ConnectionTypeObserver implementation. virtual void OnConnectionTypeChanged(ConnectionType type) OVERRIDE { pending_connection_type_ = type; base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE ? params_.connection_type_offline_delay_ : params_.connection_type_online_delay_; // Cancels any previous timer. timer_.Start(FROM_HERE, delay, this, &NetworkChangeCalculator::Notify); } private: void Notify() { // Don't bother signaling about dead connections. if (have_announced_ && (last_announced_connection_type_ == CONNECTION_NONE) && (pending_connection_type_ == CONNECTION_NONE)) { return; } have_announced_ = true; last_announced_connection_type_ = pending_connection_type_; // Immediately before sending out an online signal, send out an offline // signal to perform any destructive actions before constructive actions. if (pending_connection_type_ != CONNECTION_NONE) NetworkChangeNotifier::NotifyObserversOfNetworkChange(CONNECTION_NONE); NetworkChangeNotifier::NotifyObserversOfNetworkChange( pending_connection_type_); } const NetworkChangeCalculatorParams params_; // Indicates if NotifyObserversOfNetworkChange has been called yet. bool have_announced_; // Last value passed to NotifyObserversOfNetworkChange. ConnectionType last_announced_connection_type_; // Value to pass to NotifyObserversOfNetworkChange when Notify is called. ConnectionType pending_connection_type_; // Used to delay notifications so duplicates can be combined. base::OneShotTimer timer_; }; NetworkChangeNotifier::~NetworkChangeNotifier() { DCHECK_EQ(this, g_network_change_notifier); g_network_change_notifier = NULL; } // static void NetworkChangeNotifier::SetFactory( NetworkChangeNotifierFactory* factory) { CHECK(!g_network_change_notifier_factory); g_network_change_notifier_factory = factory; } // static NetworkChangeNotifier* NetworkChangeNotifier::Create() { if (g_network_change_notifier_factory) return g_network_change_notifier_factory->CreateInstance(); #if defined(OS_WIN) NetworkChangeNotifierWin* network_change_notifier = new NetworkChangeNotifierWin(); network_change_notifier->WatchForAddressChange(); return network_change_notifier; #elif defined(OS_CHROMEOS) || defined(OS_ANDROID) // ChromeOS and Android builds MUST use their own class factory. #if !defined(OS_CHROMEOS) // TODO(oshima): ash_shell do not have access to chromeos'es // notifier yet. Re-enable this when chromeos'es notifier moved to // chromeos root directory. crbug.com/119298. CHECK(false); #endif return NULL; #elif defined(OS_LINUX) return NetworkChangeNotifierLinux::Create(); #elif defined(OS_MACOSX) return new NetworkChangeNotifierMac(); #else NOTIMPLEMENTED(); return NULL; #endif } // static NetworkChangeNotifier::ConnectionType NetworkChangeNotifier::GetConnectionType() { return g_network_change_notifier ? g_network_change_notifier->GetCurrentConnectionType() : CONNECTION_UNKNOWN; } // static void NetworkChangeNotifier::GetDnsConfig(DnsConfig* config) { if (!g_network_change_notifier) { *config = DnsConfig(); } else { g_network_change_notifier->network_state_->GetDnsConfig(config); } } // static const char* NetworkChangeNotifier::ConnectionTypeToString( ConnectionType type) { static const char* kConnectionTypeNames[] = { "CONNECTION_UNKNOWN", "CONNECTION_ETHERNET", "CONNECTION_WIFI", "CONNECTION_2G", "CONNECTION_3G", "CONNECTION_4G", "CONNECTION_NONE" }; COMPILE_ASSERT( arraysize(kConnectionTypeNames) == NetworkChangeNotifier::CONNECTION_NONE + 1, ConnectionType_name_count_mismatch); if (type < CONNECTION_UNKNOWN || type > CONNECTION_NONE) { NOTREACHED(); return "CONNECTION_INVALID"; } return kConnectionTypeNames[type]; } // static void NetworkChangeNotifier::NotifyDataReceived(const URLRequest& request, int bytes_read) { if (!g_network_change_notifier) return; g_network_change_notifier->histogram_watcher_->NotifyDataReceived(request, bytes_read); } // static void NetworkChangeNotifier::InitHistogramWatcher() { if (!g_network_change_notifier) return; g_network_change_notifier->histogram_watcher_->Init(); } #if defined(OS_LINUX) // static const internal::AddressTrackerLinux* NetworkChangeNotifier::GetAddressTracker() { return g_network_change_notifier ? g_network_change_notifier->GetAddressTrackerInternal() : NULL; } #endif // static bool NetworkChangeNotifier::IsOffline() { return GetConnectionType() == CONNECTION_NONE; } // static bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type) { bool is_cellular = false; switch (type) { case CONNECTION_2G: case CONNECTION_3G: case CONNECTION_4G: is_cellular = true; break; case CONNECTION_UNKNOWN: case CONNECTION_ETHERNET: case CONNECTION_WIFI: case CONNECTION_NONE: is_cellular = false; break; } return is_cellular; } // static NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() { return new MockNetworkChangeNotifier(); } void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver* observer) { if (g_network_change_notifier) g_network_change_notifier->ip_address_observer_list_->AddObserver(observer); } void NetworkChangeNotifier::AddConnectionTypeObserver( ConnectionTypeObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->connection_type_observer_list_->AddObserver( observer); } } void NetworkChangeNotifier::AddDNSObserver(DNSObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->resolver_state_observer_list_->AddObserver( observer); } } void NetworkChangeNotifier::AddNetworkChangeObserver( NetworkChangeObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->network_change_observer_list_->AddObserver( observer); } } void NetworkChangeNotifier::RemoveIPAddressObserver( IPAddressObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->ip_address_observer_list_->RemoveObserver( observer); } } void NetworkChangeNotifier::RemoveConnectionTypeObserver( ConnectionTypeObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->connection_type_observer_list_->RemoveObserver( observer); } } void NetworkChangeNotifier::RemoveDNSObserver(DNSObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->resolver_state_observer_list_->RemoveObserver( observer); } } void NetworkChangeNotifier::RemoveNetworkChangeObserver( NetworkChangeObserver* observer) { if (g_network_change_notifier) { g_network_change_notifier->network_change_observer_list_->RemoveObserver( observer); } } NetworkChangeNotifier::NetworkChangeNotifier( const NetworkChangeCalculatorParams& params /*= NetworkChangeCalculatorParams()*/) : ip_address_observer_list_( new ObserverListThreadSafe( ObserverListBase::NOTIFY_EXISTING_ONLY)), connection_type_observer_list_( new ObserverListThreadSafe( ObserverListBase::NOTIFY_EXISTING_ONLY)), resolver_state_observer_list_( new ObserverListThreadSafe( ObserverListBase::NOTIFY_EXISTING_ONLY)), network_change_observer_list_( new ObserverListThreadSafe( ObserverListBase::NOTIFY_EXISTING_ONLY)), network_state_(new NetworkState()), histogram_watcher_(new HistogramWatcher()), network_change_calculator_(new NetworkChangeCalculator(params)) { DCHECK(!g_network_change_notifier); g_network_change_notifier = this; network_change_calculator_->Init(); } #if defined(OS_LINUX) const internal::AddressTrackerLinux* NetworkChangeNotifier::GetAddressTrackerInternal() const { return NULL; } #endif // static void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() { if (g_network_change_notifier) { g_network_change_notifier->ip_address_observer_list_->Notify( &IPAddressObserver::OnIPAddressChanged); } } // static void NetworkChangeNotifier::NotifyObserversOfDNSChange() { if (g_network_change_notifier) { g_network_change_notifier->resolver_state_observer_list_->Notify( &DNSObserver::OnDNSChanged); } } // static void NetworkChangeNotifier::SetDnsConfig(const DnsConfig& config) { if (!g_network_change_notifier) return; g_network_change_notifier->network_state_->SetDnsConfig(config); NotifyObserversOfDNSChange(); } void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange() { if (g_network_change_notifier) { g_network_change_notifier->connection_type_observer_list_->Notify( &ConnectionTypeObserver::OnConnectionTypeChanged, GetConnectionType()); } } void NetworkChangeNotifier::NotifyObserversOfNetworkChange( ConnectionType type) { if (g_network_change_notifier) { g_network_change_notifier->network_change_observer_list_->Notify( &NetworkChangeObserver::OnNetworkChanged, type); } } NetworkChangeNotifier::DisableForTest::DisableForTest() : network_change_notifier_(g_network_change_notifier) { DCHECK(g_network_change_notifier); g_network_change_notifier = NULL; } NetworkChangeNotifier::DisableForTest::~DisableForTest() { DCHECK(!g_network_change_notifier); g_network_change_notifier = network_change_notifier_; } } // namespace net