// Copyright 2012 The Chromium Authors
// 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/memory/ref_counted.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/observer_list.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 BUILDFLAG(IS_WIN)
#include "net/base/network_change_notifier_win.h"
#elif BUILDFLAG(IS_LINUX)
#include "net/base/network_change_notifier_linux.h"
#elif BUILDFLAG(IS_APPLE)
#include "net/base/network_change_notifier_mac.h"
#elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
#include "net/base/network_change_notifier_passive.h"
#elif BUILDFLAG(IS_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:
  explicit 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 SingleThreadTaskRunner::CurrentDefaultHandle
            // 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;

NetworkChangeNotifier::NetworkChangeCalculatorParams::
    NetworkChangeCalculatorParams() = default;

// Calculates NetworkChange signal from IPAddress, ConnectionCost, and
// ConnectionType signals.
class NetworkChangeNotifier::NetworkChangeCalculator
    : public ConnectionTypeObserver,
      public ConnectionCostObserver,
      public IPAddressObserver {
 public:
  explicit NetworkChangeCalculator(const NetworkChangeCalculatorParams& params)
      : params_(params) {
    DCHECK(g_network_change_notifier);
    AddConnectionTypeObserver(this);
    AddConnectionCostObserver(this);
    AddIPAddressObserver(this);
  }

  NetworkChangeCalculator(const NetworkChangeCalculator&) = delete;
  NetworkChangeCalculator& operator=(const NetworkChangeCalculator&) = delete;

  ~NetworkChangeCalculator() override {
    DCHECK(thread_checker_.CalledOnValidThread());
    RemoveConnectionTypeObserver(this);
    RemoveConnectionCostObserver(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);
  }

  // NetworkChangeNotifier::ConnectionCostObserver implementation.
  void OnConnectionCostChanged(ConnectionCost cost) override {
    base::UmaHistogramEnumeration("Net.NetworkChangeNotifier.NewConnectionCost",
                                  cost, CONNECTION_COST_LAST);
  }

 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_ = false;
  // Last value passed to NotifyObserversOfNetworkChange.
  ConnectionType last_announced_connection_type_ = CONNECTION_NONE;
  // Value to pass to NotifyObserversOfNetworkChange when Notify is called.
  ConnectionType pending_connection_type_ = CONNECTION_NONE;
  // 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_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::IPAddressObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        connection_type_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::ConnectionTypeObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        resolver_state_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::DNSObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        network_change_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::NetworkChangeObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        max_bandwidth_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::MaxBandwidthObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        network_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::NetworkObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        connection_cost_observer_list_(
            base::MakeRefCounted<base::ObserverListThreadSafe<
                NetworkChangeNotifier::ConnectionCostObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)),
        default_network_active_observer_list_(
            base::MakeRefCounted<
                base::ObserverListThreadSafe<DefaultNetworkActiveObserver>>(
                base::ObserverListPolicy::EXISTING_ONLY)) {}

  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_;
  const scoped_refptr<
      base::ObserverListThreadSafe<DefaultNetworkActiveObserver>>
      default_network_active_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_ = false;
};

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->CreateInstanceWithInitialTypes(
        initial_type, initial_subtype);
  }

#if BUILDFLAG(IS_WIN)
  std::unique_ptr<NetworkChangeNotifierWin> network_change_notifier =
      std::make_unique<NetworkChangeNotifierWin>();
  network_change_notifier->WatchForAddressChange();
  return network_change_notifier;
#elif BUILDFLAG(IS_ANDROID)
  // Fallback to use NetworkChangeNotifierPassive if
  // NetworkChangeNotifierFactory is not set. Currently used for tests and when
  // running network service in a separate process.
  return std::make_unique<NetworkChangeNotifierPassive>(initial_type,
                                                        initial_subtype);
#elif BUILDFLAG(IS_CHROMEOS)
  return std::make_unique<NetworkChangeNotifierPassive>(initial_type,
                                                        initial_subtype);
#elif BUILDFLAG(IS_LINUX)
  return std::make_unique<NetworkChangeNotifierLinux>(
      std::unordered_set<std::string>());
#elif BUILDFLAG(IS_APPLE)
  return std::make_unique<NetworkChangeNotifierMac>();
#elif BUILDFLAG(IS_FUCHSIA)
  return std::make_unique<NetworkChangeNotifierFuchsia>(
      /*require_wlan=*/false);
#else
  NOTIMPLEMENTED();
  return nullptr;
#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(
    handles::NetworkHandle network) {
  DCHECK(AreNetworkHandlesSupported());
  return g_network_change_notifier
             ? g_network_change_notifier->GetCurrentNetworkConnectionType(
                   network)
             : CONNECTION_UNKNOWN;
}

// static
handles::NetworkHandle NetworkChangeNotifier::GetDefaultNetwork() {
  DCHECK(AreNetworkHandlesSupported());
  return g_network_change_notifier
             ? g_network_change_notifier->GetCurrentDefaultNetwork()
             : handles::kInvalidNetworkHandle;
}

// static
SystemDnsConfigChangeNotifier*
NetworkChangeNotifier::GetSystemDnsConfigNotifier() {
  if (g_network_change_notifier)
    return g_network_change_notifier->GetCurrentSystemDnsConfigNotifier();
  return nullptr;
}

// static
bool NetworkChangeNotifier::IsDefaultNetworkActive() {
  if (g_network_change_notifier)
    return g_network_change_notifier->IsDefaultNetworkActiveInternal();
  // Assume true as a "default" to avoid batching indefinitely.
  return true;
}

// 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(std::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 BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// static
AddressMapOwnerLinux* NetworkChangeNotifier::GetAddressMapOwner() {
  return g_network_change_notifier
             ? g_network_change_notifier->GetAddressMapOwnerInternal()
             : nullptr;
}
#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_FUCHSIA)
// static
const internal::NetworkInterfaceCache*
NetworkChangeNotifier::GetNetworkInterfaceCache() {
  return g_network_change_notifier
             ? g_network_change_notifier->GetNetworkInterfaceCacheInternal()
             : nullptr;
}
#endif  // BUILDFLAG(IS_FUCHSIA)

// 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 (const auto& network_interface : interfaces) {
#if BUILDFLAG(IS_WIN)
    if (network_interface.friendly_name == "Teredo Tunneling Pseudo-Interface")
      continue;
#endif
#if BUILDFLAG(IS_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 (network_interface.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(network_interface.friendly_name).find("vmnet") !=
        std::string::npos) {
      continue;
    }
    if (first) {
      first = false;
      result = network_interface.type;
    } else if (result != network_interface.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;

NetworkChangeNotifier::DefaultNetworkActiveObserver::
    DefaultNetworkActiveObserver() = default;
NetworkChangeNotifier::DefaultNetworkActiveObserver::
    ~DefaultNetworkActiveObserver() = 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::AddDefaultNetworkActiveObserver(
    DefaultNetworkActiveObserver* observer) {
  DCHECK(!observer->observer_list_);
  observer->observer_list_ =
      GetObserverList().default_network_active_observer_list_;
  observer->observer_list_->AddObserver(observer);
  base::AutoLock auto_lock(NetworkChangeNotifierCreationLock());
  // Currently we lose DefaultNetworkActiveObserverAdded notifications for
  // observers added prior to NCN creation. This should be a non-issue as
  // currently only Cronet listens to this and its observers are always added
  // after NCN creation.
  if (g_network_change_notifier) {
    g_network_change_notifier->DefaultNetworkActiveObserverAdded();
  }
}

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::RemoveDefaultNetworkActiveObserver(
    DefaultNetworkActiveObserver* observer) {
  if (observer->observer_list_) {
    observer->observer_list_->RemoveObserver(observer);
    observer->observer_list_.reset();
    g_network_change_notifier->DefaultNetworkActiveObserverRemoved();
  }
}

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::NotifyObserversOfDefaultNetworkActiveForTests() {
  if (g_network_change_notifier)
    g_network_change_notifier->NotifyObserversOfDefaultNetworkActiveImpl();
}

// 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 BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
AddressMapOwnerLinux* NetworkChangeNotifier::GetAddressMapOwnerInternal() {
  return nullptr;
}
#endif

#if BUILDFLAG(IS_FUCHSIA)
const internal::NetworkInterfaceCache*
NetworkChangeNotifier::GetNetworkInterfaceCacheInternal() const {
  return nullptr;
}
#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(
    handles::NetworkHandle network) const {
  return CONNECTION_UNKNOWN;
}

handles::NetworkHandle NetworkChangeNotifier::GetCurrentDefaultNetwork() const {
  return handles::kInvalidNetworkHandle;
}

SystemDnsConfigChangeNotifier*
NetworkChangeNotifier::GetCurrentSystemDnsConfigNotifier() {
  DCHECK(system_dns_config_notifier_);
  return system_dns_config_notifier_;
}

bool NetworkChangeNotifier::IsDefaultNetworkActiveInternal() {
  return true;
}

// 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,
    handles::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());
  }
}

// static
void NetworkChangeNotifier::NotifyObserversOfDefaultNetworkActive() {
  if (g_network_change_notifier &&
      !NetworkChangeNotifier::test_notifications_only_) {
    g_network_change_notifier->NotifyObserversOfDefaultNetworkActiveImpl();
  }
}

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,
    handles::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);
}

void NetworkChangeNotifier::NotifyObserversOfDefaultNetworkActiveImpl() {
  GetObserverList().default_network_active_observer_list_->Notify(
      FROM_HERE, &DefaultNetworkActiveObserver::OnDefaultNetworkActive);
}

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
