text: // 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 <limits>
  #include <string>
  #include <unordered_set>
  #include <utility>
  
  #include "base/cxx17_backports.h"
  #include "base/memory/ref_counted.h"
  #include "base/metrics/histogram_macros.h"
  #include "base/no_destructor.h"
  #include "base/sequence_checker.h"
  #include "base/strings/string_util.h"
  #include "base/synchronization/lock.h"
  #include "base/threading/thread_checker.h"
  #include "base/timer/timer.h"
  #include "build/build_config.h"
  #include "build/chromeos_buildflags.h"
  #include "net/base/network_change_notifier_factory.h"
  #include "net/base/network_interfaces.h"
  #include "net/base/url_util.h"
  #include "net/dns/dns_config.h"
  #include "net/dns/dns_config_service.h"
  #include "net/dns/system_dns_config_change_notifier.h"
  #include "net/url_request/url_request.h"
  #include "third_party/abseil-cpp/absl/types/optional.h"
  #include "url/gurl.h"
  
  #if defined(OS_WIN)
  #include "net/base/network_change_notifier_win.h"
  // TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
  // of lacros-chrome is complete.
  #elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
  #include "net/base/network_change_notifier_linux.h"
  #elif defined(OS_APPLE)
  #include "net/base/network_change_notifier_mac.h"
  #elif BUILDFLAG(IS_CHROMEOS_ASH) || defined(OS_ANDROID)
  #include "net/base/network_change_notifier_posix.h"
  #elif defined(OS_FUCHSIA)
  #include "net/base/network_change_notifier_fuchsia.h"
  #endif
  
  namespace net {
  
  namespace {
  
  // The process-wide singleton notifier.
  NetworkChangeNotifier* g_network_change_notifier = nullptr;
  
  // Class factory singleton.
  NetworkChangeNotifierFactory* g_network_change_notifier_factory = nullptr;
  
  // Lock to protect |g_network_change_notifier| during creation time. Since
  // creation of the process-wide instance can happen on any thread, this lock is
  // used to guarantee only one instance is created. Once the global instance is
  // created, the owner is responsible for destroying it on the same thread. All
  // the other calls to the NetworkChangeNotifier do not require this lock as
  // the global instance is only destroyed when the process is getting killed.
  base::Lock& NetworkChangeNotifierCreationLock() {
    static base::NoDestructor<base::Lock> instance;
    return *instance;
  }
  
  class MockNetworkChangeNotifier : public NetworkChangeNotifier {
   public:
    MockNetworkChangeNotifier(
        std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier)
        : NetworkChangeNotifier(
              NetworkChangeCalculatorParams(),
              dns_config_notifier.get(),
              // Omit adding observers from the constructor as that would prevent
              // construction when SequencedTaskRunnerHandle isn't set.
              /* omit_observers_in_constructor_for_testing=*/true),
          dns_config_notifier_(std::move(dns_config_notifier)) {}
  
    ~MockNetworkChangeNotifier() override { StopSystemDnsConfigNotifier(); }
  
    ConnectionType GetCurrentConnectionType() const override {
      return CONNECTION_UNKNOWN;
    }
  
   private:
    std::unique_ptr<SystemDnsConfigChangeNotifier> dns_config_notifier_;
  };
  
  }  // namespace
  
  // static
  bool NetworkChangeNotifier::test_notifications_only_ = false;
  // static
  const NetworkChangeNotifier::NetworkHandle
      NetworkChangeNotifier::kInvalidNetworkHandle = -1;
  
  NetworkChangeNotifier::NetworkChangeCalculatorParams::
      NetworkChangeCalculatorParams() = default;
  
  // Calculates NetworkChange signal from IPAddress and ConnectionType signals.
  class NetworkChangeNotifier::NetworkChangeCalculator
      : public ConnectionTypeObserver,
        public IPAddressObserver {
   public:
    explicit NetworkChangeCalculator(const NetworkChangeCalculatorParams& params)
        : params_(params),
          have_announced_(false),
          last_announced_connection_type_(CONNECTION_NONE),
          pending_connection_type_(CONNECTION_NONE) {
      DCHECK(g_network_change_notifier);
      AddConnectionTypeObserver(this);
      AddIPAddressObserver(this);
    }
  
    NetworkChangeCalculator(const NetworkChangeCalculator&) = delete;
    NetworkChangeCalculator& operator=(const NetworkChangeCalculator&) = delete;
  
    ~NetworkChangeCalculator() override {
      DCHECK(thread_checker_.CalledOnValidThread());
      RemoveConnectionTypeObserver(this);
      RemoveIPAddressObserver(this);
    }
  
    // NetworkChangeNotifier::IPAddressObserver implementation.
    void OnIPAddressChanged() override {
      DCHECK(thread_checker_.CalledOnValidThread());
      pending_connection_type_ = GetConnectionType();
      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.
    void OnConnectionTypeChanged(ConnectionType type) override {
      DCHECK(thread_checker_.CalledOnValidThread());
      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() {
      DCHECK(thread_checker_.CalledOnValidThread());
      // Don't bother signaling about dead connections.
      if (have_announced_ &&
          (last_announced_connection_type_ == CONNECTION_NONE) &&
          (pending_connection_type_ == CONNECTION_NONE)) {
        return;
      }
  
      UMA_HISTOGRAM_ENUMERATION("Net.NetworkChangeNotifier.NewConnectionType",
                                pending_connection_type_, CONNECTION_LAST + 1);
  
      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_;
  
    base::ThreadChecker thread_checker_;
  };
  
  // Holds the collection of observer lists used by NetworkChangeNotifier.
  class NetworkChangeNotifier::ObserverList {
   public:
    ObserverList()
        : ip_address_observer_list_(new base::ObserverListThreadSafe<
                                    NetworkChangeNotifier::IPAddressObserver>(
              base::ObserverListPolicy::EXISTING_ONLY)),
          connection_type_observer_list_(
              new base::ObserverListThreadSafe<
                  NetworkChangeNotifier::ConnectionTypeObserver>(
                  base::ObserverListPolicy::EXISTING_ONLY)),
          resolver_state_observer_list_(new base::ObserverListThreadSafe<
                                        NetworkChangeNotifier::DNSObserver>(
              base::ObserverListPolicy::EXISTING_ONLY)),
          network_change_observer_list_(
              new base::ObserverListThreadSafe<
                  NetworkChangeNotifier::NetworkChangeObserver>(
                  base::ObserverListPolicy::EXISTING_ONLY)),
          max_bandwidth_observer_list_(
              new base::ObserverListThreadSafe<
                  NetworkChangeNotifier::MaxBandwidthObserver>(
                  base::ObserverListPolicy::EXISTING_ONLY)),
          network_observer_list_(new base::ObserverListThreadSafe<
                                 NetworkChangeNotifier::NetworkObserver>(
              base::ObserverListPolicy::EXISTING_ONLY)),
          connection_cost_observer_list_(
              new base::ObserverListThreadSafe<
                  NetworkChangeNotifier::ConnectionCostObserver>(
                  base::ObserverListPolicy::EXISTING_ONLY)),
          connection_cost_observers_added_(false) {}
  
    ObserverList(const ObserverList&) = delete;
    ObserverList& operator=(const ObserverList&) = delete;
    ~ObserverList() = default;
  
    const scoped_refptr<
        base::ObserverListThreadSafe<NetworkChangeNotifier::IPAddressObserver>>
        ip_address_observer_list_;
    const scoped_refptr<base::ObserverListThreadSafe<
        NetworkChangeNotifier::ConnectionTypeObserver>>
        connection_type_observer_list_;
    const scoped_refptr<
        base::ObserverListThreadSafe<NetworkChangeNotifier::DNSObserver>>
        resolver_state_observer_list_;
    const scoped_refptr<base::ObserverListThreadSafe<
        NetworkChangeNotifier::NetworkChangeObserver>>
        network_change_observer_list_;
    const scoped_refptr<
        base::ObserverListThreadSafe<NetworkChangeNotifier::MaxBandwidthObserver>>
        max_bandwidth_observer_list_;
    const scoped_refptr<
        base::ObserverListThreadSafe<NetworkChangeNotifier::NetworkObserver>>
        network_observer_list_;
    const scoped_refptr<base::ObserverListThreadSafe<
        NetworkChangeNotifier::ConnectionCostObserver>>
        connection_cost_observer_list_;
  
    // Indicates if connection cost observer was added before
    // network_change_notifier was initialized, if so ConnectionCostObserverAdded
    // is invoked from constructor.
    std::atomic_bool connection_cost_observers_added_;
  };
  
  class NetworkChangeNotifier::SystemDnsConfigObserver
      : public SystemDnsConfigChangeNotifier::Observer {
   public:
    virtual ~SystemDnsConfigObserver() = default;
  
    void OnSystemDnsConfigChanged(absl::optional<DnsConfig> config) override {
      NotifyObserversOfDNSChange();
    }
  };
  
  void NetworkChangeNotifier::ClearGlobalPointer() {
    if (!cleared_global_pointer_) {
      cleared_global_pointer_ = true;
      DCHECK_EQ(this, g_network_change_notifier);
      g_network_change_notifier = nullptr;
    }
  }
  
  NetworkChangeNotifier::~NetworkChangeNotifier() {
    network_change_calculator_.reset();
    ClearGlobalPointer();
    StopSystemDnsConfigNotifier();
  }
  
  // static
  NetworkChangeNotifierFactory* NetworkChangeNotifier::GetFactory() {
    return g_network_change_notifier_factory;
  }
  
  // static
  void NetworkChangeNotifier::SetFactory(
      NetworkChangeNotifierFactory* factory) {
    CHECK(!g_network_change_notifier_factory);
    g_network_change_notifier_factory = factory;
  }
  
  // static
  std::unique_ptr<NetworkChangeNotifier> NetworkChangeNotifier::CreateIfNeeded(
      NetworkChangeNotifier::ConnectionType initial_type,
      NetworkChangeNotifier::ConnectionSubtype initial_subtype) {
    {
      base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
      if (g_network_change_notifier)
        return nullptr;
    }
  
    if (g_network_change_notifier_factory)
      return g_network_change_notifier_factory->CreateInstance();
  
  #if defined(OS_WIN)
    std::unique_ptr<NetworkChangeNotifierWin> network_change_notifier =
        std::make_unique<NetworkChangeNotifierWin>();
    network_change_notifier->WatchForAddressChange();
    return network_change_notifier;
  #elif defined(OS_ANDROID)
    // Fallback to use NetworkChangeNotifierPosix if NetworkChangeNotifierFactory
    // is not set. Currently used for tests and when running network
    // service in a separate process.
    return std::make_unique<NetworkChangeNotifierPosix>(initial_type,
                                                        initial_subtype);
  #elif BUILDFLAG(IS_CHROMEOS_ASH)
    return std::make_unique<NetworkChangeNotifierPosix>(initial_type,
                                                        initial_subtype);
  #elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
    return std::make_unique<NetworkChangeNotifierLinux>(
        std::unordered_set<std::string>());
  #elif defined(OS_APPLE)
    return std::make_unique<NetworkChangeNotifierMac>();
  #elif defined(OS_FUCHSIA)
    return std::make_unique<NetworkChangeNotifierFuchsia>(
        /*require_wlan=*/false);
  #else
    NOTIMPLEMENTED();
    return NULL;
  #endif
  }
  
  // static
  NetworkChangeNotifier::ConnectionCost
  NetworkChangeNotifier::GetConnectionCost() {
    return g_network_change_notifier
               ? g_network_change_notifier->GetCurrentConnectionCost()
               : CONNECTION_COST_UNKNOWN;
  }
  
  // static
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifier::GetConnectionType() {
    return g_network_change_notifier ?
        g_network_change_notifier->GetCurrentConnectionType() :
        CONNECTION_UNKNOWN;
  }
  
  // static
  NetworkChangeNotifier::ConnectionSubtype
  NetworkChangeNotifier::GetConnectionSubtype() {
    return g_network_change_notifier
               ? g_network_change_notifier->GetCurrentConnectionSubtype()
               : SUBTYPE_UNKNOWN;
  }
  
  // static
  void NetworkChangeNotifier::GetMaxBandwidthAndConnectionType(
      double* max_bandwidth_mbps,
      ConnectionType* connection_type) {
    if (!g_network_change_notifier) {
      *connection_type = CONNECTION_UNKNOWN;
      *max_bandwidth_mbps =
          GetMaxBandwidthMbpsForConnectionSubtype(SUBTYPE_UNKNOWN);
      return;
    }
  
    g_network_change_notifier->GetCurrentMaxBandwidthAndConnectionType(
        max_bandwidth_mbps, connection_type);
  }
  
  // static
  double NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
      ConnectionSubtype subtype) {
    switch (subtype) {
      case SUBTYPE_GSM:
        return 0.01;
      case SUBTYPE_IDEN:
        return 0.064;
      case SUBTYPE_CDMA:
        return 0.115;
      case SUBTYPE_1XRTT:
        return 0.153;
      case SUBTYPE_GPRS:
        return 0.237;
      case SUBTYPE_EDGE:
        return 0.384;
      case SUBTYPE_UMTS:
        return 2.0;
      case SUBTYPE_EVDO_REV_0:
        return 2.46;
      case SUBTYPE_EVDO_REV_A:
        return 3.1;
      case SUBTYPE_HSPA:
        return 3.6;
      case SUBTYPE_EVDO_REV_B:
        return 14.7;
      case SUBTYPE_HSDPA:
        return 14.3;
      case SUBTYPE_HSUPA:
        return 14.4;
      case SUBTYPE_EHRPD:
        return 21.0;
      case SUBTYPE_HSPAP:
        return 42.0;
      case SUBTYPE_LTE:
        return 100.0;
      case SUBTYPE_LTE_ADVANCED:
        return 100.0;
      case SUBTYPE_BLUETOOTH_1_2:
        return 1.0;
      case SUBTYPE_BLUETOOTH_2_1:
        return 3.0;
      case SUBTYPE_BLUETOOTH_3_0:
        return 24.0;
      case SUBTYPE_BLUETOOTH_4_0:
        return 1.0;
      case SUBTYPE_ETHERNET:
        return 10.0;
      case SUBTYPE_FAST_ETHERNET:
        return 100.0;
      case SUBTYPE_GIGABIT_ETHERNET:
        return 1000.0;
      case SUBTYPE_10_GIGABIT_ETHERNET:
        return 10000.0;
      case SUBTYPE_WIFI_B:
        return 11.0;
      case SUBTYPE_WIFI_G:
        return 54.0;
      case SUBTYPE_WIFI_N:
        return 600.0;
      case SUBTYPE_WIFI_AC:
        return 1300.0;
      case SUBTYPE_WIFI_AD:
        return 7000.0;
      case SUBTYPE_UNKNOWN:
        return std::numeric_limits<double>::infinity();
      case SUBTYPE_NONE:
        return 0.0;
      case SUBTYPE_OTHER:
        return std::numeric_limits<double>::infinity();
    }
    NOTREACHED();
    return std::numeric_limits<double>::infinity();
  }
  
  // static
  bool NetworkChangeNotifier::AreNetworkHandlesSupported() {
    if (g_network_change_notifier) {
      return g_network_change_notifier->AreNetworkHandlesCurrentlySupported();
    }
    return false;
  }
  
  // static
  void NetworkChangeNotifier::GetConnectedNetworks(NetworkList* network_list) {
    DCHECK(AreNetworkHandlesSupported());
    if (g_network_change_notifier) {
      g_network_change_notifier->GetCurrentConnectedNetworks(network_list);
    } else {
      network_list->clear();
    }
  }
  
  // static
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifier::GetNetworkConnectionType(NetworkHandle network) {
    DCHECK(AreNetworkHandlesSupported());
    return g_network_change_notifier
               ? g_network_change_notifier->GetCurrentNetworkConnectionType(
                     network)
               : CONNECTION_UNKNOWN;
  }
  
  // static
  NetworkChangeNotifier::NetworkHandle
  NetworkChangeNotifier::GetDefaultNetwork() {
    DCHECK(AreNetworkHandlesSupported());
    return g_network_change_notifier
               ? g_network_change_notifier->GetCurrentDefaultNetwork()
               : kInvalidNetworkHandle;
  }
  
  // static
  SystemDnsConfigChangeNotifier*
  NetworkChangeNotifier::GetSystemDnsConfigNotifier() {
    if (g_network_change_notifier)
      return g_network_change_notifier->GetCurrentSystemDnsConfigNotifier();
    return nullptr;
  }
  
  // static
  const char* NetworkChangeNotifier::ConnectionTypeToString(
      ConnectionType type) {
    static const char* const kConnectionTypeNames[] = {
        "CONNECTION_UNKNOWN", "CONNECTION_ETHERNET",  "CONNECTION_WIFI",
        "CONNECTION_2G",      "CONNECTION_3G",        "CONNECTION_4G",
        "CONNECTION_NONE",    "CONNECTION_BLUETOOTH", "CONNECTION_5G",
    };
    static_assert(base::size(kConnectionTypeNames) ==
                      NetworkChangeNotifier::CONNECTION_LAST + 1,
                  "ConnectionType name count should match");
    if (type < CONNECTION_UNKNOWN || type > CONNECTION_LAST) {
      NOTREACHED();
      return "CONNECTION_INVALID";
    }
    return kConnectionTypeNames[type];
  }
  
  #if defined(OS_LINUX) || defined(OS_CHROMEOS)
  // 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:
      case CONNECTION_5G:
        is_cellular =  true;
        break;
      case CONNECTION_UNKNOWN:
      case CONNECTION_ETHERNET:
      case CONNECTION_WIFI:
      case CONNECTION_NONE:
      case CONNECTION_BLUETOOTH:
        is_cellular = false;
        break;
    }
    return is_cellular;
  }
  
  // static
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifier::ConnectionTypeFromInterfaces() {
    NetworkInterfaceList interfaces;
    if (!GetNetworkList(&interfaces, EXCLUDE_HOST_SCOPE_VIRTUAL_INTERFACES))
      return CONNECTION_UNKNOWN;
    return ConnectionTypeFromInterfaceList(interfaces);
  }
  
  // static
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifier::ConnectionTypeFromInterfaceList(
      const NetworkInterfaceList& interfaces) {
    bool first = true;
    ConnectionType result = CONNECTION_NONE;
    for (size_t i = 0; i < interfaces.size(); ++i) {
  #if defined(OS_WIN)
      if (interfaces[i].friendly_name == "Teredo Tunneling Pseudo-Interface")
        continue;
  #endif
  #if defined(OS_APPLE)
      // Ignore link-local addresses as they aren't globally routable.
      // Mac assigns these to disconnected interfaces like tunnel interfaces
      // ("utun"), airdrop interfaces ("awdl"), and ethernet ports ("en").
      if (interfaces[i].address.IsLinkLocal())
        continue;
  #endif
  
      // Remove VMware network interfaces as they're internal and should not be
      // used to determine the network connection type.
      if (base::ToLowerASCII(interfaces[i].friendly_name).find("vmnet") !=
          std::string::npos) {
        continue;
      }
      if (first) {
        first = false;
        result = interfaces[i].type;
      } else if (result != interfaces[i].type) {
        return CONNECTION_UNKNOWN;
      }
    }
    return result;
  }
  
  // static
  std::unique_ptr<NetworkChangeNotifier>
  NetworkChangeNotifier::CreateMockIfNeeded() {
    {
      base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
      if (g_network_change_notifier)
        return nullptr;
    }
    // Use an empty noop SystemDnsConfigChangeNotifier to disable actual system
    // DNS configuration notifications.
    return std::make_unique<MockNetworkChangeNotifier>(
        std::make_unique<SystemDnsConfigChangeNotifier>(
            nullptr /* task_runner */, nullptr /* dns_config_service */));
  }
  
  NetworkChangeNotifier::IPAddressObserver::IPAddressObserver() = default;
  NetworkChangeNotifier::IPAddressObserver::~IPAddressObserver() = default;
  
  NetworkChangeNotifier::ConnectionTypeObserver::ConnectionTypeObserver() =
      default;
  NetworkChangeNotifier::ConnectionTypeObserver::~ConnectionTypeObserver() =
      default;
  
  NetworkChangeNotifier::DNSObserver::DNSObserver() = default;
  NetworkChangeNotifier::DNSObserver::~DNSObserver() = default;
  
  NetworkChangeNotifier::NetworkChangeObserver::NetworkChangeObserver() = default;
  NetworkChangeNotifier::NetworkChangeObserver::~NetworkChangeObserver() =
      default;
  
  NetworkChangeNotifier::MaxBandwidthObserver::MaxBandwidthObserver() = default;
  NetworkChangeNotifier::MaxBandwidthObserver::~MaxBandwidthObserver() = default;
  
  NetworkChangeNotifier::NetworkObserver::NetworkObserver() = default;
  NetworkChangeNotifier::NetworkObserver::~NetworkObserver() = default;
  
  NetworkChangeNotifier::ConnectionCostObserver::ConnectionCostObserver() =
      default;
  NetworkChangeNotifier::ConnectionCostObserver::~ConnectionCostObserver() =
      default;
  
  void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver* observer) {
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().ip_address_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddConnectionTypeObserver(
      ConnectionTypeObserver* observer) {
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().connection_type_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddDNSObserver(DNSObserver* observer) {
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().resolver_state_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddNetworkChangeObserver(
      NetworkChangeObserver* observer) {
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().network_change_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddMaxBandwidthObserver(
      MaxBandwidthObserver* observer) {
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().max_bandwidth_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddNetworkObserver(NetworkObserver* observer) {
    base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
    DCHECK(AreNetworkHandlesSupported());
    DCHECK(!observer->observer_list_);
    observer->observer_list_ = GetObserverList().network_observer_list_;
    observer->observer_list_->AddObserver(observer);
  }
  
  void NetworkChangeNotifier::AddConnectionCostObserver(
      ConnectionCostObserver* observer) {
    DCHECK(!observer->observer_list_);
    GetObserverList().connection_cost_observers_added_ = true;
    observer->observer_list_ = GetObserverList().connection_cost_observer_list_;
    observer->observer_list_->AddObserver(observer);
    base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
    if (g_network_change_notifier) {
      g_network_change_notifier->ConnectionCostObserverAdded();
    }
  }
  
  void NetworkChangeNotifier::RemoveIPAddressObserver(
      IPAddressObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveConnectionTypeObserver(
      ConnectionTypeObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveDNSObserver(DNSObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveNetworkChangeObserver(
      NetworkChangeObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveMaxBandwidthObserver(
      MaxBandwidthObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveNetworkObserver(NetworkObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::RemoveConnectionCostObserver(
      ConnectionCostObserver* observer) {
    if (observer->observer_list_) {
      observer->observer_list_->RemoveObserver(observer);
      observer->observer_list_.reset();
    }
  }
  
  void NetworkChangeNotifier::TriggerNonSystemDnsChange() {
    NetworkChangeNotifier::NotifyObserversOfDNSChange();
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests() {
    if (g_network_change_notifier)
      g_network_change_notifier->NotifyObserversOfIPAddressChangeImpl();
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
      ConnectionType type) {
    if (g_network_change_notifier)
      g_network_change_notifier->NotifyObserversOfConnectionTypeChangeImpl(type);
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfDNSChangeForTests() {
    if (g_network_change_notifier)
      g_network_change_notifier->NotifyObserversOfDNSChangeImpl();
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
      ConnectionType type) {
    if (g_network_change_notifier)
      g_network_change_notifier->NotifyObserversOfNetworkChangeImpl(type);
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeForTests(
      double max_bandwidth_mbps,
      ConnectionType type) {
    if (g_network_change_notifier) {
      g_network_change_notifier->NotifyObserversOfMaxBandwidthChangeImpl(
          max_bandwidth_mbps, type);
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeForTests(
      ConnectionCost cost) {
    if (g_network_change_notifier)
      g_network_change_notifier->NotifyObserversOfConnectionCostChangeImpl(cost);
  }
  
  // static
  void NetworkChangeNotifier::SetTestNotificationsOnly(bool test_only) {
    DCHECK(!g_network_change_notifier);
    NetworkChangeNotifier::test_notifications_only_ = test_only;
  }
  
  NetworkChangeNotifier::NetworkChangeNotifier(
      const NetworkChangeCalculatorParams& params
      /*= NetworkChangeCalculatorParams()*/,
      SystemDnsConfigChangeNotifier* system_dns_config_notifier /*= nullptr */,
      bool omit_observers_in_constructor_for_testing /*= false */)
      : system_dns_config_notifier_(system_dns_config_notifier),
        system_dns_config_observer_(std::make_unique<SystemDnsConfigObserver>()) {
    {
      base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
      if (!system_dns_config_notifier_) {
        static base::NoDestructor<SystemDnsConfigChangeNotifier> singleton{};
        system_dns_config_notifier_ = singleton.get();
      }
  
      DCHECK(!g_network_change_notifier);
      g_network_change_notifier = this;
  
      system_dns_config_notifier_->AddObserver(system_dns_config_observer_.get());
      if (GetObserverList().connection_cost_observers_added_) {
        g_network_change_notifier->ConnectionCostObserverAdded();
      }
    }
    if (!omit_observers_in_constructor_for_testing) {
      network_change_calculator_ =
          std::make_unique<NetworkChangeCalculator>(params);
    }
  }
  
  #if defined(OS_LINUX) || defined(OS_CHROMEOS)
  const internal::AddressTrackerLinux*
  NetworkChangeNotifier::GetAddressTrackerInternal() const {
    return NULL;
  }
  #endif
  
  NetworkChangeNotifier::ConnectionCost
  NetworkChangeNotifier::GetCurrentConnectionCost() {
    // This is the default non-platform specific implementation and assumes that
    // cellular connectivity is metered and non-cellular is not. The function can
    // be specialized on each platform specific notifier implementation.
    return IsConnectionCellular(GetCurrentConnectionType())
               ? CONNECTION_COST_METERED
               : CONNECTION_COST_UNMETERED;
  }
  
  NetworkChangeNotifier::ConnectionSubtype
  NetworkChangeNotifier::GetCurrentConnectionSubtype() const {
    return SUBTYPE_UNKNOWN;
  }
  
  void NetworkChangeNotifier::GetCurrentMaxBandwidthAndConnectionType(
      double* max_bandwidth_mbps,
      ConnectionType* connection_type) const {
    // This default implementation conforms to the NetInfo V3 specification but
    // should be overridden to provide specific bandwidth data based on the
    // platform.
    *connection_type = GetCurrentConnectionType();
    *max_bandwidth_mbps =
        *connection_type == CONNECTION_NONE
            ? GetMaxBandwidthMbpsForConnectionSubtype(SUBTYPE_NONE)
            : GetMaxBandwidthMbpsForConnectionSubtype(SUBTYPE_UNKNOWN);
  }
  
  bool NetworkChangeNotifier::AreNetworkHandlesCurrentlySupported() const {
    return false;
  }
  
  void NetworkChangeNotifier::GetCurrentConnectedNetworks(
      NetworkList* network_list) const {
    network_list->clear();
  }
  
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifier::GetCurrentNetworkConnectionType(
      NetworkHandle network) const {
    return CONNECTION_UNKNOWN;
  }
  
  NetworkChangeNotifier::NetworkHandle
  NetworkChangeNotifier::GetCurrentDefaultNetwork() const {
    return kInvalidNetworkHandle;
  }
  
  SystemDnsConfigChangeNotifier*
  NetworkChangeNotifier::GetCurrentSystemDnsConfigNotifier() {
    DCHECK(system_dns_config_notifier_);
    return system_dns_config_notifier_;
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfIPAddressChangeImpl();
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange() {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfConnectionTypeChangeImpl(
          GetConnectionType());
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfNetworkChange(
      ConnectionType type) {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfNetworkChangeImpl(type);
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(
      double max_bandwidth_mbps,
      ConnectionType type) {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfMaxBandwidthChangeImpl(
          max_bandwidth_mbps, type);
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfDNSChange() {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfDNSChangeImpl();
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChange(
      NetworkChangeType type,
      NetworkHandle network) {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfSpecificNetworkChangeImpl(
          type, network);
    }
  }
  
  // static
  void NetworkChangeNotifier::NotifyObserversOfConnectionCostChange() {
    if (g_network_change_notifier &&
        !NetworkChangeNotifier::test_notifications_only_) {
      g_network_change_notifier->NotifyObserversOfConnectionCostChangeImpl(
          GetConnectionCost());
    }
  }
  
  void NetworkChangeNotifier::StopSystemDnsConfigNotifier() {
    if (!system_dns_config_notifier_)
      return;
  
    system_dns_config_notifier_->RemoveObserver(
        system_dns_config_observer_.get());
    system_dns_config_observer_ = nullptr;
    system_dns_config_notifier_ = nullptr;
  }
  
  void NetworkChangeNotifier::NotifyObserversOfIPAddressChangeImpl() {
    GetObserverList().ip_address_observer_list_->Notify(
        FROM_HERE, &IPAddressObserver::OnIPAddressChanged);
  }
  
  void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeImpl(
      ConnectionType type) {
    GetObserverList().connection_type_observer_list_->Notify(
        FROM_HERE, &ConnectionTypeObserver::OnConnectionTypeChanged, type);
  }
  
  void NetworkChangeNotifier::NotifyObserversOfNetworkChangeImpl(
      ConnectionType type) {
    GetObserverList().network_change_observer_list_->Notify(
        FROM_HERE, &NetworkChangeObserver::OnNetworkChanged, type);
  }
  
  void NetworkChangeNotifier::NotifyObserversOfDNSChangeImpl() {
    GetObserverList().resolver_state_observer_list_->Notify(
        FROM_HERE, &DNSObserver::OnDNSChanged);
  }
  
  void NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChangeImpl(
      double max_bandwidth_mbps,
      ConnectionType type) {
    GetObserverList().max_bandwidth_observer_list_->Notify(
        FROM_HERE, &MaxBandwidthObserver::OnMaxBandwidthChanged,
        max_bandwidth_mbps, type);
  }
  
  void NetworkChangeNotifier::NotifyObserversOfSpecificNetworkChangeImpl(
      NetworkChangeType type,
      NetworkHandle network) {
    switch (type) {
      case NetworkChangeType::kConnected:
        GetObserverList().network_observer_list_->Notify(
            FROM_HERE, &NetworkObserver::OnNetworkConnected, network);
        break;
      case NetworkChangeType::kDisconnected:
        GetObserverList().network_observer_list_->Notify(
            FROM_HERE, &NetworkObserver::OnNetworkDisconnected, network);
        break;
      case NetworkChangeType::kSoonToDisconnect:
        GetObserverList().network_observer_list_->Notify(
            FROM_HERE, &NetworkObserver::OnNetworkSoonToDisconnect, network);
        break;
      case NetworkChangeType::kMadeDefault:
        GetObserverList().network_observer_list_->Notify(
            FROM_HERE, &NetworkObserver::OnNetworkMadeDefault, network);
        break;
    }
  }
  
  void NetworkChangeNotifier::NotifyObserversOfConnectionCostChangeImpl(
      ConnectionCost cost) {
    GetObserverList().connection_cost_observer_list_->Notify(
        FROM_HERE, &ConnectionCostObserver::OnConnectionCostChanged, cost);
  }
  
  NetworkChangeNotifier::DisableForTest::DisableForTest()
      : network_change_notifier_(g_network_change_notifier) {
    DCHECK(g_network_change_notifier);
    g_network_change_notifier = nullptr;
  }
  
  NetworkChangeNotifier::DisableForTest::~DisableForTest() {
    DCHECK(!g_network_change_notifier);
    g_network_change_notifier = network_change_notifier_;
  }
  
  // static
  NetworkChangeNotifier::ObserverList& NetworkChangeNotifier::GetObserverList() {
    static base::NoDestructor<NetworkChangeNotifier::ObserverList> observers;
    return *observers;
  }
  
  }  // namespace net
  