text: // Copyright 2018 The Chromium OS 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 "shill/link_monitor.h"
  
  #include <string>
  
  #include <base/bind.h>
  
  #include "shill/active_link_monitor.h"
  #include "shill/connection.h"
  #include "shill/device_info.h"
  #include "shill/event_dispatcher.h"
  #include "shill/logging.h"
  #include "shill/net/shill_time.h"
  #include "shill/passive_link_monitor.h"
  
  using base::Bind;
  using base::Unretained;
  namespace shill {
  
  namespace Logging {
  static auto kModuleLogScope = ScopeLogger::kLink;
  static std::string ObjectID(const Connection* c) {
    return c->interface_name();
  }
  }  // namespace Logging
  
  const int LinkMonitor::kFailureThreshold = ActiveLinkMonitor::kFailureThreshold;
  const char LinkMonitor::kDefaultLinkMonitorTechnologies[] = "wifi";
  
  LinkMonitor::LinkMonitor(const ConnectionRefPtr& connection,
                           EventDispatcher* dispatcher,
                           Metrics* metrics,
                           DeviceInfo* device_info,
                           const FailureCallback& failure_callback,
                           const GatewayChangeCallback& gateway_change_callback)
      : connection_(connection),
        dispatcher_(dispatcher),
        metrics_(metrics),
        failure_callback_(failure_callback),
        gateway_change_callback_(gateway_change_callback),
        active_link_monitor_(new ActiveLinkMonitor(
            connection,
            dispatcher,
            metrics,
            device_info,
            Bind(&LinkMonitor::OnActiveLinkMonitorFailure, Unretained(this)),
            Bind(&LinkMonitor::OnActiveLinkMonitorSuccess, Unretained(this)))),
        passive_link_monitor_(new PassiveLinkMonitor(
            connection,
            dispatcher,
            Bind(&LinkMonitor::OnPassiveLinkMonitorResultCallback,
                 Unretained(this)))),
        time_(Time::GetInstance()) {}
  
  LinkMonitor::~LinkMonitor() {
    Stop();
  }
  
  bool LinkMonitor::Start() {
    Stop();
    time_->GetTimeMonotonic(&started_monitoring_at_);
    // Start active link monitor.
    return active_link_monitor_->Start(
        ActiveLinkMonitor::kDefaultTestPeriodMilliseconds);
  }
  
  void LinkMonitor::Stop() {
    SLOG(connection_.get(), 2) << "In " << __func__ << ".";
    timerclear(&started_monitoring_at_);
    active_link_monitor_->Stop();
    passive_link_monitor_->Stop();
    gateway_mac_address_.Clear();
  }
  
  void LinkMonitor::OnAfterResume() {
    // Preserve gateway settings across resume.
    ByteString prior_gateway_mac_address(gateway_mac_address_);
    bool gateway_supports_unicast_arp =
        active_link_monitor_->gateway_supports_unicast_arp();
    Stop();
    gateway_mac_address_ = prior_gateway_mac_address;
    active_link_monitor_->set_gateway_mac_address(gateway_mac_address_);
    active_link_monitor_->set_gateway_supports_unicast_arp(
        gateway_supports_unicast_arp);
  
    active_link_monitor_->Start(ActiveLinkMonitor::kFastTestPeriodMilliseconds);
  }
  
  int LinkMonitor::GetResponseTimeMilliseconds() const {
    return active_link_monitor_->GetResponseTimeMilliseconds();
  }
  
  bool LinkMonitor::IsGatewayFound() const {
    return !gateway_mac_address_.IsZero();
  }
  
  void LinkMonitor::OnActiveLinkMonitorFailure(
      Metrics::LinkMonitorFailure failure,
      int broadcast_failure_count,
      int unicast_failure_count) {
    failure_callback_.Run();
  
    struct timeval now, elapsed_time;
    time_->GetTimeMonotonic(&now);
    timersub(&now, &started_monitoring_at_, &elapsed_time);
  
    metrics_->NotifyLinkMonitorFailure(
        connection_->technology(), failure, elapsed_time.tv_sec,
        broadcast_failure_count, unicast_failure_count);
  
    Stop();
  }
  
  void LinkMonitor::OnActiveLinkMonitorSuccess() {
    if (!gateway_mac_address_.Equals(
            active_link_monitor_->gateway_mac_address())) {
      gateway_mac_address_ = active_link_monitor_->gateway_mac_address();
      // Notify device of the new gateway mac address.
      gateway_change_callback_.Run();
    }
  
    // Start passive link monitoring.
    passive_link_monitor_->Start(PassiveLinkMonitor::kDefaultMonitorCycles);
  }
  
  void LinkMonitor::OnPassiveLinkMonitorResultCallback(bool status) {
    // TODO(zqiu): Add metrics for tracking passive link monitor results.
  
    // Start active monitor
    active_link_monitor_->Start(
        ActiveLinkMonitor::kDefaultTestPeriodMilliseconds);
  }
  
  }  // namespace shill
  