Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/base/network_change_notifier.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/synchronization/lock.h"
      9 #include "build/build_config.h"
     10 #include "net/base/net_util.h"
     11 #include "net/base/network_change_notifier_factory.h"
     12 #include "net/dns/dns_config_service.h"
     13 #include "net/url_request/url_request.h"
     14 #include "url/gurl.h"
     15 
     16 #if defined(OS_WIN)
     17 #include "net/base/network_change_notifier_win.h"
     18 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
     19 #include "net/base/network_change_notifier_linux.h"
     20 #elif defined(OS_MACOSX)
     21 #include "net/base/network_change_notifier_mac.h"
     22 #endif
     23 
     24 namespace net {
     25 
     26 namespace {
     27 
     28 // The actual singleton notifier.  The class contract forbids usage of the API
     29 // in ways that would require us to place locks around access to this object.
     30 // (The prohibition on global non-POD objects makes it tricky to do such a thing
     31 // anyway.)
     32 NetworkChangeNotifier* g_network_change_notifier = NULL;
     33 
     34 // Class factory singleton.
     35 NetworkChangeNotifierFactory* g_network_change_notifier_factory = NULL;
     36 
     37 class MockNetworkChangeNotifier : public NetworkChangeNotifier {
     38  public:
     39   virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
     40     return CONNECTION_UNKNOWN;
     41   }
     42 };
     43 
     44 }  // namespace
     45 
     46 // The main observer class that records UMAs for network events.
     47 class HistogramWatcher
     48     : public NetworkChangeNotifier::ConnectionTypeObserver,
     49       public NetworkChangeNotifier::IPAddressObserver,
     50       public NetworkChangeNotifier::DNSObserver,
     51       public NetworkChangeNotifier::NetworkChangeObserver {
     52  public:
     53   HistogramWatcher()
     54       : last_ip_address_change_(base::TimeTicks::Now()),
     55         last_connection_change_(base::TimeTicks::Now()),
     56         last_dns_change_(base::TimeTicks::Now()),
     57         last_network_change_(base::TimeTicks::Now()),
     58         last_connection_type_(NetworkChangeNotifier::CONNECTION_UNKNOWN),
     59         offline_packets_received_(0),
     60         bytes_read_since_last_connection_change_(0),
     61         peak_kbps_since_last_connection_change_(0) {}
     62 
     63   // Registers our three Observer implementations.  This is called from the
     64   // network thread so that our Observer implementations are also called
     65   // from the network thread.  This avoids multi-threaded race conditions
     66   // because the only other interface, |NotifyDataReceived| is also
     67   // only called from the network thread.
     68   void Init() {
     69     NetworkChangeNotifier::AddConnectionTypeObserver(this);
     70     NetworkChangeNotifier::AddIPAddressObserver(this);
     71     NetworkChangeNotifier::AddDNSObserver(this);
     72     NetworkChangeNotifier::AddNetworkChangeObserver(this);
     73   }
     74 
     75   virtual ~HistogramWatcher() {}
     76 
     77   // NetworkChangeNotifier::IPAddressObserver implementation.
     78   virtual void OnIPAddressChanged() OVERRIDE {
     79     UMA_HISTOGRAM_MEDIUM_TIMES("NCN.IPAddressChange",
     80                                SinceLast(&last_ip_address_change_));
     81     UMA_HISTOGRAM_MEDIUM_TIMES(
     82         "NCN.ConnectionTypeChangeToIPAddressChange",
     83         last_ip_address_change_ - last_connection_change_);
     84   }
     85 
     86   // NetworkChangeNotifier::ConnectionTypeObserver implementation.
     87   virtual void OnConnectionTypeChanged(
     88       NetworkChangeNotifier::ConnectionType type) OVERRIDE {
     89     base::TimeTicks now = base::TimeTicks::Now();
     90     int32 kilobytes_read = bytes_read_since_last_connection_change_ / 1000;
     91     base::TimeDelta state_duration = SinceLast(&last_connection_change_);
     92     if (bytes_read_since_last_connection_change_) {
     93       switch (last_connection_type_) {
     94         case NetworkChangeNotifier::CONNECTION_UNKNOWN:
     95           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnUnknown",
     96                               first_byte_after_connection_change_);
     97           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnUnknown",
     98                               fastest_RTT_since_last_connection_change_);
     99           break;
    100         case NetworkChangeNotifier::CONNECTION_ETHERNET:
    101           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnEthernet",
    102                               first_byte_after_connection_change_);
    103           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnEthernet",
    104                               fastest_RTT_since_last_connection_change_);
    105           break;
    106         case NetworkChangeNotifier::CONNECTION_WIFI:
    107           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnWifi",
    108                               first_byte_after_connection_change_);
    109           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnWifi",
    110                               fastest_RTT_since_last_connection_change_);
    111           break;
    112         case NetworkChangeNotifier::CONNECTION_2G:
    113           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn2G",
    114                               first_byte_after_connection_change_);
    115           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn2G",
    116                               fastest_RTT_since_last_connection_change_);
    117           break;
    118         case NetworkChangeNotifier::CONNECTION_3G:
    119           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn3G",
    120                               first_byte_after_connection_change_);
    121           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn3G",
    122                               fastest_RTT_since_last_connection_change_);
    123           break;
    124         case NetworkChangeNotifier::CONNECTION_4G:
    125           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOn4G",
    126                               first_byte_after_connection_change_);
    127           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOn4G",
    128                               fastest_RTT_since_last_connection_change_);
    129           break;
    130         case NetworkChangeNotifier::CONNECTION_NONE:
    131           UMA_HISTOGRAM_TIMES("NCN.CM.FirstReadOnNone",
    132                               first_byte_after_connection_change_);
    133           UMA_HISTOGRAM_TIMES("NCN.CM.FastestRTTOnNone",
    134                               fastest_RTT_since_last_connection_change_);
    135           break;
    136       }
    137     }
    138     if (peak_kbps_since_last_connection_change_) {
    139       switch (last_connection_type_) {
    140         case NetworkChangeNotifier::CONNECTION_UNKNOWN:
    141           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnUnknown",
    142                                peak_kbps_since_last_connection_change_);
    143           break;
    144         case NetworkChangeNotifier::CONNECTION_ETHERNET:
    145           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnEthernet",
    146                                peak_kbps_since_last_connection_change_);
    147           break;
    148         case NetworkChangeNotifier::CONNECTION_WIFI:
    149           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnWifi",
    150                                peak_kbps_since_last_connection_change_);
    151           break;
    152         case NetworkChangeNotifier::CONNECTION_2G:
    153           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn2G",
    154                                peak_kbps_since_last_connection_change_);
    155           break;
    156         case NetworkChangeNotifier::CONNECTION_3G:
    157           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn3G",
    158                                peak_kbps_since_last_connection_change_);
    159           break;
    160         case NetworkChangeNotifier::CONNECTION_4G:
    161           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOn4G",
    162                                peak_kbps_since_last_connection_change_);
    163           break;
    164         case NetworkChangeNotifier::CONNECTION_NONE:
    165           UMA_HISTOGRAM_COUNTS("NCN.CM.PeakKbpsOnNone",
    166                                peak_kbps_since_last_connection_change_);
    167           break;
    168       }
    169     }
    170     switch (last_connection_type_) {
    171       case NetworkChangeNotifier::CONNECTION_UNKNOWN:
    172         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnUnknown", state_duration);
    173         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnUnknown", kilobytes_read);
    174         break;
    175       case NetworkChangeNotifier::CONNECTION_ETHERNET:
    176         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnEthernet", state_duration);
    177         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnEthernet", kilobytes_read);
    178         break;
    179       case NetworkChangeNotifier::CONNECTION_WIFI:
    180         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnWifi", state_duration);
    181         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnWifi", kilobytes_read);
    182         break;
    183       case NetworkChangeNotifier::CONNECTION_2G:
    184         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn2G", state_duration);
    185         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn2G", kilobytes_read);
    186         break;
    187       case NetworkChangeNotifier::CONNECTION_3G:
    188         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn3G", state_duration);
    189         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn3G", kilobytes_read);
    190         break;
    191       case NetworkChangeNotifier::CONNECTION_4G:
    192         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOn4G", state_duration);
    193         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOn4G", kilobytes_read);
    194         break;
    195       case NetworkChangeNotifier::CONNECTION_NONE:
    196         UMA_HISTOGRAM_LONG_TIMES("NCN.CM.TimeOnNone", state_duration);
    197         UMA_HISTOGRAM_COUNTS("NCN.CM.KBTransferedOnNone", kilobytes_read);
    198         break;
    199     }
    200 
    201     if (type != NetworkChangeNotifier::CONNECTION_NONE) {
    202       UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OnlineChange", state_duration);
    203 
    204       if (offline_packets_received_) {
    205         if ((now - last_offline_packet_received_) <
    206             base::TimeDelta::FromSeconds(5)) {
    207           // We can compare this sum with the sum of NCN.OfflineDataRecv.
    208           UMA_HISTOGRAM_COUNTS_10000(
    209               "NCN.OfflineDataRecvAny5sBeforeOnline",
    210               offline_packets_received_);
    211         }
    212 
    213         UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecvUntilOnline",
    214                                    now - last_offline_packet_received_);
    215       }
    216     } else {
    217       UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineChange", state_duration);
    218     }
    219     UMA_HISTOGRAM_MEDIUM_TIMES(
    220         "NCN.IPAddressChangeToConnectionTypeChange",
    221         now - last_ip_address_change_);
    222 
    223     offline_packets_received_ = 0;
    224     bytes_read_since_last_connection_change_ = 0;
    225     peak_kbps_since_last_connection_change_ = 0;
    226     last_connection_type_ = type;
    227     polling_interval_ = base::TimeDelta::FromSeconds(1);
    228   }
    229 
    230   // NetworkChangeNotifier::DNSObserver implementation.
    231   virtual void OnDNSChanged() OVERRIDE {
    232     UMA_HISTOGRAM_MEDIUM_TIMES("NCN.DNSConfigChange",
    233                                SinceLast(&last_dns_change_));
    234   }
    235 
    236   // NetworkChangeNotifier::NetworkChangeObserver implementation.
    237   virtual void OnNetworkChanged(
    238       NetworkChangeNotifier::ConnectionType type) OVERRIDE {
    239     if (type != NetworkChangeNotifier::CONNECTION_NONE) {
    240       UMA_HISTOGRAM_MEDIUM_TIMES("NCN.NetworkOnlineChange",
    241                                  SinceLast(&last_network_change_));
    242     } else {
    243       UMA_HISTOGRAM_MEDIUM_TIMES("NCN.NetworkOfflineChange",
    244                                  SinceLast(&last_network_change_));
    245     }
    246   }
    247 
    248   // Record histogram data whenever we receive a packet. Should only be called
    249   // from the network thread.
    250   void NotifyDataReceived(const URLRequest& request, int bytes_read) {
    251     if (IsLocalhost(request.url().host()) ||
    252         !request.url().SchemeIsHTTPOrHTTPS()) {
    253       return;
    254     }
    255 
    256     base::TimeTicks now = base::TimeTicks::Now();
    257     base::TimeDelta request_duration = now - request.creation_time();
    258     if (bytes_read_since_last_connection_change_ == 0) {
    259       first_byte_after_connection_change_ = now - last_connection_change_;
    260       fastest_RTT_since_last_connection_change_ = request_duration;
    261     }
    262     bytes_read_since_last_connection_change_ += bytes_read;
    263     if (request_duration < fastest_RTT_since_last_connection_change_)
    264       fastest_RTT_since_last_connection_change_ = request_duration;
    265     // Ignore tiny transfers which will not produce accurate rates.
    266     // Ignore zero duration transfers which might cause divide by zero.
    267     if (bytes_read > 10000 &&
    268         request_duration > base::TimeDelta::FromMilliseconds(1) &&
    269         request.creation_time() > last_connection_change_) {
    270       int32 kbps = bytes_read * 8 / request_duration.InMilliseconds();
    271       if (kbps > peak_kbps_since_last_connection_change_)
    272         peak_kbps_since_last_connection_change_ = kbps;
    273     }
    274 
    275     if (last_connection_type_ != NetworkChangeNotifier::CONNECTION_NONE)
    276       return;
    277 
    278     UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecv",
    279                                now - last_connection_change_);
    280     offline_packets_received_++;
    281     last_offline_packet_received_ = now;
    282 
    283     if ((now - last_polled_connection_) > polling_interval_) {
    284       polling_interval_ *= 2;
    285       last_polled_connection_ = now;
    286       last_polled_connection_type_ =
    287           NetworkChangeNotifier::GetConnectionType();
    288     }
    289     if (last_polled_connection_type_ ==
    290         NetworkChangeNotifier::CONNECTION_NONE) {
    291       UMA_HISTOGRAM_MEDIUM_TIMES("NCN.PollingOfflineDataRecv",
    292                                  now - last_connection_change_);
    293     }
    294   }
    295 
    296  private:
    297   static base::TimeDelta SinceLast(base::TimeTicks *last_time) {
    298     base::TimeTicks current_time = base::TimeTicks::Now();
    299     base::TimeDelta delta = current_time - *last_time;
    300     *last_time = current_time;
    301     return delta;
    302   }
    303 
    304   base::TimeTicks last_ip_address_change_;
    305   base::TimeTicks last_connection_change_;
    306   base::TimeTicks last_dns_change_;
    307   base::TimeTicks last_network_change_;
    308   base::TimeTicks last_offline_packet_received_;
    309   base::TimeTicks last_polled_connection_;
    310   // |polling_interval_| is initialized by |OnConnectionTypeChanged| on our
    311   // first transition to offline and on subsequent transitions.  Once offline,
    312   // |polling_interval_| doubles as offline data is received and we poll
    313   // with |NetworkChangeNotifier::GetConnectionType| to verify the connection
    314   // state.
    315   base::TimeDelta polling_interval_;
    316   // |last_connection_type_| is the last value passed to
    317   // |OnConnectionTypeChanged|.
    318   NetworkChangeNotifier::ConnectionType last_connection_type_;
    319   // |last_polled_connection_type_| is last result from calling
    320   // |NetworkChangeNotifier::GetConnectionType| in |NotifyDataReceived|.
    321   NetworkChangeNotifier::ConnectionType last_polled_connection_type_;
    322   // Count of how many times NotifyDataReceived() has been called while the
    323   // NetworkChangeNotifier thought network connection was offline.
    324   int32 offline_packets_received_;
    325   // Number of bytes of network data received since last connectivity change.
    326   int32 bytes_read_since_last_connection_change_;
    327   // Fastest round-trip-time (RTT) since last connectivity change. RTT measured
    328   // from URLRequest creation until first byte received.
    329   base::TimeDelta fastest_RTT_since_last_connection_change_;
    330   // Time between connectivity change and first network data byte received.
    331   base::TimeDelta first_byte_after_connection_change_;
    332   // Rough measurement of peak KB/s witnessed since last connectivity change.
    333   // The accuracy is decreased by ignoring these factors:
    334   // 1) Multiple URLRequests can occur concurrently.
    335   // 2) NotifyDataReceived() may be called repeatedly for one URLRequest.
    336   // 3) The transfer time includes at least one RTT while no bytes are read.
    337   // Erring on the conservative side is hopefully offset by taking the maximum.
    338   int32 peak_kbps_since_last_connection_change_;
    339 
    340   DISALLOW_COPY_AND_ASSIGN(HistogramWatcher);
    341 };
    342 
    343 // NetworkState is thread safe.
    344 class NetworkChangeNotifier::NetworkState {
    345  public:
    346   NetworkState() {}
    347   ~NetworkState() {}
    348 
    349   void GetDnsConfig(DnsConfig* config) const {
    350     base::AutoLock lock(lock_);
    351     *config = dns_config_;
    352   }
    353 
    354   void SetDnsConfig(const DnsConfig& dns_config) {
    355     base::AutoLock lock(lock_);
    356     dns_config_ = dns_config;
    357   }
    358 
    359  private:
    360   mutable base::Lock lock_;
    361   DnsConfig dns_config_;
    362 };
    363 
    364 NetworkChangeNotifier::NetworkChangeCalculatorParams::
    365     NetworkChangeCalculatorParams() {
    366 }
    367 
    368 // Calculates NetworkChange signal from IPAddress and ConnectionType signals.
    369 class NetworkChangeNotifier::NetworkChangeCalculator
    370     : public ConnectionTypeObserver,
    371       public IPAddressObserver {
    372  public:
    373   NetworkChangeCalculator(const NetworkChangeCalculatorParams& params)
    374       : params_(params),
    375         have_announced_(false),
    376         last_announced_connection_type_(CONNECTION_NONE),
    377         pending_connection_type_(CONNECTION_NONE) {}
    378 
    379   void Init() {
    380     AddConnectionTypeObserver(this);
    381     AddIPAddressObserver(this);
    382   }
    383 
    384   virtual ~NetworkChangeCalculator() {
    385     RemoveConnectionTypeObserver(this);
    386     RemoveIPAddressObserver(this);
    387   }
    388 
    389   // NetworkChangeNotifier::IPAddressObserver implementation.
    390   virtual void OnIPAddressChanged() OVERRIDE {
    391     base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE
    392         ? params_.ip_address_offline_delay_ : params_.ip_address_online_delay_;
    393     // Cancels any previous timer.
    394     timer_.Start(FROM_HERE, delay, this, &NetworkChangeCalculator::Notify);
    395   }
    396 
    397   // NetworkChangeNotifier::ConnectionTypeObserver implementation.
    398   virtual void OnConnectionTypeChanged(ConnectionType type) OVERRIDE {
    399     pending_connection_type_ = type;
    400     base::TimeDelta delay = last_announced_connection_type_ == CONNECTION_NONE
    401         ? params_.connection_type_offline_delay_
    402         : params_.connection_type_online_delay_;
    403     // Cancels any previous timer.
    404     timer_.Start(FROM_HERE, delay, this, &NetworkChangeCalculator::Notify);
    405   }
    406 
    407  private:
    408   void Notify() {
    409     // Don't bother signaling about dead connections.
    410     if (have_announced_ &&
    411         (last_announced_connection_type_ == CONNECTION_NONE) &&
    412         (pending_connection_type_ == CONNECTION_NONE)) {
    413       return;
    414     }
    415     have_announced_ = true;
    416     last_announced_connection_type_ = pending_connection_type_;
    417     // Immediately before sending out an online signal, send out an offline
    418     // signal to perform any destructive actions before constructive actions.
    419     if (pending_connection_type_ != CONNECTION_NONE)
    420       NetworkChangeNotifier::NotifyObserversOfNetworkChange(CONNECTION_NONE);
    421     NetworkChangeNotifier::NotifyObserversOfNetworkChange(
    422         pending_connection_type_);
    423   }
    424 
    425   const NetworkChangeCalculatorParams params_;
    426 
    427   // Indicates if NotifyObserversOfNetworkChange has been called yet.
    428   bool have_announced_;
    429   // Last value passed to NotifyObserversOfNetworkChange.
    430   ConnectionType last_announced_connection_type_;
    431   // Value to pass to NotifyObserversOfNetworkChange when Notify is called.
    432   ConnectionType pending_connection_type_;
    433   // Used to delay notifications so duplicates can be combined.
    434   base::OneShotTimer<NetworkChangeCalculator> timer_;
    435 };
    436 
    437 NetworkChangeNotifier::~NetworkChangeNotifier() {
    438   DCHECK_EQ(this, g_network_change_notifier);
    439   g_network_change_notifier = NULL;
    440 }
    441 
    442 // static
    443 void NetworkChangeNotifier::SetFactory(
    444     NetworkChangeNotifierFactory* factory) {
    445   CHECK(!g_network_change_notifier_factory);
    446   g_network_change_notifier_factory = factory;
    447 }
    448 
    449 // static
    450 NetworkChangeNotifier* NetworkChangeNotifier::Create() {
    451   if (g_network_change_notifier_factory)
    452     return g_network_change_notifier_factory->CreateInstance();
    453 
    454 #if defined(OS_WIN)
    455   NetworkChangeNotifierWin* network_change_notifier =
    456       new NetworkChangeNotifierWin();
    457   network_change_notifier->WatchForAddressChange();
    458   return network_change_notifier;
    459 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
    460   // ChromeOS and Android builds MUST use their own class factory.
    461 #if !defined(OS_CHROMEOS)
    462   // TODO(oshima): ash_shell do not have access to chromeos'es
    463   // notifier yet. Re-enable this when chromeos'es notifier moved to
    464   // chromeos root directory. crbug.com/119298.
    465   CHECK(false);
    466 #endif
    467   return NULL;
    468 #elif defined(OS_LINUX)
    469   return NetworkChangeNotifierLinux::Create();
    470 #elif defined(OS_MACOSX)
    471   return new NetworkChangeNotifierMac();
    472 #else
    473   NOTIMPLEMENTED();
    474   return NULL;
    475 #endif
    476 }
    477 
    478 // static
    479 NetworkChangeNotifier::ConnectionType
    480 NetworkChangeNotifier::GetConnectionType() {
    481   return g_network_change_notifier ?
    482       g_network_change_notifier->GetCurrentConnectionType() :
    483       CONNECTION_UNKNOWN;
    484 }
    485 
    486 // static
    487 void NetworkChangeNotifier::GetDnsConfig(DnsConfig* config) {
    488   if (!g_network_change_notifier) {
    489     *config = DnsConfig();
    490   } else {
    491     g_network_change_notifier->network_state_->GetDnsConfig(config);
    492   }
    493 }
    494 
    495 // static
    496 const char* NetworkChangeNotifier::ConnectionTypeToString(
    497     ConnectionType type) {
    498   static const char* kConnectionTypeNames[] = {
    499     "CONNECTION_UNKNOWN",
    500     "CONNECTION_ETHERNET",
    501     "CONNECTION_WIFI",
    502     "CONNECTION_2G",
    503     "CONNECTION_3G",
    504     "CONNECTION_4G",
    505     "CONNECTION_NONE"
    506   };
    507   COMPILE_ASSERT(
    508       arraysize(kConnectionTypeNames) ==
    509           NetworkChangeNotifier::CONNECTION_NONE + 1,
    510       ConnectionType_name_count_mismatch);
    511   if (type < CONNECTION_UNKNOWN || type > CONNECTION_NONE) {
    512     NOTREACHED();
    513     return "CONNECTION_INVALID";
    514   }
    515   return kConnectionTypeNames[type];
    516 }
    517 
    518 // static
    519 void NetworkChangeNotifier::NotifyDataReceived(const URLRequest& request,
    520                                                int bytes_read) {
    521   if (!g_network_change_notifier)
    522     return;
    523   g_network_change_notifier->histogram_watcher_->NotifyDataReceived(request,
    524                                                                     bytes_read);
    525 }
    526 
    527 // static
    528 void NetworkChangeNotifier::InitHistogramWatcher() {
    529   if (!g_network_change_notifier)
    530     return;
    531   g_network_change_notifier->histogram_watcher_->Init();
    532 }
    533 
    534 #if defined(OS_LINUX)
    535 // static
    536 const internal::AddressTrackerLinux*
    537 NetworkChangeNotifier::GetAddressTracker() {
    538   return g_network_change_notifier ?
    539         g_network_change_notifier->GetAddressTrackerInternal() : NULL;
    540 }
    541 #endif
    542 
    543 // static
    544 bool NetworkChangeNotifier::IsOffline() {
    545    return GetConnectionType() == CONNECTION_NONE;
    546 }
    547 
    548 // static
    549 bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type) {
    550   bool is_cellular = false;
    551   switch (type) {
    552     case CONNECTION_2G:
    553     case CONNECTION_3G:
    554     case CONNECTION_4G:
    555       is_cellular =  true;
    556       break;
    557     case CONNECTION_UNKNOWN:
    558     case CONNECTION_ETHERNET:
    559     case CONNECTION_WIFI:
    560     case CONNECTION_NONE:
    561       is_cellular = false;
    562       break;
    563   }
    564   return is_cellular;
    565 }
    566 
    567 // static
    568 NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() {
    569   return new MockNetworkChangeNotifier();
    570 }
    571 
    572 void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver* observer) {
    573   if (g_network_change_notifier)
    574     g_network_change_notifier->ip_address_observer_list_->AddObserver(observer);
    575 }
    576 
    577 void NetworkChangeNotifier::AddConnectionTypeObserver(
    578     ConnectionTypeObserver* observer) {
    579   if (g_network_change_notifier) {
    580     g_network_change_notifier->connection_type_observer_list_->AddObserver(
    581         observer);
    582   }
    583 }
    584 
    585 void NetworkChangeNotifier::AddDNSObserver(DNSObserver* observer) {
    586   if (g_network_change_notifier) {
    587     g_network_change_notifier->resolver_state_observer_list_->AddObserver(
    588         observer);
    589   }
    590 }
    591 
    592 void NetworkChangeNotifier::AddNetworkChangeObserver(
    593     NetworkChangeObserver* observer) {
    594   if (g_network_change_notifier) {
    595     g_network_change_notifier->network_change_observer_list_->AddObserver(
    596         observer);
    597   }
    598 }
    599 
    600 void NetworkChangeNotifier::RemoveIPAddressObserver(
    601     IPAddressObserver* observer) {
    602   if (g_network_change_notifier) {
    603     g_network_change_notifier->ip_address_observer_list_->RemoveObserver(
    604         observer);
    605   }
    606 }
    607 
    608 void NetworkChangeNotifier::RemoveConnectionTypeObserver(
    609     ConnectionTypeObserver* observer) {
    610   if (g_network_change_notifier) {
    611     g_network_change_notifier->connection_type_observer_list_->RemoveObserver(
    612         observer);
    613   }
    614 }
    615 
    616 void NetworkChangeNotifier::RemoveDNSObserver(DNSObserver* observer) {
    617   if (g_network_change_notifier) {
    618     g_network_change_notifier->resolver_state_observer_list_->RemoveObserver(
    619         observer);
    620   }
    621 }
    622 
    623 void NetworkChangeNotifier::RemoveNetworkChangeObserver(
    624     NetworkChangeObserver* observer) {
    625   if (g_network_change_notifier) {
    626     g_network_change_notifier->network_change_observer_list_->RemoveObserver(
    627         observer);
    628   }
    629 }
    630 
    631 NetworkChangeNotifier::NetworkChangeNotifier(
    632     const NetworkChangeCalculatorParams& params
    633         /*= NetworkChangeCalculatorParams()*/)
    634     : ip_address_observer_list_(
    635         new ObserverListThreadSafe<IPAddressObserver>(
    636             ObserverListBase<IPAddressObserver>::NOTIFY_EXISTING_ONLY)),
    637       connection_type_observer_list_(
    638         new ObserverListThreadSafe<ConnectionTypeObserver>(
    639             ObserverListBase<ConnectionTypeObserver>::NOTIFY_EXISTING_ONLY)),
    640       resolver_state_observer_list_(
    641         new ObserverListThreadSafe<DNSObserver>(
    642             ObserverListBase<DNSObserver>::NOTIFY_EXISTING_ONLY)),
    643       network_change_observer_list_(
    644         new ObserverListThreadSafe<NetworkChangeObserver>(
    645             ObserverListBase<NetworkChangeObserver>::NOTIFY_EXISTING_ONLY)),
    646       network_state_(new NetworkState()),
    647       histogram_watcher_(new HistogramWatcher()),
    648       network_change_calculator_(new NetworkChangeCalculator(params)) {
    649   DCHECK(!g_network_change_notifier);
    650   g_network_change_notifier = this;
    651   network_change_calculator_->Init();
    652 }
    653 
    654 #if defined(OS_LINUX)
    655 const internal::AddressTrackerLinux*
    656 NetworkChangeNotifier::GetAddressTrackerInternal() const {
    657   return NULL;
    658 }
    659 #endif
    660 
    661 // static
    662 void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
    663   if (g_network_change_notifier) {
    664     g_network_change_notifier->ip_address_observer_list_->Notify(
    665         &IPAddressObserver::OnIPAddressChanged);
    666   }
    667 }
    668 
    669 // static
    670 void NetworkChangeNotifier::NotifyObserversOfDNSChange() {
    671   if (g_network_change_notifier) {
    672     g_network_change_notifier->resolver_state_observer_list_->Notify(
    673         &DNSObserver::OnDNSChanged);
    674   }
    675 }
    676 
    677 // static
    678 void NetworkChangeNotifier::SetDnsConfig(const DnsConfig& config) {
    679   if (!g_network_change_notifier)
    680     return;
    681   g_network_change_notifier->network_state_->SetDnsConfig(config);
    682   NotifyObserversOfDNSChange();
    683 }
    684 
    685 void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange() {
    686   if (g_network_change_notifier) {
    687     g_network_change_notifier->connection_type_observer_list_->Notify(
    688         &ConnectionTypeObserver::OnConnectionTypeChanged,
    689         GetConnectionType());
    690   }
    691 }
    692 
    693 void NetworkChangeNotifier::NotifyObserversOfNetworkChange(
    694     ConnectionType type) {
    695   if (g_network_change_notifier) {
    696     g_network_change_notifier->network_change_observer_list_->Notify(
    697         &NetworkChangeObserver::OnNetworkChanged,
    698         type);
    699   }
    700 }
    701 
    702 NetworkChangeNotifier::DisableForTest::DisableForTest()
    703     : network_change_notifier_(g_network_change_notifier) {
    704   DCHECK(g_network_change_notifier);
    705   g_network_change_notifier = NULL;
    706 }
    707 
    708 NetworkChangeNotifier::DisableForTest::~DisableForTest() {
    709   DCHECK(!g_network_change_notifier);
    710   g_network_change_notifier = network_change_notifier_;
    711 }
    712 
    713 }  // namespace net
    714