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_linux.h"
  
  #include "base/bind.h"
  #include "base/compiler_specific.h"
  #include "base/threading/thread.h"
  #include "net/base/address_tracker_linux.h"
  #include "net/dns/dns_config_service.h"
  
  namespace net {
  
  class NetworkChangeNotifierLinux::Thread : public base::Thread {
   public:
    Thread();
    ~Thread() override;
  
    // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
    // Safe to call from any thread.
    NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
      return address_tracker_.GetCurrentConnectionType();
    }
  
    const internal::AddressTrackerLinux* address_tracker() const {
      return &address_tracker_;
    }
  
   protected:
    // base::Thread
    void Init() override;
    void CleanUp() override;
  
   private:
    void OnIPAddressChanged();
    void OnLinkChanged();
    scoped_ptr<DnsConfigService> dns_config_service_;
    // Used to detect online/offline state and IP address changes.
    internal::AddressTrackerLinux address_tracker_;
    NetworkChangeNotifier::ConnectionType last_type_;
  
    DISALLOW_COPY_AND_ASSIGN(Thread);
  };
  
  NetworkChangeNotifierLinux::Thread::Thread()
      : base::Thread("NetworkChangeNotifier"),
        address_tracker_(
            base::Bind(&NetworkChangeNotifierLinux::Thread::OnIPAddressChanged,
                       base::Unretained(this)),
            base::Bind(&NetworkChangeNotifierLinux::Thread::OnLinkChanged,
                       base::Unretained(this)),
            base::Bind(base::DoNothing)),
        last_type_(NetworkChangeNotifier::CONNECTION_NONE) {
  }
  
  NetworkChangeNotifierLinux::Thread::~Thread() {
    DCHECK(!Thread::IsRunning());
  }
  
  void NetworkChangeNotifierLinux::Thread::Init() {
    address_tracker_.Init();
    dns_config_service_ = DnsConfigService::CreateSystemService();
    dns_config_service_->WatchConfig(
        base::Bind(&NetworkChangeNotifier::SetDnsConfig));
  }
  
  void NetworkChangeNotifierLinux::Thread::CleanUp() {
    dns_config_service_.reset();
  }
  
  void NetworkChangeNotifierLinux::Thread::OnIPAddressChanged() {
    NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
    // When the IP address of a network interface is added/deleted, the
    // connection type may have changed.
    OnLinkChanged();
  }
  
  void NetworkChangeNotifierLinux::Thread::OnLinkChanged() {
    if (last_type_ != GetCurrentConnectionType()) {
      NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
      last_type_ = GetCurrentConnectionType();
    }
  }
  
  NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() {
    return new NetworkChangeNotifierLinux();
  }
  
  NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
      : NetworkChangeNotifier(NetworkChangeCalculatorParamsLinux()),
        notifier_thread_(new Thread()) {
    // We create this notifier thread because the notification implementation
    // needs a MessageLoopForIO, and there's no guarantee that
    // MessageLoop::current() meets that criterion.
    base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
    notifier_thread_->StartWithOptions(thread_options);
  }
  
  NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
    // Stopping from here allows us to sanity- check that the notifier
    // thread shut down properly.
    notifier_thread_->Stop();
  }
  
  // static
  NetworkChangeNotifier::NetworkChangeCalculatorParams
  NetworkChangeNotifierLinux::NetworkChangeCalculatorParamsLinux() {
    NetworkChangeCalculatorParams params;
    // Delay values arrived at by simple experimentation and adjusted so as to
    // produce a single signal when switching between network connections.
    params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(2000);
    params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(2000);
    params.connection_type_offline_delay_ =
        base::TimeDelta::FromMilliseconds(1500);
    params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
    return params;
  }
  
  NetworkChangeNotifier::ConnectionType
  NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
    return notifier_thread_->GetCurrentConnectionType();
  }
  
  const internal::AddressTrackerLinux*
  NetworkChangeNotifierLinux::GetAddressTrackerInternal() const {
    return notifier_thread_->address_tracker();
  }
  
  }  // namespace net
  