//
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#ifndef SHILL_DEVICE_H_
#define SHILL_DEVICE_H_

#include <memory>
#include <set>
#include <string>
#include <vector>

#include <base/macros.h>
#include <base/memory/ref_counted.h>
#include <base/memory/weak_ptr.h>
#include <gtest/gtest_prod.h>  // for FRIEND_TEST

#include "shill/adaptor_interfaces.h"
#include "shill/callbacks.h"
#include "shill/connection_diagnostics.h"
#include "shill/connection_tester.h"
#include "shill/connectivity_trial.h"
#include "shill/dns_server_tester.h"
#include "shill/event_dispatcher.h"
#include "shill/ipconfig.h"
#include "shill/net/ip_address.h"
#include "shill/net/shill_time.h"
#include "shill/portal_detector.h"
#include "shill/property_store.h"
#include "shill/refptr_types.h"
#include "shill/service.h"
#include "shill/technology.h"

namespace shill {

class ControlInterface;
class DHCPProvider;
class DeviceAdaptorInterface;
class Endpoint;
class Error;
class EventDispatcher;
class GeolocationInfo;
class LinkMonitor;
class Manager;
class Metrics;
class RTNLHandler;
class TrafficMonitor;

// Device superclass.  Individual network interfaces types will inherit from
// this class.
class Device : public base::RefCounted<Device> {
 public:
  // Progressively scanning for access points (APs) is done with multiple scans,
  // each containing a group of channels.  The scans are performed in order of
  // decreasing likelihood of connecting on one of the channels in a group
  // (the number of channels in a group is a matter for system tuning).  Fully
  // scanning for APs does a complete scan of all the channels in a single scan.
  // Progressive scanning is supported for wifi devices; technologies that
  // support scan but don't support progressive scan will always perform a full
  // scan, regardless of the requested scan type.
  enum ScanType { kProgressiveScan, kFullScan };

  // A constructor for the Device object
  Device(ControlInterface* control_interface,
         EventDispatcher* dispatcher,
         Metrics* metrics,
         Manager* manager,
         const std::string& link_name,
         const std::string& address,
         int interface_index,
         Technology::Identifier technology);

  // Initialize type-specific network interface properties.
  virtual void Initialize();

  // Enable or disable the device. This is a convenience method for
  // cases where we want to SetEnabledNonPersistent, but don't care
  // about the results.
  virtual void SetEnabled(bool enable);
  // Enable or disable the device. Unlike SetEnabledPersistent, it does not
  // save the setting in the profile.
  //
  // TODO(quiche): Replace both of the next two methods with calls to
  // SetEnabledChecked.
  virtual void SetEnabledNonPersistent(bool enable,
                                       Error* error,
                                       const ResultCallback& callback);
  // Enable or disable the device, and save the setting in the profile.
  // The setting is persisted before the enable or disable operation
  // starts, so that even if it fails, the user's intent is still recorded
  // for the next time shill restarts.
  virtual void SetEnabledPersistent(bool enable,
                                    Error* error,
                                    const ResultCallback& callback);
  // Enable or disable the Device, depending on |enable|.
  // Save the new setting to the profile, if |persist| is true.
  // Report synchronous errors using |error|, and asynchronous completion
  // with |callback|.
  virtual void SetEnabledChecked(bool enable,
                                 bool persist,
                                 Error* error,
                                 const ResultCallback& callback);
  // Similar to SetEnabledChecked, but without sanity checking, and
  // without saving the new value of |enable| to the profile. If you
  // are sane (i.e. not Cellular), you should use
  // SetEnabledChecked instead.
  virtual void SetEnabledUnchecked(bool enable,
                                   Error* error,
                                   const ResultCallback& callback);

  // Returns true if the underlying device reports that it is already enabled.
  // Used when the device is registered with the Manager, so that shill can
  // sync its state/ with the true state of the device. The default is to
  // report false.
  virtual bool IsUnderlyingDeviceEnabled() const;

  virtual void LinkEvent(unsigned flags, unsigned change);

  // The default implementation sets |error| to kNotSupported.
  virtual void Scan(ScanType scan_type, Error* error,
                    const std::string& reason);
  // The default implementation sets |error| to kNotSupported.
  virtual void SetSchedScan(bool enable, Error* error);
  virtual void RegisterOnNetwork(const std::string& network_id, Error* error,
                                 const ResultCallback& callback);
  virtual void RequirePIN(const std::string& pin, bool require,
                          Error* error, const ResultCallback& callback);
  virtual void EnterPIN(const std::string& pin,
                        Error* error, const ResultCallback& callback);
  virtual void UnblockPIN(const std::string& unblock_code,
                          const std::string& pin,
                          Error* error, const ResultCallback& callback);
  virtual void ChangePIN(const std::string& old_pin,
                         const std::string& new_pin,
                         Error* error, const ResultCallback& callback);
  virtual void Reset(Error* error, const ResultCallback& callback);

  virtual void SetCarrier(const std::string& carrier,
                          Error* error, const ResultCallback& callback);

  // Returns true if IPv6 is allowed and should be enabled when the device
  // tries to acquire an IP configuration. The default implementation allows
  // IPv6, which can be overridden by a derived class.
  virtual bool IsIPv6Allowed() const;

  virtual void DisableIPv6();
  virtual void EnableIPv6();
  virtual void EnableIPv6Privacy();

  // Returns true if the selected service on the device (if any) is connected.
  // Returns false if there is no selected service, or if the selected service
  // is not connected.
  bool IsConnected() const;

  // Called by Device so that subclasses can run hooks on the selected service
  // getting an IP.  Subclasses should call up to the parent first.
  virtual void OnConnected();

  // Called by the Connection so that the Device can update the service sorting
  // after one connection is bound to another.
  virtual void OnConnectionUpdated();

  // Returns true if the selected service on the device (if any) is connected
  // and matches the passed-in argument |service|.  Returns false if there is
  // no connected service, or if it does not match |service|.
  virtual bool IsConnectedToService(const ServiceRefPtr& service) const;

  // Returns true if the DHCP parameters provided indicate that we are tethered
  // to a mobile device.
  virtual bool IsConnectedViaTether() const;

  // Restart the portal detection process on a connected device.  This is
  // useful if the properties on the connected service have changed in a
  // way that may affect the decision to run portal detection at all.
  // Returns true if portal detection was started.
  virtual bool RestartPortalDetection();

  // Called by the manager to start a single connectivity test.  This is used to
  // log connection state triggered by a user feedback log request.
  virtual bool StartConnectivityTest();

  // Get receive and transmit byte counters.
  virtual uint64_t GetReceiveByteCount();
  virtual uint64_t GetTransmitByteCount();

  // Perform a TDLS |operation| on the underlying device, with respect
  // to a given |peer|.  The string returned is empty for any operation
  // other than kTDLSOperationStatus, which returns the state of the
  // TDLS link with |peer|.  This method is only valid for WiFi devices,
  // but needs to be declared here since it is part of the Device RPC
  // API.
  virtual std::string PerformTDLSOperation(const std::string& operation,
                                           const std::string& peer,
                                           Error* error);

  // Reset the persisted byte counters associated with the device.
  void ResetByteCounters();

  // Requests that portal detection be done, if this device has the default
  // connection.  Returns true if portal detection was started.
  virtual bool RequestPortalDetection();

  std::string GetRpcIdentifier() const;
  std::string GetStorageIdentifier() const;

  // Returns a list of Geolocation objects. Each object is multiple
  // key-value pairs representing one entity that can be used for
  // Geolocation.
  virtual std::vector<GeolocationInfo> GetGeolocationObjects() const;

  // Enable or disable this interface to receive packets even if it is not
  // the default connection.  This is useful in limited situations such as
  // during portal detection.
  virtual void SetLooseRouting(bool is_loose_routing);

  // Enable or disable same-net multi-home support for this interface.  When
  // enabled, ARP filtering is enabled in order to avoid the "ARP Flux"
  // effect where peers may end up with inaccurate IP address mappings due to
  // the default Linux ARP transmit / reply behavior.  See
  // http://linux-ip.net/html/ether-arp.html for more details on this effect.
  virtual void SetIsMultiHomed(bool is_multi_homed);

  const std::string& address() const { return hardware_address_; }
  const std::string& link_name() const { return link_name_; }
  int interface_index() const { return interface_index_; }
  virtual const ConnectionRefPtr& connection() const { return connection_; }
  bool enabled() const { return enabled_; }
  bool enabled_persistent() const { return enabled_persistent_; }
  virtual Technology::Identifier technology() const { return technology_; }
  std::string GetTechnologyString(Error* error);

  virtual const IPConfigRefPtr& ipconfig() const { return ipconfig_; }
  virtual const IPConfigRefPtr& ip6config() const { return ip6config_; }
  virtual const IPConfigRefPtr& dhcpv6_config() const { return dhcpv6_config_; }
  void set_ipconfig(const IPConfigRefPtr& config) { ipconfig_ = config; }

  const std::string& FriendlyName() const;

  // Returns a string that is guaranteed to uniquely identify this Device
  // instance.
  const std::string& UniqueName() const;

  PropertyStore* mutable_store() { return &store_; }
  const PropertyStore& store() const { return store_; }
  RTNLHandler* rtnl_handler() { return rtnl_handler_; }
  bool running() const { return running_; }

  EventDispatcher* dispatcher() const { return dispatcher_; }

  // Load configuration for the device from |storage|.  This may include
  // instantiating non-visible services for which configuration has been
  // stored.
  virtual bool Load(StoreInterface* storage);

  // Save configuration for the device to |storage|.
  virtual bool Save(StoreInterface* storage);

  void set_dhcp_provider(DHCPProvider* provider) { dhcp_provider_ = provider; }

  DeviceAdaptorInterface* adaptor() const { return adaptor_.get(); }

  // Suspend event handler. Called by Manager before the system
  // suspends. This handler, along with any other suspend handlers,
  // will have Manager::kTerminationActionsTimeoutMilliseconds to
  // execute before the system enters the suspend state. |callback|
  // must be invoked after all synchronous and/or asynchronous actions
  // this function performs complete. Code that needs to run on exit should use
  // Manager::AddTerminationAction, rather than OnBeforeSuspend.
  //
  // The default implementation invokes the |callback| immediately, since
  // there is nothing to be done in the general case.
  virtual void OnBeforeSuspend(const ResultCallback& callback);

  // Resume event handler. Called by Manager as the system resumes.
  // The base class implementation takes care of renewing a DHCP lease
  // (if necessary). Derived classes may implement any technology
  // specific requirements by overriding, but should include a call to
  // the base class implementation.
  virtual void OnAfterResume();

  // This method is invoked when the system resumes from suspend temporarily in
  // the "dark resume" state. The system will reenter suspend in
  // Manager::kTerminationActionsTimeoutMilliseconds. |callback| must be invoked
  // after all synchronous and/or asynchronous actions this function performs
  // and/or posts complete.
  //
  // The default implementation invokes the |callback| immediately, since
  // there is nothing to be done in the general case.
  virtual void OnDarkResume(const ResultCallback& callback);

  // Destroy the lease, if any, with this |name|.
  // Called by the service during Unload() as part of the cleanup sequence.
  virtual void DestroyIPConfigLease(const std::string& name);

  // Called by DeviceInfo when the kernel adds or removes a globally-scoped
  // IPv6 address from this interface.
  virtual void OnIPv6AddressChanged();

  // Called by DeviceInfo when the kernel receives a update for IPv6 DNS server
  // addresses from this interface.
  virtual void OnIPv6DnsServerAddressesChanged();

  // Called when link becomes unreliable (multiple link monitor failures
  // detected in short period of time).
  virtual void OnUnreliableLink();

  // Called when link becomes reliable (no link failures in a predefined period
  // of time).
  virtual void OnReliableLink();

  // Program a rule into the NIC to wake the system from suspend upon receiving
  // packets from |ip_endpoint|. |error| indicates the result of the
  // operation.
  virtual void AddWakeOnPacketConnection(const std::string& ip_endpoint,
                                         Error* error);
  // Removes a rule previously programmed into the NIC to wake the system from
  // suspend upon receiving packets from |ip_endpoint|. |error| indicates the
  // result of the operation.
  virtual void RemoveWakeOnPacketConnection(const std::string& ip_endpoint,
                                            Error* error);
  // Removes all wake-on-packet rules programmed into the NIC. |error| indicates
  // the result of the operation.
  virtual void RemoveAllWakeOnPacketConnections(Error* error);

  // Initiate renewal of existing DHCP lease.
  void RenewDHCPLease();

  // Resolve the |input| string into a MAC address for a peer local to this
  // device. This could be a trivial operation if the |input| is already a MAC
  // address, or could involve an ARP table lookup.  Returns true and populates
  // |output| if the resolution completes, otherwise returns false and
  // populates |error|.
  virtual bool ResolvePeerMacAddress(const std::string& input,
                                     std::string* output,
                                     Error* error);

  // Creates a byte vector from a colon-separated hardware address string.
  static std::vector<uint8_t> MakeHardwareAddressFromString(
      const std::string& address_string);

  // Creates a colon-separated hardware address string from a byte vector.
  static std::string MakeStringFromHardwareAddress(
      const std::vector<uint8_t>& address_data);

  // Request the WiFi device to roam to AP with |addr|.
  // This call will send Roam command to wpa_supplicant.
  virtual bool RequestRoam(const std::string& addr, Error* error);

 protected:
  friend class base::RefCounted<Device>;
  friend class DeviceHealthCheckerTest;
  FRIEND_TEST(CellularServiceTest, IsAutoConnectable);
  FRIEND_TEST(CellularTest, EnableTrafficMonitor);
  FRIEND_TEST(CellularTest, ModemStateChangeDisable);
  FRIEND_TEST(CellularTest, UseNoArpGateway);
  FRIEND_TEST(DeviceHealthCheckerTest, HealthCheckerPersistsAcrossDeviceReset);
  FRIEND_TEST(DeviceHealthCheckerTest, RequestConnectionHealthCheck);
  FRIEND_TEST(DeviceHealthCheckerTest, SetupHealthChecker);
  FRIEND_TEST(DevicePortalDetectionTest, RequestStartConnectivityTest);
  FRIEND_TEST(DeviceTest, AcquireIPConfigWithoutSelectedService);
  FRIEND_TEST(DeviceTest, AcquireIPConfigWithSelectedService);
  FRIEND_TEST(DeviceTest, AvailableIPConfigs);
  FRIEND_TEST(DeviceTest, DestroyIPConfig);
  FRIEND_TEST(DeviceTest, DestroyIPConfigNULL);
  FRIEND_TEST(DeviceTest, ConfigWithMinimumMTU);
  FRIEND_TEST(DeviceTest, EnableIPv6);
  FRIEND_TEST(DeviceTest, GetProperties);
  FRIEND_TEST(DeviceTest, IPConfigUpdatedFailureWithIPv6Config);
  FRIEND_TEST(DeviceTest, IPConfigUpdatedFailureWithIPv6Connection);
  FRIEND_TEST(DeviceTest, IsConnectedViaTether);
  FRIEND_TEST(DeviceTest, LinkMonitorFailure);
  FRIEND_TEST(DeviceTest, Load);
  FRIEND_TEST(DeviceTest, OnDHCPv6ConfigExpired);
  FRIEND_TEST(DeviceTest, OnDHCPv6ConfigFailed);
  FRIEND_TEST(DeviceTest, OnDHCPv6ConfigUpdated);
  FRIEND_TEST(DeviceTest, OnIPv6AddressChanged);
  FRIEND_TEST(DeviceTest, OnIPv6ConfigurationCompleted);
  FRIEND_TEST(DeviceTest, OnIPv6DnsServerAddressesChanged);
  FRIEND_TEST(DeviceTest,
              OnIPv6DnsServerAddressesChanged_LeaseExpirationUpdated);
  FRIEND_TEST(DeviceTest, PrependIPv4DNSServers);
  FRIEND_TEST(DeviceTest, PrependIPv6DNSServers);
  FRIEND_TEST(DeviceTest, Save);
  FRIEND_TEST(DeviceTest, SelectedService);
  FRIEND_TEST(DeviceTest, SetEnabledNonPersistent);
  FRIEND_TEST(DeviceTest, SetEnabledPersistent);
  FRIEND_TEST(DeviceTest, SetServiceConnectedState);
  FRIEND_TEST(DeviceTest, ShouldUseArpGateway);
  FRIEND_TEST(DeviceTest, Start);
  FRIEND_TEST(DeviceTest, StartTrafficMonitor);
  FRIEND_TEST(DeviceTest, Stop);
  FRIEND_TEST(DeviceTest, StopTrafficMonitor);
  FRIEND_TEST(ManagerTest, ConnectedTechnologies);
  FRIEND_TEST(ManagerTest, DefaultTechnology);
  FRIEND_TEST(ManagerTest, DeviceRegistrationAndStart);
  FRIEND_TEST(ManagerTest, GetEnabledDeviceWithTechnology);
  FRIEND_TEST(ManagerTest, SetEnabledStateForTechnology);
  FRIEND_TEST(ManagerTest, GetEnabledDeviceByLinkName);
  FRIEND_TEST(PPPDeviceTest, UpdateIPConfigFromPPP);
  FRIEND_TEST(WiFiMainTest, Connect);
  FRIEND_TEST(WiFiMainTest, UseArpGateway);
  FRIEND_TEST(WiMaxTest, ConnectTimeout);
  FRIEND_TEST(WiMaxTest, UseNoArpGateway);

  virtual ~Device();

  // Each device must implement this method to do the work needed to
  // enable the device to operate for establishing network connections.
  // The |error| argument, if not nullptr,
  // will refer to an Error that starts out with the value
  // Error::kOperationInitiated. This reflects the assumption that
  // enable (and disable) operations will usually be non-blocking,
  // and their completion will be indicated by means of an asynchronous
  // reply sometime later. There are two circumstances in which a
  // device's Start() method may overwrite |error|:
  //
  // 1. If an early failure is detected, such that the non-blocking
  //    part of the operation never takes place, then |error| should
  //    be set to the appropriate value corresponding to the type
  //    of failure. This is the "immediate failure" case.
  // 2. If the device is enabled without performing any non-blocking
  //    steps, then |error| should be Reset, i.e., its value set
  //    to Error::kSuccess. This is the "immediate success" case.
  //
  // In these two cases, because completion is immediate, |callback|
  // is not used. If neither of these two conditions holds, then |error|
  // should not be modified, and |callback| should be passed to the
  // method that will initiate the non-blocking operation.
  virtual void Start(Error* error,
                     const EnabledStateChangedCallback& callback) = 0;

  // Each device must implement this method to do the work needed to
  // disable the device, i.e., clear any running state, and make the
  // device no longer capable of establishing network connections.
  // The discussion for Start() regarding the use of |error| and
  // |callback| apply to Stop() as well.
  virtual void Stop(Error* error,
                    const EnabledStateChangedCallback& callback) = 0;

  // The EnabledStateChangedCallback that gets passed to the device's
  // Start() and Stop() methods is bound to this method. |callback|
  // is the callback that was passed to SetEnabled().
  void OnEnabledStateChanged(const ResultCallback& callback,
                             const Error& error);

  // Drops the currently selected service along with its IP configuration and
  // connection, if any.
  virtual void DropConnection();

  // If there's an IP configuration in |ipconfig_|, releases the IP address and
  // destroys the configuration instance.
  void DestroyIPConfig();

  // Creates a new DHCP IP configuration instance, stores it in |ipconfig_| and
  // requests a new IP configuration.  Saves the DHCP lease to the generic
  // lease filename based on the interface name.  Registers a callback to
  // IPConfigUpdatedCallback on IP configuration changes. Returns true if the IP
  // request was successfully sent.
  bool AcquireIPConfig();

  // Creates a new DHCP IP configuration instance, stores it in |ipconfig_| and
  // requests a new IP configuration.  Saves the DHCP lease to a filename
  // based on the passed-in |lease_name|.  Registers a callback to
  // IPConfigUpdatedCallback on IP configuration changes. Returns true if the IP
  // request was successfully sent.
  bool AcquireIPConfigWithLeaseName(const std::string& lease_name);

#ifndef DISABLE_DHCPV6
  // Creates a new DHCPv6 configuration instances, stores it in
  // |dhcpv6_config_| and requests a new configuration.  Saves the DHCPv6
  // lease to a filename based on the passed-in |lease_name|.
  // The acquired configurations will not be used to setup a connection
  // for the device.
  bool AcquireIPv6ConfigWithLeaseName(const std::string& lease_name);
#endif

  // Assigns the IP configuration |properties| to |ipconfig_|.
  void AssignIPConfig(const IPConfig::Properties& properties);

  // Callback invoked on successful IP configuration updates.
  virtual void OnIPConfigUpdated(const IPConfigRefPtr& ipconfig,
                                 bool new_lease_acquired);

  // Called when IPv6 configuration changes.
  virtual void OnIPv6ConfigUpdated();

  // Callback invoked on IP configuration failures.
  void OnIPConfigFailed(const IPConfigRefPtr& ipconfig);

  // Callback invoked when "Refresh" is invoked on an IPConfig.  This usually
  // signals a change in static IP parameters.
  void OnIPConfigRefreshed(const IPConfigRefPtr& ipconfig);

  // Callback invoked when an IPConfig restarts due to lease expiry.  This
  // is advisory, since an "Updated" or "Failed" signal is guaranteed to
  // follow.
  void OnIPConfigExpired(const IPConfigRefPtr& ipconfig);

  // Called by Device so that subclasses can run hooks on the selected service
  // failing to get an IP.  The default implementation disconnects the selected
  // service with Service::kFailureDHCP.
  virtual void OnIPConfigFailure();

  // Callback invoked on successful DHCPv6 configuration updates.
  void OnDHCPv6ConfigUpdated(const IPConfigRefPtr& ipconfig,
                             bool new_lease_acquired);

  // Callback invoked on DHCPv6 configuration failures.
  void OnDHCPv6ConfigFailed(const IPConfigRefPtr& ipconfig);

  // Callback invoked when an DHCPv6Config restarts due to lease expiry.  This
  // is advisory, since an "Updated" or "Failed" signal is guaranteed to
  // follow.
  void OnDHCPv6ConfigExpired(const IPConfigRefPtr& ipconfig);

  // Maintain connection state (Routes, IP Addresses and DNS) in the OS.
  void CreateConnection();

  // Remove connection state
  void DestroyConnection();

  // Selects a service to be "current" -- i.e. link-state or configuration
  // events that happen to the device are attributed to this service.
  void SelectService(const ServiceRefPtr& service);

  // Set the state of the |selected_service_|.
  virtual void SetServiceState(Service::ConnectState state);

  // Set the failure of the selected service (implicitly sets the state to
  // "failure").
  virtual void SetServiceFailure(Service::ConnectFailure failure_state);

  // Records the failure mode and time of the selected service, and
  // sets the Service state of the selected service to "Idle".
  // Avoids showing a failure mole in the UI.
  virtual void SetServiceFailureSilent(Service::ConnectFailure failure_state);

  // Called by the Portal Detector whenever a trial completes.  Device
  // subclasses that choose unique mappings from portal results to connected
  // states can override this method in order to do so.
  virtual void PortalDetectorCallback(const PortalDetector::Result& result);

  // Initiate portal detection, if enabled for this device type.
  bool StartPortalDetection();

  // Stop portal detection if it is running.
  void StopPortalDetection();

  // Initiate connection diagnostics with the |result| from a completed portal
  // detection attempt.
  virtual bool StartConnectionDiagnosticsAfterPortalDetection(
      const PortalDetector::Result& result);

  // Stop connection diagnostics if it is running.
  void StopConnectionDiagnostics();

  // Stop connectivity tester if it exists.
  void StopConnectivityTest();

  // Initiate link monitoring, if enabled for this device type.
  bool StartLinkMonitor();

  // Stop link monitoring if it is running.
  void StopLinkMonitor();

  // Respond to a LinkMonitor failure in a Device-specific manner.
  virtual void OnLinkMonitorFailure();

  // Respond to a LinkMonitor gateway's MAC address found/change event.
  virtual void OnLinkMonitorGatewayChange();

  // Returns true if traffic monitor is enabled on this device. The default
  // implementation will return false, which can be overridden by a derived
  // class.
  virtual bool IsTrafficMonitorEnabled() const;

  // Initiates traffic monitoring on the device if traffic monitor is enabled.
  void StartTrafficMonitor();

  // Stops traffic monitoring on the device if traffic monitor is enabled.
  void StopTrafficMonitor();

  // Start DNS test for the given servers. When retry_until_success is set,
  // callback will only be invoke when the test succeed or the test failed to
  // start (internal error). This function will return false if there is a test
  // that's already running, and true otherwise.
  virtual bool StartDNSTest(
      const std::vector<std::string>& dns_servers,
      const bool retry_until_success,
      const base::Callback<void(const DNSServerTester::Status)>& callback);
  // Stop DNS test if one is running.
  virtual void StopDNSTest();

  // Timer function for monitoring IPv6 DNS server's lifetime.
  void StartIPv6DNSServerTimer(uint32_t lifetime_seconds);
  void StopIPv6DNSServerTimer();

  // Stop all monitoring/testing activities on this device. Called when tearing
  // down or changing network connection on the device.
  void StopAllActivities();

  // Called by the Traffic Monitor when it detects a network problem. Device
  // subclasses that want to roam to a different network when encountering
  // network problems can override this method in order to do so. The parent
  // implementation handles the metric reporting of the network problem.
  virtual void OnEncounterNetworkProblem(int reason);

  // Set the state of the selected service, with checks to make sure
  // the service is already in a connected state before doing so.
  void SetServiceConnectedState(Service::ConnectState state);

  // Specifies whether an ARP gateway should be used for the
  // device technology.
  virtual bool ShouldUseArpGateway() const;

  // Indicates if the selected service is configured with a static IP address.
  bool IsUsingStaticIP() const;

  // Indicates if the selected service is configured with static nameservers.
  bool IsUsingStaticNameServers() const;

  const ServiceRefPtr& selected_service() const { return selected_service_; }

  void HelpRegisterConstDerivedString(
      const std::string& name,
      std::string(Device::*get)(Error*));

  void HelpRegisterConstDerivedRpcIdentifier(
      const std::string& name,
      RpcIdentifier(Device::*get)(Error*));

  void HelpRegisterConstDerivedRpcIdentifiers(
      const std::string& name,
      RpcIdentifiers(Device::*get)(Error*));

  void HelpRegisterConstDerivedUint64(
      const std::string& name,
      uint64_t(Device::*get)(Error*));

  // Called by the ConnectionTester whenever a connectivity test completes.
  virtual void ConnectionTesterCallback();

  // Property getters reserved for subclasses
  ControlInterface* control_interface() const { return control_interface_; }
  Metrics* metrics() const { return metrics_; }
  Manager* manager() const { return manager_; }
  const LinkMonitor* link_monitor() const { return link_monitor_.get(); }
  void set_link_monitor(LinkMonitor* link_monitor);
  // Use for unit test.
  void set_traffic_monitor(TrafficMonitor* traffic_monitor);

  // Calculates the time (in seconds) till a DHCP lease is due for renewal,
  // and stores this value in |result|. Returns false is there is no upcoming
  // DHCP lease renewal, true otherwise.
  bool TimeToNextDHCPLeaseRenewal(uint32_t* result);

 private:
  friend class CellularCapabilityTest;
  friend class CellularTest;
  friend class DeviceAdaptorInterface;
  friend class DeviceByteCountTest;
  friend class DevicePortalDetectionTest;
  friend class DeviceTest;
  friend class EthernetTest;
  friend class OpenVPNDriverTest;
  friend class TestDevice;
  friend class VirtualDeviceTest;
  friend class WiFiObjectTest;

  static const char kIPFlagTemplate[];
  static const char kIPFlagVersion4[];
  static const char kIPFlagVersion6[];
  static const char kIPFlagDisableIPv6[];
  static const char kIPFlagUseTempAddr[];
  static const char kIPFlagUseTempAddrUsedAndDefault[];
  static const char kIPFlagReversePathFilter[];
  static const char kIPFlagReversePathFilterEnabled[];
  static const char kIPFlagReversePathFilterLooseMode[];
  static const char kIPFlagArpAnnounce[];
  static const char kIPFlagArpAnnounceDefault[];
  static const char kIPFlagArpAnnounceBestLocal[];
  static const char kIPFlagArpIgnore[];
  static const char kIPFlagArpIgnoreDefault[];
  static const char kIPFlagArpIgnoreLocalOnly[];
  static const char kStoragePowered[];
  static const char kStorageReceiveByteCount[];
  static const char kStorageTransmitByteCount[];
  static const char kFallbackDnsTestHostname[];
  static const char* kFallbackDnsServers[];
  static const int kDNSTimeoutMilliseconds;

  // Maximum seconds between two link monitor failures to declare this link
  // (network) as unreliable.
  static const int kLinkUnreliableThresholdSeconds;

  static const size_t kHardwareAddressLength;

  // Configure static IP address parameters if the service provides them.
  void ConfigureStaticIPTask();

  // Right now, Devices reference IPConfigs directly when persisted to disk
  // It's not clear that this makes sense long-term, but that's how it is now.
  // This call generates a string in the right format for this persisting.
  // |suffix| is injected into the storage identifier used for the configs.
  std::string SerializeIPConfigs(const std::string& suffix);

  // Set an IP configuration flag on the device. |family| should be "ipv6" or
  // "ipv4". |flag| should be the name of the flag to be set and |value| is
  // what this flag should be set to. Overridden by unit tests to pretend
  // writing to procfs.
  virtual bool SetIPFlag(IPAddress::Family family,
                         const std::string& flag,
                         const std::string& value);

  // Request the removal of reverse-path filtering for this interface.
  // This will allow packets destined for this interface to be accepted,
  // even if this is not the default route for such a packet to arrive.
  void DisableReversePathFilter();

  // Request reverse-path filtering for this interface.
  void EnableReversePathFilter();

  // Disable ARP filtering on the device.  The interface will exhibit the
  // default Linux behavior -- incoming ARP requests are responded to by all
  // interfaces.  Outgoing ARP requests can contain any local address.
  void DisableArpFiltering();

  // Enable ARP filtering on the device.  Incoming ARP requests are responded
  // to only by the interface(s) owning the address.  Outgoing ARP requests
  // will contain the best local address for the target.
  void EnableArpFiltering();

  std::string GetSelectedServiceRpcIdentifier(Error* error);
  std::vector<std::string> AvailableIPConfigs(Error* error);

  // Get the LinkMonitor's average response time.
  uint64_t GetLinkMonitorResponseTime(Error* error);

  // Get receive and transmit byte counters. These methods simply wrap
  // GetReceiveByteCount and GetTransmitByteCount in order to be used by
  // HelpRegisterConstDerivedUint64.
  uint64_t GetReceiveByteCountProperty(Error* error);
  uint64_t GetTransmitByteCountProperty(Error* error);

  // Emit a property change signal for the "IPConfigs" property of this device.
  void UpdateIPConfigsProperty();

  // Called by DNS server tester when the fallback DNS servers test completes.
  void FallbackDNSResultCallback(const DNSServerTester::Status status);

  // Called by DNS server tester when the configured DNS servers test completes.
  void ConfigDNSResultCallback(const DNSServerTester::Status status);

  // Update DNS setting with the given DNS servers for the current connection.
  void SwitchDNSServers(const std::vector<std::string>& dns_servers);

  // Called when the lifetime for IPv6 DNS server expires.
  void IPv6DNSServerExpired();

  // Return true if given IP configuration contain both IP address and DNS
  // servers. Hence, ready to be used for network connection.
  bool IPConfigCompleted(const IPConfigRefPtr& ipconfig);

  // Setup network connection with given IP configuration, and start portal
  // detection on that connection.
  void SetupConnection(const IPConfigRefPtr& ipconfig);

  // Set the system hostname to |hostname| if this device is configured to
  // do so.  If |hostname| is too long, truncate this parameter to fit within
  // the maximum hostname size.
  bool SetHostname(const std::string& hostname);

  // Prepend the Manager's configured list of DNS servers into |ipconfig|
  // ensuring that only DNS servers of the same address family as |ipconfig| are
  // included in the final list.
  void PrependDNSServersIntoIPConfig(const IPConfigRefPtr& ipconfig);

  // Mutate |servers| to include the Manager's prepended list of DNS servers for
  // |family|.  On return, it is guaranteed that there are no duplicate entries
  // in |servers|.
  void PrependDNSServers(const IPAddress::Family family,
                         std::vector<std::string>* servers);

  // Called by |connection_diagnostics| after diagnostics have finished.
  void ConnectionDiagnosticsCallback(
      const std::string& connection_issue,
      const std::vector<ConnectionDiagnostics::Event>& diagnostic_events);

  // |enabled_persistent_| is the value of the Powered property, as
  // read from the profile. If it is not found in the profile, it
  // defaults to true. |enabled_| reflects the real-time state of
  // the device, i.e., enabled or disabled. |enabled_pending_| reflects
  // the target state of the device while an enable or disable operation
  // is occurring.
  //
  // Some typical sequences for these state variables are shown below.
  //
  // Shill starts up, profile has been read:
  //  |enabled_persistent_|=true   |enabled_|=false   |enabled_pending_|=false
  //
  // Shill acts on the value of |enabled_persistent_|, calls SetEnabled(true):
  //  |enabled_persistent_|=true   |enabled_|=false   |enabled_pending_|=true
  //
  // SetEnabled completes successfully, device is enabled:
  //  |enabled_persistent_|=true   |enabled_|=true    |enabled_pending_|=true
  //
  // User presses "Disable" button, SetEnabled(false) is called:
  //  |enabled_persistent_|=false   |enabled_|=true    |enabled_pending_|=false
  //
  // SetEnabled completes successfully, device is disabled:
  //  |enabled_persistent_|=false   |enabled_|=false    |enabled_pending_|=false
  bool enabled_;
  bool enabled_persistent_;
  bool enabled_pending_;

  // Other properties
  bool reconnect_;
  const std::string hardware_address_;

  PropertyStore store_;

  const int interface_index_;
  bool running_;  // indicates whether the device is actually in operation
  const std::string link_name_;
  const std::string unique_id_;
  ControlInterface* control_interface_;
  EventDispatcher* dispatcher_;
  Metrics* metrics_;
  Manager* manager_;
  IPConfigRefPtr ipconfig_;
  IPConfigRefPtr ip6config_;
  IPConfigRefPtr dhcpv6_config_;
  ConnectionRefPtr connection_;
  base::WeakPtrFactory<Device> weak_ptr_factory_;
  std::unique_ptr<DeviceAdaptorInterface> adaptor_;
  std::unique_ptr<PortalDetector> portal_detector_;
  std::unique_ptr<LinkMonitor> link_monitor_;
  // Used for verifying whether DNS server is functional.
  std::unique_ptr<DNSServerTester> dns_server_tester_;
  base::Callback<void(const PortalDetector::Result&)>
      portal_detector_callback_;
  // Callback to invoke when IPv6 DNS servers lifetime expired.
  base::CancelableClosure ipv6_dns_server_expired_callback_;
  std::unique_ptr<TrafficMonitor> traffic_monitor_;
  // DNS servers obtained from ipconfig (either from DHCP or static config)
  // that are not working.
  std::vector<std::string> config_dns_servers_;
  Technology::Identifier technology_;
  // The number of portal detection attempts from Connected to Online state.
  // This includes all failure/timeout attempts and the final successful
  // attempt.
  int portal_attempts_to_online_;

  // Keep track of the offset between the interface-reported byte counters
  // and our persisted value.
  uint64_t receive_byte_offset_;
  uint64_t transmit_byte_offset_;

  // Maintain a reference to the connected / connecting service
  ServiceRefPtr selected_service_;

  // Cache singleton pointers for performance and test purposes.
  DHCPProvider* dhcp_provider_;
  RTNLHandler* rtnl_handler_;

  // Time when link monitor last failed.
  Time* time_;
  time_t last_link_monitor_failed_time_;
  // Callback to invoke when link becomes reliable again after it was previously
  // unreliable.
  base::CancelableClosure reliable_link_callback_;

  std::unique_ptr<ConnectionTester> connection_tester_;
  base::Callback<void()> connection_tester_callback_;

  // Track whether packets from non-optimal routes will be accepted by this
  // device.  This is referred to as "loose mode" (see RFC3704).
  bool is_loose_routing_;

  // Track the current same-net multi-home state.
  bool is_multi_homed_;

  // Remember which flag files were previously successfully written.
  std::set<std::string> written_flags_;

  std::unique_ptr<ConnectionDiagnostics> connection_diagnostics_;
  base::Callback<void(const std::string&,
                      const std::vector<ConnectionDiagnostics::Event>&)>
      connection_diagnostics_callback_;

  DISALLOW_COPY_AND_ASSIGN(Device);
};

}  // namespace shill

#endif  // SHILL_DEVICE_H_