Commit 5482d5e5 authored by derekjchow's avatar derekjchow Committed by Commit bot

Determine connection type in NetworkChangeNotifierLinux.

Check interface for wireless extensions to determine if it is a wifi connection. Check SIOCETHTOOL for ethernet. GetCurrentConnectionType will return the CONNECTION_UNKNOWN unless all connections are the same type.

R=pauljensen@chromium.org
BUG=160537

Review URL: https://codereview.chromium.org/739983005

Cr-Commit-Position: refs/heads/master@{#314042}
parent 92a354fc
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include <linux/if.h> #include <linux/if.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "base/files/scoped_file.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "net/base/net_util_linux.h"
namespace net { namespace net {
namespace internal { namespace internal {
...@@ -76,26 +78,22 @@ bool GetAddress(const struct nlmsghdr* header, ...@@ -76,26 +78,22 @@ bool GetAddress(const struct nlmsghdr* header,
return true; return true;
} }
// Returns the name for the interface with interface index |interface_index|. } // namespace
// The return value points to a function-scoped static so it may be changed by
// subsequent calls. This function could be replaced with if_indextoname() but // static
// net/if.h cannot be mixed with linux/if.h so we'll stick with exclusively char* AddressTrackerLinux::GetInterfaceName(int interface_index, char* buf) {
// talking to the kernel and not the C library. memset(buf, 0, IFNAMSIZ);
const char* GetInterfaceName(int interface_index) { base::ScopedFD ioctl_socket(socket(AF_INET, SOCK_DGRAM, 0));
int ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0); if (!ioctl_socket.is_valid())
if (ioctl_socket < 0) return buf;
return "";
static struct ifreq ifr; struct ifreq ifr = {};
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = interface_index; ifr.ifr_ifindex = interface_index;
int rv = ioctl(ioctl_socket, SIOCGIFNAME, &ifr);
close(ioctl_socket);
if (rv != 0)
return "";
return ifr.ifr_name;
}
} // namespace if (ioctl(ioctl_socket.get(), SIOCGIFNAME, &ifr) == 0)
strncpy(buf, ifr.ifr_name, IFNAMSIZ - 1);
return buf;
}
AddressTrackerLinux::AddressTrackerLinux() AddressTrackerLinux::AddressTrackerLinux()
: get_interface_name_(GetInterfaceName), : get_interface_name_(GetInterfaceName),
...@@ -103,9 +101,9 @@ AddressTrackerLinux::AddressTrackerLinux() ...@@ -103,9 +101,9 @@ AddressTrackerLinux::AddressTrackerLinux()
link_callback_(base::Bind(&base::DoNothing)), link_callback_(base::Bind(&base::DoNothing)),
tunnel_callback_(base::Bind(&base::DoNothing)), tunnel_callback_(base::Bind(&base::DoNothing)),
netlink_fd_(-1), netlink_fd_(-1),
is_offline_(true), connection_type_initialized_(false),
is_offline_initialized_(false), connection_type_initialized_cv_(&connection_type_lock_),
is_offline_initialized_cv_(&is_offline_lock_), current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
tracking_(false) { tracking_(false) {
} }
...@@ -117,9 +115,9 @@ AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, ...@@ -117,9 +115,9 @@ AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback,
link_callback_(link_callback), link_callback_(link_callback),
tunnel_callback_(tunnel_callback), tunnel_callback_(tunnel_callback),
netlink_fd_(-1), netlink_fd_(-1),
is_offline_(true), connection_type_initialized_(false),
is_offline_initialized_(false), connection_type_initialized_cv_(&connection_type_lock_),
is_offline_initialized_cv_(&is_offline_lock_), current_connection_type_(NetworkChangeNotifier::CONNECTION_NONE),
tracking_(true) { tracking_(true) {
DCHECK(!address_callback.is_null()); DCHECK(!address_callback.is_null());
DCHECK(!link_callback.is_null()); DCHECK(!link_callback.is_null());
...@@ -203,9 +201,9 @@ void AddressTrackerLinux::Init() { ...@@ -203,9 +201,9 @@ void AddressTrackerLinux::Init() {
// Consume pending message to populate links_online_, but don't notify. // Consume pending message to populate links_online_, but don't notify.
ReadMessages(&address_changed, &link_changed, &tunnel_changed); ReadMessages(&address_changed, &link_changed, &tunnel_changed);
{ {
AddressTrackerAutoLock lock(*this, is_offline_lock_); AddressTrackerAutoLock lock(*this, connection_type_lock_);
is_offline_initialized_ = true; connection_type_initialized_ = true;
is_offline_initialized_cv_.Signal(); connection_type_initialized_cv_.Signal();
} }
if (tracking_) { if (tracking_) {
...@@ -221,10 +219,10 @@ void AddressTrackerLinux::Init() { ...@@ -221,10 +219,10 @@ void AddressTrackerLinux::Init() {
void AddressTrackerLinux::AbortAndForceOnline() { void AddressTrackerLinux::AbortAndForceOnline() {
CloseSocket(); CloseSocket();
AddressTrackerAutoLock lock(*this, is_offline_lock_); AddressTrackerAutoLock lock(*this, connection_type_lock_);
is_offline_ = false; current_connection_type_ = NetworkChangeNotifier::CONNECTION_UNKNOWN;
is_offline_initialized_ = true; connection_type_initialized_ = true;
is_offline_initialized_cv_.Signal(); connection_type_initialized_cv_.Signal();
} }
AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const {
...@@ -241,15 +239,12 @@ NetworkChangeNotifier::ConnectionType ...@@ -241,15 +239,12 @@ NetworkChangeNotifier::ConnectionType
AddressTrackerLinux::GetCurrentConnectionType() { AddressTrackerLinux::GetCurrentConnectionType() {
// http://crbug.com/125097 // http://crbug.com/125097
base::ThreadRestrictions::ScopedAllowWait allow_wait; base::ThreadRestrictions::ScopedAllowWait allow_wait;
AddressTrackerAutoLock lock(*this, is_offline_lock_); AddressTrackerAutoLock lock(*this, connection_type_lock_);
// Make sure the initial offline state is set before returning. // Make sure the initial connection type is set before returning.
while (!is_offline_initialized_) { while (!connection_type_initialized_) {
is_offline_initialized_cv_.Wait(); connection_type_initialized_cv_.Wait();
} }
// TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. return current_connection_type_;
// http://crbug.com/160537
return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
NetworkChangeNotifier::CONNECTION_UNKNOWN;
} }
void AddressTrackerLinux::ReadMessages(bool* address_changed, void AddressTrackerLinux::ReadMessages(bool* address_changed,
...@@ -279,15 +274,8 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed, ...@@ -279,15 +274,8 @@ void AddressTrackerLinux::ReadMessages(bool* address_changed,
} }
HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed); HandleMessage(buffer, rv, address_changed, link_changed, tunnel_changed);
} }
if (*link_changed) { if (*link_changed || *address_changed)
bool is_offline; UpdateCurrentConnectionType();
{
AddressTrackerAutoLock lock(*this, online_links_lock_);
is_offline = online_links_.empty();
}
AddressTrackerAutoLock lock(*this, is_offline_lock_);
is_offline_ = is_offline;
}
} }
void AddressTrackerLinux::HandleMessage(char* buffer, void AddressTrackerLinux::HandleMessage(char* buffer,
...@@ -351,14 +339,14 @@ void AddressTrackerLinux::HandleMessage(char* buffer, ...@@ -351,14 +339,14 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
AddressTrackerAutoLock lock(*this, online_links_lock_); AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.insert(msg->ifi_index).second) { if (online_links_.insert(msg->ifi_index).second) {
*link_changed = true; *link_changed = true;
if (IsTunnelInterface(msg)) if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true; *tunnel_changed = true;
} }
} else { } else {
AddressTrackerAutoLock lock(*this, online_links_lock_); AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) { if (online_links_.erase(msg->ifi_index)) {
*link_changed = true; *link_changed = true;
if (IsTunnelInterface(msg)) if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true; *tunnel_changed = true;
} }
} }
...@@ -369,7 +357,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer, ...@@ -369,7 +357,7 @@ void AddressTrackerLinux::HandleMessage(char* buffer,
AddressTrackerAutoLock lock(*this, online_links_lock_); AddressTrackerAutoLock lock(*this, online_links_lock_);
if (online_links_.erase(msg->ifi_index)) { if (online_links_.erase(msg->ifi_index)) {
*link_changed = true; *link_changed = true;
if (IsTunnelInterface(msg)) if (IsTunnelInterface(msg->ifi_index))
*tunnel_changed = true; *tunnel_changed = true;
} }
} break; } break;
...@@ -401,9 +389,41 @@ void AddressTrackerLinux::CloseSocket() { ...@@ -401,9 +389,41 @@ void AddressTrackerLinux::CloseSocket() {
netlink_fd_ = -1; netlink_fd_ = -1;
} }
bool AddressTrackerLinux::IsTunnelInterface(const struct ifinfomsg* msg) const { bool AddressTrackerLinux::IsTunnelInterface(int interface_index) const {
// Linux kernel drivers/net/tun.c uses "tun" name prefix. // Linux kernel drivers/net/tun.c uses "tun" name prefix.
return strncmp(get_interface_name_(msg->ifi_index), "tun", 3) == 0; char buf[IFNAMSIZ] = {0};
return strncmp(get_interface_name_(interface_index, buf), "tun", 3) == 0;
}
void AddressTrackerLinux::UpdateCurrentConnectionType() {
AddressTrackerLinux::AddressMap address_map = GetAddressMap();
base::hash_set<int> online_links = GetOnlineLinks();
// Strip out tunnel interfaces from online_links
for (base::hash_set<int>::const_iterator it = online_links.begin();
it != online_links.end();) {
if (IsTunnelInterface(*it)) {
base::hash_set<int>::const_iterator tunnel_it = it;
++it;
online_links.erase(*tunnel_it);
} else {
++it;
}
}
NetworkInterfaceList networks;
NetworkChangeNotifier::ConnectionType type =
NetworkChangeNotifier::CONNECTION_NONE;
if (GetNetworkListImpl(&networks, 0, online_links, address_map,
get_interface_name_)) {
type = NetworkChangeNotifier::ConnectionTypeFromInterfaceList(networks);
} else {
type = online_links.empty() ? NetworkChangeNotifier::CONNECTION_NONE
: NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
AddressTrackerAutoLock lock(*this, connection_type_lock_);
current_connection_type_ = type;
} }
AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock( AddressTrackerLinux::AddressTrackerAutoLock::AddressTrackerAutoLock(
......
...@@ -65,6 +65,13 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : ...@@ -65,6 +65,13 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
// Safe to call from any thread, but will block until Init() has completed. // Safe to call from any thread, but will block until Init() has completed.
NetworkChangeNotifier::ConnectionType GetCurrentConnectionType(); NetworkChangeNotifier::ConnectionType GetCurrentConnectionType();
// Returns the name for the interface with interface index |interface_index|.
// |buf| should be a pointer to an array of size IFNAMSIZ. The returned
// pointer will point to |buf|. This function acts like if_indextoname which
// cannot be used as net/if.h cannot be mixed with linux/if.h. We'll stick
// with exclusively talking to the kernel and not the C library.
static char* GetInterfaceName(int interface_index, char* buf);
private: private:
friend class AddressTrackerLinuxTest; friend class AddressTrackerLinuxTest;
...@@ -83,8 +90,9 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : ...@@ -83,8 +90,9 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
}; };
// A function that returns the name of an interface given the interface index // A function that returns the name of an interface given the interface index
// in |interface_index|. // in |interface_index|. |ifname| should be a buffer of size IFNAMSIZ. The
typedef const char* (*GetInterfaceNameFunction)(int interface_index); // function should return a pointer to |ifname|.
typedef char* (*GetInterfaceNameFunction)(int interface_index, char* ifname);
// Sets |*address_changed| to indicate whether |address_map_| changed and // Sets |*address_changed| to indicate whether |address_map_| changed and
// sets |*link_changed| to indicate if |online_links_| changed and sets // sets |*link_changed| to indicate if |online_links_| changed and sets
...@@ -114,8 +122,11 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : ...@@ -114,8 +122,11 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
// Close |netlink_fd_| // Close |netlink_fd_|
void CloseSocket(); void CloseSocket();
// Does |msg| refer to a tunnel interface? // Does |interface_index| refer to a tunnel interface?
bool IsTunnelInterface(const struct ifinfomsg* msg) const; bool IsTunnelInterface(int interface_index) const;
// Updates current_connection_type_ based on the network list.
void UpdateCurrentConnectionType();
// Gets the name of an interface given the interface index |interface_index|. // Gets the name of an interface given the interface index |interface_index|.
// May return empty string if it fails but should not return NULL. This is // May return empty string if it fails but should not return NULL. This is
...@@ -136,10 +147,10 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux : ...@@ -136,10 +147,10 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux :
mutable base::Lock online_links_lock_; mutable base::Lock online_links_lock_;
base::hash_set<int> online_links_; base::hash_set<int> online_links_;
base::Lock is_offline_lock_; base::Lock connection_type_lock_;
bool is_offline_; bool connection_type_initialized_;
bool is_offline_initialized_; base::ConditionVariable connection_type_initialized_cv_;
base::ConditionVariable is_offline_initialized_cv_; NetworkChangeNotifier::ConnectionType current_connection_type_;
bool tracking_; bool tracking_;
// Used to verify single-threaded access in non-tracking mode. // Used to verify single-threaded access in non-tracking mode.
......
...@@ -22,10 +22,10 @@ namespace { ...@@ -22,10 +22,10 @@ namespace {
const int kTestInterfaceTun = 123; const int kTestInterfaceTun = 123;
const char* TestGetInterfaceName(int interface_index) { char* TestGetInterfaceName(int interface_index, char* buf) {
if (interface_index == kTestInterfaceTun) if (interface_index == kTestInterfaceTun)
return "tun0"; return strncpy(buf, "tun0", IFNAMSIZ);
return "eth0"; return strncpy(buf, "eth0", IFNAMSIZ);
} }
} // namespace } // namespace
...@@ -520,8 +520,10 @@ TEST_F(AddressTrackerLinuxTest, TunnelInterface) { ...@@ -520,8 +520,10 @@ TEST_F(AddressTrackerLinuxTest, TunnelInterface) {
TEST_F(AddressTrackerLinuxTest, GetInterfaceName) { TEST_F(AddressTrackerLinuxTest, GetInterfaceName) {
InitializeAddressTracker(true); InitializeAddressTracker(true);
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++) {
EXPECT_NE((const char*)NULL, original_get_interface_name_(i)); char buf[IFNAMSIZ] = {0};
EXPECT_NE((const char*)NULL, original_get_interface_name_(i, buf));
}
} }
TEST_F(AddressTrackerLinuxTest, NonTrackingMode) { TEST_F(AddressTrackerLinuxTest, NonTrackingMode) {
......
...@@ -4,12 +4,18 @@ ...@@ -4,12 +4,18 @@
#include "net/base/net_util_linux.h" #include "net/base/net_util_linux.h"
#include <net/if.h> #if !defined(OS_ANDROID)
#include <netinet/in.h> #include <linux/ethtool.h>
#endif // !defined(OS_ANDROID)
#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/wireless.h>
#include <set> #include <set>
#include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
...@@ -69,6 +75,34 @@ inline const unsigned char* GetIPAddressData(const IPAddressNumber& ip) { ...@@ -69,6 +75,34 @@ inline const unsigned char* GetIPAddressData(const IPAddressNumber& ip) {
#endif #endif
} }
// Gets the connection type for interface |ifname| by checking for wireless
// or ethtool extensions.
NetworkChangeNotifier::ConnectionType GetInterfaceConnectionType(
const std::string& ifname) {
base::ScopedFD s(socket(AF_INET, SOCK_STREAM, 0));
if (!s.is_valid())
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
// Test wireless extensions for CONNECTION_WIFI
struct iwreq pwrq = {};
strncpy(pwrq.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
if (ioctl(s.get(), SIOCGIWNAME, &pwrq) != -1)
return NetworkChangeNotifier::CONNECTION_WIFI;
#if !defined(OS_ANDROID)
// Test ethtool for CONNECTION_ETHERNET
struct ethtool_cmd ecmd = {};
ecmd.cmd = ETHTOOL_GSET;
struct ifreq ifr = {};
ifr.ifr_data = &ecmd;
strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ - 1);
if (ioctl(s.get(), SIOCETHTOOL, &ifr) != -1)
return NetworkChangeNotifier::CONNECTION_ETHERNET;
#endif // !defined(OS_ANDROID)
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
bool GetNetworkListImpl( bool GetNetworkListImpl(
NetworkInterfaceList* networks, NetworkInterfaceList* networks,
int policy, int policy,
...@@ -112,13 +146,12 @@ bool GetNetworkListImpl( ...@@ -112,13 +146,12 @@ bool GetNetworkListImpl(
ifnames.find(it->second.ifa_index); ifnames.find(it->second.ifa_index);
std::string ifname; std::string ifname;
if (itname == ifnames.end()) { if (itname == ifnames.end()) {
char buffer[IF_NAMESIZE] = {0}; char buffer[IFNAMSIZ] = {0};
if (get_interface_name(it->second.ifa_index, buffer)) { ifname.assign(get_interface_name(it->second.ifa_index, buffer));
ifname = ifnames[it->second.ifa_index] = buffer; // Ignore addresses whose interface name can't be retrieved.
} else { if (ifname.empty())
// Ignore addresses whose interface name can't be retrieved.
continue; continue;
} ifnames[it->second.ifa_index] = ifname;
} else { } else {
ifname = itname->second; ifname = itname->second;
} }
...@@ -128,9 +161,11 @@ bool GetNetworkListImpl( ...@@ -128,9 +161,11 @@ bool GetNetworkListImpl(
if (ShouldIgnoreInterface(ifname, policy)) if (ShouldIgnoreInterface(ifname, policy))
continue; continue;
NetworkChangeNotifier::ConnectionType type =
GetInterfaceConnectionType(ifname);
networks->push_back( networks->push_back(
NetworkInterface(ifname, ifname, it->second.ifa_index, NetworkInterface(ifname, ifname, it->second.ifa_index, type, it->first,
NetworkChangeNotifier::CONNECTION_UNKNOWN, it->first,
it->second.ifa_prefixlen, ip_attributes)); it->second.ifa_prefixlen, ip_attributes));
} }
...@@ -146,9 +181,9 @@ bool GetNetworkList(NetworkInterfaceList* networks, int policy) { ...@@ -146,9 +181,9 @@ bool GetNetworkList(NetworkInterfaceList* networks, int policy) {
internal::AddressTrackerLinux tracker; internal::AddressTrackerLinux tracker;
tracker.Init(); tracker.Init();
return internal::GetNetworkListImpl(networks, policy, return internal::GetNetworkListImpl(
tracker.GetOnlineLinks(), networks, policy, tracker.GetOnlineLinks(), tracker.GetAddressMap(),
tracker.GetAddressMap(), &if_indextoname); &internal::AddressTrackerLinux::GetInterfaceName);
} }
} // namespace net } // namespace net
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#define NET_BASE_NET_UTIL_LINUX_H_ #define NET_BASE_NET_UTIL_LINUX_H_
// This file is only used to expose some of the internals // This file is only used to expose some of the internals
// of net_util_linux.cc to tests. // of net_util_linux.cc to address_tracker_linux and tests.
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
#include "net/base/address_tracker_linux.h" #include "net/base/address_tracker_linux.h"
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
namespace net { namespace net {
namespace internal { namespace internal {
typedef char* (*GetInterfaceNameFunction)(unsigned int interface_index, typedef char* (*GetInterfaceNameFunction)(int interface_index, char* ifname);
char* ifname);
NET_EXPORT bool GetNetworkListImpl( NET_EXPORT bool GetNetworkListImpl(
NetworkInterfaceList* networks, NetworkInterfaceList* networks,
......
...@@ -913,11 +913,11 @@ char* CopyInterfaceName(const char* ifname, int ifname_size, char* output) { ...@@ -913,11 +913,11 @@ char* CopyInterfaceName(const char* ifname, int ifname_size, char* output) {
return output; return output;
} }
char* GetInterfaceName(unsigned int interface_index, char* ifname) { char* GetInterfaceName(int interface_index, char* ifname) {
return CopyInterfaceName(ifname_em1, arraysize(ifname_em1), ifname); return CopyInterfaceName(ifname_em1, arraysize(ifname_em1), ifname);
} }
char* GetInterfaceNameVM(unsigned int interface_index, char* ifname) { char* GetInterfaceNameVM(int interface_index, char* ifname) {
return CopyInterfaceName(ifname_vm, arraysize(ifname_vm), ifname); return CopyInterfaceName(ifname_vm, arraysize(ifname_vm), ifname);
} }
......
...@@ -654,6 +654,27 @@ bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type) { ...@@ -654,6 +654,27 @@ bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type) {
return is_cellular; return is_cellular;
} }
// 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 (first) {
first = false;
result = interfaces[i].type;
} else if (result != interfaces[i].type) {
return CONNECTION_UNKNOWN;
}
}
return result;
}
// static // static
NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() { NetworkChangeNotifier* NetworkChangeNotifier::CreateMock() {
return new MockNetworkChangeNotifier(); return new MockNetworkChangeNotifier();
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_H_ #ifndef NET_BASE_NETWORK_CHANGE_NOTIFIER_H_
#define NET_BASE_NETWORK_CHANGE_NOTIFIER_H_ #define NET_BASE_NETWORK_CHANGE_NOTIFIER_H_
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/observer_list_threadsafe.h" #include "base/observer_list_threadsafe.h"
#include "base/time/time.h" #include "base/time/time.h"
...@@ -17,6 +19,8 @@ namespace net { ...@@ -17,6 +19,8 @@ namespace net {
struct DnsConfig; struct DnsConfig;
class HistogramWatcher; class HistogramWatcher;
class NetworkChangeNotifierFactory; class NetworkChangeNotifierFactory;
struct NetworkInterface;
typedef std::vector<NetworkInterface> NetworkInterfaceList;
class URLRequest; class URLRequest;
#if defined(OS_LINUX) #if defined(OS_LINUX)
...@@ -258,6 +262,13 @@ class NET_EXPORT NetworkChangeNotifier { ...@@ -258,6 +262,13 @@ class NET_EXPORT NetworkChangeNotifier {
// current connection is cellular. // current connection is cellular.
static bool IsConnectionCellular(ConnectionType type); static bool IsConnectionCellular(ConnectionType type);
// Gets the current connection type based on |interfaces|. Returns
// CONNECTION_NONE if there are no interfaces, CONNECTION_UNKNOWN if two
// interfaces have different connection types or the connection type of all
// interfaces if they have the same interface type.
static ConnectionType ConnectionTypeFromInterfaceList(
const NetworkInterfaceList& interfaces);
// Like Create(), but for use in tests. The mock object doesn't monitor any // Like Create(), but for use in tests. The mock object doesn't monitor any
// events, it merely rebroadcasts notifications when requested. // events, it merely rebroadcasts notifications when requested.
static NetworkChangeNotifier* CreateMock(); static NetworkChangeNotifier* CreateMock();
......
...@@ -33,9 +33,12 @@ class NetworkChangeNotifierLinux::Thread : public base::Thread { ...@@ -33,9 +33,12 @@ class NetworkChangeNotifierLinux::Thread : public base::Thread {
void CleanUp() override; void CleanUp() override;
private: private:
void OnIPAddressChanged();
void OnLinkChanged();
scoped_ptr<DnsConfigService> dns_config_service_; scoped_ptr<DnsConfigService> dns_config_service_;
// Used to detect online/offline state and IP address changes. // Used to detect online/offline state and IP address changes.
internal::AddressTrackerLinux address_tracker_; internal::AddressTrackerLinux address_tracker_;
NetworkChangeNotifier::ConnectionType last_type_;
DISALLOW_COPY_AND_ASSIGN(Thread); DISALLOW_COPY_AND_ASSIGN(Thread);
}; };
...@@ -43,11 +46,12 @@ class NetworkChangeNotifierLinux::Thread : public base::Thread { ...@@ -43,11 +46,12 @@ class NetworkChangeNotifierLinux::Thread : public base::Thread {
NetworkChangeNotifierLinux::Thread::Thread() NetworkChangeNotifierLinux::Thread::Thread()
: base::Thread("NetworkChangeNotifier"), : base::Thread("NetworkChangeNotifier"),
address_tracker_( address_tracker_(
base::Bind(&NetworkChangeNotifier:: base::Bind(&NetworkChangeNotifierLinux::Thread::OnIPAddressChanged,
NotifyObserversOfIPAddressChange), base::Unretained(this)),
base::Bind(&NetworkChangeNotifier:: base::Bind(&NetworkChangeNotifierLinux::Thread::OnLinkChanged,
NotifyObserversOfConnectionTypeChange), base::Unretained(this)),
base::Bind(base::DoNothing)) { base::Bind(base::DoNothing)),
last_type_(NetworkChangeNotifier::CONNECTION_NONE) {
} }
NetworkChangeNotifierLinux::Thread::~Thread() { NetworkChangeNotifierLinux::Thread::~Thread() {
...@@ -65,6 +69,20 @@ void NetworkChangeNotifierLinux::Thread::CleanUp() { ...@@ -65,6 +69,20 @@ void NetworkChangeNotifierLinux::Thread::CleanUp() {
dns_config_service_.reset(); 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() { NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() {
return new NetworkChangeNotifierLinux(); return new NetworkChangeNotifierLinux();
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "net/base/network_change_notifier.h" #include "net/base/network_change_notifier.h"
#include "net/base/net_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace net { namespace net {
...@@ -57,4 +58,33 @@ TEST(NetworkChangeNotifierTest, NetMaxBandwidthRange) { ...@@ -57,4 +58,33 @@ TEST(NetworkChangeNotifierTest, NetMaxBandwidthRange) {
} }
} }
TEST(NetworkChangeNotifierTest, ConnectionTypeFromInterfaceList) {
NetworkInterfaceList list;
// Test empty list.
EXPECT_EQ(NetworkChangeNotifier::ConnectionTypeFromInterfaceList(list),
NetworkChangeNotifier::CONNECTION_NONE);
for (int i = NetworkChangeNotifier::CONNECTION_UNKNOWN;
i <= NetworkChangeNotifier::CONNECTION_LAST; i++) {
// Check individual types.
NetworkInterface interface;
interface.type = static_cast<NetworkChangeNotifier::ConnectionType>(i);
list.clear();
list.push_back(interface);
EXPECT_EQ(NetworkChangeNotifier::ConnectionTypeFromInterfaceList(list), i);
// Check two types.
for (int j = NetworkChangeNotifier::CONNECTION_UNKNOWN;
j <= NetworkChangeNotifier::CONNECTION_LAST; j++) {
list.clear();
interface.type = static_cast<NetworkChangeNotifier::ConnectionType>(i);
list.push_back(interface);
interface.type = static_cast<NetworkChangeNotifier::ConnectionType>(j);
list.push_back(interface);
EXPECT_EQ(NetworkChangeNotifier::ConnectionTypeFromInterfaceList(list),
i == j ? i : NetworkChangeNotifier::CONNECTION_UNKNOWN);
}
}
}
} // namespace net } // namespace net
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment