summaryrefslogtreecommitdiff
path: root/chromium/net/base/network_change_notifier.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/base/network_change_notifier.cc')
-rw-r--r--chromium/net/base/network_change_notifier.cc713
1 files changed, 713 insertions, 0 deletions
diff --git a/chromium/net/base/network_change_notifier.cc b/chromium/net/base/network_change_notifier.cc
new file mode 100644
index 00000000000..a6335d03dc2
--- /dev/null
+++ b/chromium/net/base/network_change_notifier.cc
@@ -0,0 +1,713 @@
+// 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().SchemeIs("http") || request.url().SchemeIs("https"))) {
+ 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<NetworkChangeCalculator> 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<IPAddressObserver>(
+ ObserverListBase<IPAddressObserver>::NOTIFY_EXISTING_ONLY)),
+ connection_type_observer_list_(
+ new ObserverListThreadSafe<ConnectionTypeObserver>(
+ ObserverListBase<ConnectionTypeObserver>::NOTIFY_EXISTING_ONLY)),
+ resolver_state_observer_list_(
+ new ObserverListThreadSafe<DNSObserver>(
+ ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)),
+ network_change_observer_list_(
+ new ObserverListThreadSafe<NetworkChangeObserver>(
+ ObserverListBase<NetworkChangeObserver>::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