diff options
author | pauljensen@chromium.org <pauljensen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 17:11:55 +0000 |
---|---|---|
committer | pauljensen@chromium.org <pauljensen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-29 17:11:55 +0000 |
commit | d3eada55f844f6c5a698f48ebc4a2197885b5135 (patch) | |
tree | 844cf611b9bdeadb7164516240e6f3074acb2c9d /net/base | |
parent | c46c2a993a37c4f2e7b69319abbbbbc63f7ba019 (diff) | |
download | chromium_src-d3eada55f844f6c5a698f48ebc4a2197885b5135.zip chromium_src-d3eada55f844f6c5a698f48ebc4a2197885b5135.tar.gz chromium_src-d3eada55f844f6c5a698f48ebc4a2197885b5135.tar.bz2 |
Use Netlink instead of NetworkManager (via D-bus) to monitor network
connectivity on Linux.
BUG=117973
Review URL: https://chromiumcodereview.appspot.com/11359141
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170211 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/address_tracker_linux.cc | 214 | ||||
-rw-r--r-- | net/base/address_tracker_linux.h | 49 | ||||
-rw-r--r-- | net/base/address_tracker_linux_unittest.cc | 209 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.cc | 257 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux.h | 9 | ||||
-rw-r--r-- | net/base/network_change_notifier_linux_unittest.cc | 243 |
6 files changed, 359 insertions, 622 deletions
diff --git a/net/base/address_tracker_linux.cc b/net/base/address_tracker_linux.cc index 52bc928..67eaa69 100644 --- a/net/base/address_tracker_linux.cc +++ b/net/base/address_tracker_linux.cc @@ -5,9 +5,11 @@ #include "net/base/address_tracker_linux.h" #include <errno.h> +#include <linux/if.h> #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "base/threading/thread_restrictions.h" #include "net/base/network_change_notifier_linux.h" namespace net { @@ -63,28 +65,29 @@ bool GetAddress(const struct nlmsghdr* header, IPAddressNumber* out) { return true; } -void CloseSocket(int fd) { - if (HANDLE_EINTR(close(fd)) < 0) - PLOG(ERROR) << "Could not close NETLINK socket."; -} - } // namespace -AddressTrackerLinux::AddressTrackerLinux(const base::Closure& callback) - : callback_(callback), - netlink_fd_(-1) { - DCHECK(!callback.is_null()); +AddressTrackerLinux::AddressTrackerLinux(const base::Closure& address_callback, + const base::Closure& link_callback) + : address_callback_(address_callback), + link_callback_(link_callback), + netlink_fd_(-1), + is_offline_(true), + is_offline_initialized_(false), + is_offline_initialized_cv_(&is_offline_lock_) { + DCHECK(!address_callback.is_null()); + DCHECK(!link_callback.is_null()); } AddressTrackerLinux::~AddressTrackerLinux() { - if (netlink_fd_ >= 0) - CloseSocket(netlink_fd_); + CloseSocket(); } void AddressTrackerLinux::Init() { - int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (sock < 0) { + netlink_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlink_fd_ < 0) { PLOG(ERROR) << "Could not create NETLINK socket"; + AbortAndForceOnline(); return; } @@ -93,26 +96,14 @@ void AddressTrackerLinux::Init() { addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); // TODO(szym): Track RTMGRP_LINK as well for ifi_type, http://crbug.com/113993 - addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY; - int rv = bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)); + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | + RTMGRP_LINK; + int rv = bind(netlink_fd_, + reinterpret_cast<struct sockaddr*>(&addr), + sizeof(addr)); if (rv < 0) { PLOG(ERROR) << "Could not bind NETLINK socket"; - CloseSocket(sock); - return; - } - - // Watch for asynchronous messages. - if (SetNonBlocking(sock)) { - PLOG(ERROR) << "Could not make NETLINK socket non-blocking"; - CloseSocket(sock); - return; - } - - rv = MessageLoopForIO::current()->WatchFileDescriptor( - sock, true, MessageLoopForIO::WATCH_READ, &watcher_, this); - if (rv < 0) { - PLOG(ERROR) << "Could not watch NETLINK socket"; - CloseSocket(sock); + AbortAndForceOnline(); return; } @@ -125,105 +116,200 @@ void AddressTrackerLinux::Init() { struct rtgenmsg msg; } request = {}; - request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request.msg)); + request.header.nlmsg_len = NLMSG_LENGTH(sizeof(request)); request.header.nlmsg_type = RTM_GETADDR; request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; request.header.nlmsg_pid = getpid(); request.msg.rtgen_family = AF_UNSPEC; - rv = HANDLE_EINTR(sendto(sock, &request, request.header.nlmsg_len, 0, + rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, + 0, reinterpret_cast<struct sockaddr*>(&peer), + sizeof(peer))); + if (rv < 0) { + PLOG(ERROR) << "Could not send NETLINK request"; + AbortAndForceOnline(); + return; + } + + // Consume pending message to populate the AddressMap, but don't notify. + // Sending another request without first reading responses results in EBUSY. + bool address_changed; + bool link_changed; + ReadMessages(&address_changed, &link_changed); + + // Request dump of link state + request.header.nlmsg_type = RTM_GETLINK; + + rv = HANDLE_EINTR(sendto(netlink_fd_, &request, request.header.nlmsg_len, 0, reinterpret_cast<struct sockaddr*>(&peer), sizeof(peer))); if (rv < 0) { PLOG(ERROR) << "Could not send NETLINK request"; - CloseSocket(sock); + AbortAndForceOnline(); return; } - netlink_fd_ = sock; + // Consume pending message to populate links_online_, but don't notify. + ReadMessages(&address_changed, &link_changed); + { + base::AutoLock lock(is_offline_lock_); + is_offline_initialized_ = true; + is_offline_initialized_cv_.Signal(); + } + + rv = MessageLoopForIO::current()->WatchFileDescriptor( + netlink_fd_, true, MessageLoopForIO::WATCH_READ, &watcher_, this); + if (rv < 0) { + PLOG(ERROR) << "Could not watch NETLINK socket"; + AbortAndForceOnline(); + return; + } +} - // Consume any pending messages to populate the AddressMap, but don't notify. - ReadMessages(); +void AddressTrackerLinux::AbortAndForceOnline() { + CloseSocket(); + base::AutoLock lock(is_offline_lock_); + is_offline_ = false; + is_offline_initialized_ = true; + is_offline_initialized_cv_.Signal(); } AddressTrackerLinux::AddressMap AddressTrackerLinux::GetAddressMap() const { - base::AutoLock lock(lock_); - return map_; + base::AutoLock lock(address_map_lock_); + return address_map_; +} + +NetworkChangeNotifier::ConnectionType +AddressTrackerLinux::GetCurrentConnectionType() { + // http://crbug.com/125097 + base::ThreadRestrictions::ScopedAllowWait allow_wait; + base::AutoLock lock(is_offline_lock_); + // Make sure the initial offline state is set before returning. + while (!is_offline_initialized_) { + is_offline_initialized_cv_.Wait(); + } + // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. + // http://crbug.com/160537 + return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : + NetworkChangeNotifier::CONNECTION_UNKNOWN; } -bool AddressTrackerLinux::ReadMessages() { +void AddressTrackerLinux::ReadMessages(bool* address_changed, + bool* link_changed) { + *address_changed = false; + *link_changed = false; char buffer[4096]; - bool changed = false; + bool first_loop = true; for (;;) { - int rv = HANDLE_EINTR(recv(netlink_fd_, buffer, sizeof(buffer), 0)); + int rv = HANDLE_EINTR(recv(netlink_fd_, + buffer, + sizeof(buffer), + // Block the first time through loop. + first_loop ? 0 : MSG_DONTWAIT)); + first_loop = false; if (rv == 0) { LOG(ERROR) << "Unexpected shutdown of NETLINK socket."; - return false; + return; } if (rv < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) break; PLOG(ERROR) << "Failed to recv from netlink socket"; - return false; + return; } - changed |= HandleMessage(buffer, rv); + HandleMessage(buffer, rv, address_changed, link_changed); }; - return changed; + if (*link_changed) { + base::AutoLock lock(is_offline_lock_); + is_offline_ = online_links_.empty(); + } } -bool AddressTrackerLinux::HandleMessage(const char* buffer, size_t length) { +void AddressTrackerLinux::HandleMessage(const char* buffer, + size_t length, + bool* address_changed, + bool* link_changed) { DCHECK(buffer); - bool changed = false; for (const struct nlmsghdr* header = reinterpret_cast<const struct nlmsghdr*>(buffer); NLMSG_OK(header, length); header = NLMSG_NEXT(header, length)) { switch (header->nlmsg_type) { case NLMSG_DONE: - return changed; - case NLMSG_ERROR: - LOG(ERROR) << "Unexpected netlink error."; - return changed; + return; + case NLMSG_ERROR: { + const struct nlmsgerr* msg = + reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(header)); + LOG(ERROR) << "Unexpected netlink error " << msg->error << "."; + } return; case RTM_NEWADDR: { IPAddressNumber address; if (GetAddress(header, &address)) { - base::AutoLock lock(lock_); + base::AutoLock lock(address_map_lock_); const struct ifaddrmsg* msg = reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(header)); // Only indicate change if the address is new or ifaddrmsg info has // changed. - AddressMap::iterator it = map_.find(address); - if (it == map_.end()) { - map_.insert(it, std::make_pair(address, *msg)); - changed = true; + AddressMap::iterator it = address_map_.find(address); + if (it == address_map_.end()) { + address_map_.insert(it, std::make_pair(address, *msg)); + *address_changed = true; } else if (memcmp(&it->second, msg, sizeof(*msg))) { it->second = *msg; - changed = true; + *address_changed = true; } } } break; case RTM_DELADDR: { IPAddressNumber address; if (GetAddress(header, &address)) { - base::AutoLock lock(lock_); - if (map_.erase(address)) - changed = true; + base::AutoLock lock(address_map_lock_); + if (address_map_.erase(address)) + *address_changed = true; + } + } break; + case RTM_NEWLINK: { + const struct ifinfomsg* msg = + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); + if (!(msg->ifi_flags & IFF_LOOPBACK) && (msg->ifi_flags & IFF_UP) && + (msg->ifi_flags & IFF_LOWER_UP) && (msg->ifi_flags & IFF_RUNNING)) { + if (online_links_.insert(msg->ifi_index).second) + *link_changed = true; + } else { + if (online_links_.erase(msg->ifi_index)) + *link_changed = true; } } break; + case RTM_DELLINK: { + const struct ifinfomsg* msg = + reinterpret_cast<struct ifinfomsg*>(NLMSG_DATA(header)); + if (online_links_.erase(msg->ifi_index)) + *link_changed = true; + } break; default: break; } } - return changed; } void AddressTrackerLinux::OnFileCanReadWithoutBlocking(int fd) { DCHECK_EQ(netlink_fd_, fd); - if (ReadMessages()) - callback_.Run(); + bool address_changed; + bool link_changed; + ReadMessages(&address_changed, &link_changed); + if (address_changed) + address_callback_.Run(); + if (link_changed) + link_callback_.Run(); } void AddressTrackerLinux::OnFileCanWriteWithoutBlocking(int /* fd */) {} +void AddressTrackerLinux::CloseSocket() { + if (netlink_fd_ >= 0 && HANDLE_EINTR(close(netlink_fd_)) < 0) + PLOG(ERROR) << "Could not close NETLINK socket."; + netlink_fd_ = -1; +} + } // namespace internal } // namespace net diff --git a/net/base/address_tracker_linux.h b/net/base/address_tracker_linux.h index a70a14c..87ce139 100644 --- a/net/base/address_tracker_linux.h +++ b/net/base/address_tracker_linux.h @@ -16,9 +16,12 @@ #include "base/basictypes.h" #include "base/callback.h" #include "base/compiler_specific.h" +#include "base/hash_tables.h" #include "base/message_loop.h" +#include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "net/base/net_util.h" +#include "net/base/network_change_notifier.h" namespace net { namespace internal { @@ -30,8 +33,10 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux public: typedef std::map<IPAddressNumber, struct ifaddrmsg> AddressMap; - // Will run |callback| when the AddressMap changes. - explicit AddressTrackerLinux(const base::Closure& callback); + // Will run |address_callback| when the AddressMap changes and will run + // |link_callback| when the list of online links changes. + AddressTrackerLinux(const base::Closure& address_callback, + const base::Closure& link_callback); virtual ~AddressTrackerLinux(); // Starts watching system configuration for changes. The current thread must @@ -40,26 +45,52 @@ class NET_EXPORT_PRIVATE AddressTrackerLinux AddressMap GetAddressMap() const; + // Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType(). + // Safe to call from any thread, but will block until Init() has completed. + NetworkChangeNotifier::ConnectionType GetCurrentConnectionType(); + private: friend class AddressTrackerLinuxTest; - // Returns true if |map_| changed while reading messages from |netlink_fd_|. - bool ReadMessages(); + // Sets |*address_changed| to indicate whether |address_map_| changed and + // sets |*link_changed| to indicate if |online_links_| changed while reading + // messages from |netlink_fd_|. + void ReadMessages(bool* address_changed, bool* link_changed); + + // Sets |*address_changed| to true if |address_map_| changed, sets + // |*link_changed| to true if |online_links_| changed while reading the + // message from |buffer|. + void HandleMessage(const char* buffer, + size_t length, + bool* address_changed, + bool* link_changed); - // Returns true if |map_| changed while reading the message from |buffer|. - bool HandleMessage(const char* buffer, size_t length); + // Call when some part of initialization failed; forces online and unblocks. + void AbortAndForceOnline(); // MessageLoopForIO::Watcher: virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE; - base::Closure callback_; + // Close |netlink_fd_| + void CloseSocket(); + + base::Closure address_callback_; + base::Closure link_callback_; int netlink_fd_; MessageLoopForIO::FileDescriptorWatcher watcher_; - mutable base::Lock lock_; - AddressMap map_; + mutable base::Lock address_map_lock_; + AddressMap address_map_; + + // Set of interface indices for links that are currently online. + base::hash_set<int> online_links_; + + base::Lock is_offline_lock_; + bool is_offline_; + bool is_offline_initialized_; + base::ConditionVariable is_offline_initialized_cv_; }; } // namespace internal diff --git a/net/base/address_tracker_linux_unittest.cc b/net/base/address_tracker_linux_unittest.cc index c366f5a..a9feed4 100644 --- a/net/base/address_tracker_linux_unittest.cc +++ b/net/base/address_tracker_linux_unittest.cc @@ -4,6 +4,8 @@ #include "net/base/address_tracker_linux.h" +#include <linux/if.h> + #include <vector> #include "base/bind.h" @@ -12,27 +14,45 @@ namespace net { namespace internal { +typedef std::vector<char> Buffer; + void Noop() {} class AddressTrackerLinuxTest : public testing::Test { protected: - AddressTrackerLinuxTest() : tracker_(base::Bind(&Noop)) {} + AddressTrackerLinuxTest() : tracker_(base::Bind(&Noop), base::Bind(&Noop)) {} + + bool HandleAddressMessage(const Buffer& buf) { + bool address_changed = false; + bool link_changed = false; + tracker_.HandleMessage(&buf[0], buf.size(), + &address_changed, &link_changed); + EXPECT_FALSE(link_changed); + return address_changed; + } - bool HandleMessage(char* buf, size_t length) { - return tracker_.HandleMessage(buf, length); + bool HandleLinkMessage(const Buffer& buf) { + bool address_changed = false; + bool link_changed = false; + tracker_.HandleMessage(&buf[0], buf.size(), + &address_changed, &link_changed); + EXPECT_FALSE(address_changed); + return link_changed; } AddressTrackerLinux::AddressMap GetAddressMap() { return tracker_.GetAddressMap(); } + const base::hash_set<int>* GetOnlineLinks() const { + return &tracker_.online_links_; + } + AddressTrackerLinux tracker_; }; namespace { -typedef std::vector<char> Buffer; - class NetlinkMessage { public: explicit NetlinkMessage(uint16 type) : buffer_(NLMSG_HDRLEN) { @@ -83,12 +103,12 @@ class NetlinkMessage { Buffer buffer_; }; -void MakeMessage(uint16 type, - uint8 flags, - uint8 family, - const IPAddressNumber& address, - const IPAddressNumber& local, - Buffer* output) { +void MakeAddrMessage(uint16 type, + uint8 flags, + uint8 family, + const IPAddressNumber& address, + const IPAddressNumber& local, + Buffer* output) { NetlinkMessage nlmsg(type); struct ifaddrmsg msg = {}; msg.ifa_family = family; @@ -101,6 +121,16 @@ void MakeMessage(uint16 type, nlmsg.AppendTo(output); } +void MakeLinkMessage(uint16 type, uint32 flags, uint32 index, Buffer* output) { + NetlinkMessage nlmsg(type); + struct ifinfomsg msg = {}; + msg.ifi_index = index; + msg.ifi_flags = flags; + nlmsg.AddPayload(&msg, sizeof(msg)); + output->clear(); + nlmsg.AppendTo(output); +} + const unsigned char kAddress0[] = { 127, 0, 0, 1 }; const unsigned char kAddress1[] = { 10, 0, 0, 1 }; const unsigned char kAddress2[] = { 192, 168, 0, 1 }; @@ -117,28 +147,30 @@ TEST_F(AddressTrackerLinuxTest, NewAddress) { const IPAddressNumber kAddr3(kAddress3, kAddress3 + arraysize(kAddress3)); Buffer buffer; - MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); AddressTrackerLinux::AddressMap map = GetAddressMap(); EXPECT_EQ(1u, map.size()); - EXPECT_TRUE(map.find(kAddr0) != map.end()); + EXPECT_EQ(1u, map.count(kAddr0)); EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags); buffer.clear(); - MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr1, kAddr2, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr1, kAddr2, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(2u, map.size()); - EXPECT_TRUE(map.find(kAddr0) != map.end()); - EXPECT_TRUE(map.find(kAddr2) != map.end()); + EXPECT_EQ(1u, map.count(kAddr0)); + EXPECT_EQ(1u, map.count(kAddr2)); EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr2].ifa_flags); buffer.clear(); - MakeMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, 0, AF_INET6, kEmpty, kAddr3, &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(3u, map.size()); - EXPECT_TRUE(map.find(kAddr3) != map.end()); + EXPECT_EQ(1u, map.count(kAddr3)); } TEST_F(AddressTrackerLinuxTest, NewAddressChange) { @@ -146,26 +178,30 @@ TEST_F(AddressTrackerLinuxTest, NewAddressChange) { const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0)); Buffer buffer; - MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); AddressTrackerLinux::AddressMap map = GetAddressMap(); EXPECT_EQ(1u, map.size()); - EXPECT_TRUE(map.find(kAddr0) != map.end()); + EXPECT_EQ(1u, map.count(kAddr0)); EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags); buffer.clear(); - MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(1u, map.size()); - EXPECT_TRUE(map.find(kAddr0) != map.end()); + EXPECT_EQ(1u, map.count(kAddr0)); EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags); // Both messages in one buffer. buffer.clear(); - MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, &buffer); - MakeMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kEmpty, + &buffer); + MakeAddrMessage(RTM_NEWADDR, IFA_F_HOMEADDRESS, AF_INET, kAddr0, kEmpty, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(1u, map.size()); EXPECT_EQ(IFA_F_HOMEADDRESS, map[kAddr0].ifa_flags); @@ -175,14 +211,15 @@ TEST_F(AddressTrackerLinuxTest, NewAddressDuplicate) { const IPAddressNumber kAddr0(kAddress0, kAddress0 + arraysize(kAddress0)); Buffer buffer; - MakeMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kAddr0, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, IFA_F_TEMPORARY, AF_INET, kAddr0, kAddr0, + &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); AddressTrackerLinux::AddressMap map = GetAddressMap(); EXPECT_EQ(1u, map.size()); - EXPECT_TRUE(map.find(kAddr0) != map.end()); + EXPECT_EQ(1u, map.count(kAddr0)); EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags); - EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size())); + EXPECT_FALSE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(1u, map.size()); EXPECT_EQ(IFA_F_TEMPORARY, map[kAddr0].ifa_flags); @@ -195,30 +232,30 @@ TEST_F(AddressTrackerLinuxTest, DeleteAddress) { const IPAddressNumber kAddr2(kAddress2, kAddress2 + arraysize(kAddress2)); Buffer buffer; - MakeMessage(RTM_NEWADDR, 0, AF_INET, kAddr0, kEmpty, &buffer); - MakeMessage(RTM_NEWADDR, 0, AF_INET, kAddr1, kAddr2, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kAddr0, kEmpty, &buffer); + MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kAddr1, kAddr2, &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); AddressTrackerLinux::AddressMap map = GetAddressMap(); EXPECT_EQ(2u, map.size()); buffer.clear(); - MakeMessage(RTM_DELADDR, 0, AF_INET, kEmpty, kAddr0, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kEmpty, kAddr0, &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(1u, map.size()); - EXPECT_TRUE(map.find(kAddr0) == map.end()); - EXPECT_TRUE(map.find(kAddr2) != map.end()); + EXPECT_EQ(0u, map.count(kAddr0)); + EXPECT_EQ(1u, map.count(kAddr2)); buffer.clear(); - MakeMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kAddr1, &buffer); + MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kAddr1, &buffer); // kAddr1 does not exist in the map. - EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size())); + EXPECT_FALSE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(1u, map.size()); buffer.clear(); - MakeMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kEmpty, &buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + MakeAddrMessage(RTM_DELADDR, 0, AF_INET, kAddr2, kEmpty, &buffer); + EXPECT_TRUE(HandleAddressMessage(buffer)); map = GetAddressMap(); EXPECT_EQ(0u, map.size()); } @@ -230,13 +267,13 @@ TEST_F(AddressTrackerLinuxTest, IgnoredMessage) { Buffer buffer; // Ignored family. - MakeMessage(RTM_NEWADDR, 0, AF_UNSPEC, kAddr3, kAddr0, &buffer); + MakeAddrMessage(RTM_NEWADDR, 0, AF_UNSPEC, kAddr3, kAddr0, &buffer); // No address. - MakeMessage(RTM_NEWADDR, 0, AF_INET, kEmpty, kEmpty, &buffer); + MakeAddrMessage(RTM_NEWADDR, 0, AF_INET, kEmpty, kEmpty, &buffer); // Ignored type. - MakeMessage(RTM_DELROUTE, 0, AF_INET6, kAddr3, kEmpty, &buffer); - EXPECT_FALSE(HandleMessage(&buffer[0], buffer.size())); - EXPECT_EQ(0u, GetAddressMap().size()); + MakeAddrMessage(RTM_DELROUTE, 0, AF_INET6, kAddr3, kEmpty, &buffer); + EXPECT_FALSE(HandleAddressMessage(buffer)); + EXPECT_TRUE(GetAddressMap().empty()); // Valid message after ignored messages. NetlinkMessage nlmsg(RTM_NEWADDR); @@ -249,10 +286,80 @@ TEST_F(AddressTrackerLinuxTest, IgnoredMessage) { nlmsg.AddAttribute(IFA_ADDRESS, &kAddr0[0], kAddr0.size()); nlmsg.AppendTo(&buffer); - EXPECT_TRUE(HandleMessage(&buffer[0], buffer.size())); + EXPECT_TRUE(HandleAddressMessage(buffer)); EXPECT_EQ(1u, GetAddressMap().size()); } +TEST_F(AddressTrackerLinuxTest, AddInterface) { + Buffer buffer; + + // Ignores loopback. + MakeLinkMessage(RTM_NEWLINK, + IFF_LOOPBACK | IFF_UP | IFF_LOWER_UP | IFF_RUNNING, + 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Ignores not IFF_LOWER_UP. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Ignores deletion. + MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Verify success. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_EQ(1u, GetOnlineLinks()->count(0)); + EXPECT_EQ(1u, GetOnlineLinks()->size()); + + // Ignores redundant enables. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_EQ(1u, GetOnlineLinks()->count(0)); + EXPECT_EQ(1u, GetOnlineLinks()->size()); + + // Verify adding another online device (e.g. VPN) is considered a change. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 1, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_EQ(1u, GetOnlineLinks()->count(0)); + EXPECT_EQ(1u, GetOnlineLinks()->count(1)); + EXPECT_EQ(2u, GetOnlineLinks()->size()); +} + +TEST_F(AddressTrackerLinuxTest, RemoveInterface) { + Buffer buffer; + + // Should disappear when not IFF_LOWER_UP. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_FALSE(GetOnlineLinks()->empty()); + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Ignores redundant disables. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_RUNNING, 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Ignores deleting down interfaces. + MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_RUNNING, 0, &buffer); + EXPECT_FALSE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); + + // Should disappear when deleted. + MakeLinkMessage(RTM_NEWLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_FALSE(GetOnlineLinks()->empty()); + MakeLinkMessage(RTM_DELLINK, IFF_UP | IFF_LOWER_UP | IFF_RUNNING, 0, &buffer); + EXPECT_TRUE(HandleLinkMessage(buffer)); + EXPECT_TRUE(GetOnlineLinks()->empty()); +} + } // namespace } // namespace internal diff --git a/net/base/network_change_notifier_linux.cc b/net/base/network_change_notifier_linux.cc index a3753a9..fc2b372 100644 --- a/net/base/network_change_notifier_linux.cc +++ b/net/base/network_change_notifier_linux.cc @@ -1,252 +1,26 @@ // 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. -// -// This implementation of NetworkChangeNotifier's offline state detection -// depends on D-Bus and NetworkManager, and is known to work on at least -// GNOME version 2.30. If D-Bus or NetworkManager are unavailable, this -// implementation will always behave as if it is online. #include "net/base/network_change_notifier_linux.h" -#include <resolv.h> - #include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" #include "base/compiler_specific.h" -#include "base/memory/weak_ptr.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" #include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "dbus/bus.h" -#include "dbus/message.h" -#include "dbus/object_proxy.h" #include "net/base/address_tracker_linux.h" -#include "net/base/net_errors.h" #include "net/dns/dns_config_service.h" namespace net { -namespace { - -const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; -const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; -const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; - -// http://projects.gnome.org/NetworkManager/developers/spec-08.html#type-NM_STATE -enum { - NM_LEGACY_STATE_UNKNOWN = 0, - NM_LEGACY_STATE_ASLEEP = 1, - NM_LEGACY_STATE_CONNECTING = 2, - NM_LEGACY_STATE_CONNECTED = 3, - NM_LEGACY_STATE_DISCONNECTED = 4 -}; - -// http://projects.gnome.org/NetworkManager/developers/migrating-to-09/spec.html#type-NM_STATE -enum { - NM_STATE_UNKNOWN = 0, - NM_STATE_ASLEEP = 10, - NM_STATE_DISCONNECTED = 20, - NM_STATE_DISCONNECTING = 30, - NM_STATE_CONNECTING = 40, - NM_STATE_CONNECTED_LOCAL = 50, - NM_STATE_CONNECTED_SITE = 60, - NM_STATE_CONNECTED_GLOBAL = 70 -}; - -} // namespace - -// A wrapper around NetworkManager's D-Bus API. -class NetworkManagerApi { - public: - NetworkManagerApi(const base::Closure& notification_callback, dbus::Bus* bus) - : is_offline_(false), - offline_state_initialized_(true /*manual_reset*/, false), - notification_callback_(notification_callback), - helper_thread_id_(base::kInvalidThreadId), - ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)), - system_bus_(bus) { } - - ~NetworkManagerApi() { } - - // Should be called on a helper thread which must be of type IO. - void Init(); - - // Must be called by the helper thread's CleanUp() method. - void CleanUp(); - - // Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType(). - // Safe to call from any thread, but will block until Init() has completed. - NetworkChangeNotifier::ConnectionType GetCurrentConnectionType(); - - private: - // Callbacks for D-Bus API. - void OnInitialResponse(dbus::Response* response) { - HandleResponse(response); - offline_state_initialized_.Signal(); - } - - void OnSignaled(dbus::Signal* signal); - - void OnConnected(const std::string&, const std::string&, bool success) { - if (!success) { - DLOG(WARNING) << "Failed to set up offline state detection"; - offline_state_initialized_.Signal(); - } - } - - // Helper for OnInitialResponse. - void HandleResponse(dbus::Response* response); - - // Converts a NetworkManager state uint to a bool. - static bool StateIsOffline(uint32 state); - - bool is_offline_; - base::Lock is_offline_lock_; - base::WaitableEvent offline_state_initialized_; - - base::Closure notification_callback_; - - base::PlatformThreadId helper_thread_id_; - - base::WeakPtrFactory<NetworkManagerApi> ptr_factory_; - - scoped_refptr<dbus::Bus> system_bus_; - - DISALLOW_COPY_AND_ASSIGN(NetworkManagerApi); -}; - -void NetworkManagerApi::Init() { - // D-Bus requires an IO MessageLoop. - DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_IO); - helper_thread_id_ = base::PlatformThread::CurrentId(); - - if (!system_bus_) { - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SYSTEM; - options.connection_type = dbus::Bus::PRIVATE; - system_bus_ = new dbus::Bus(options); - } - - // Ignore ServiceUnknown errors to avoid log spam: http://crbug.com/109696. - dbus::ObjectProxy* proxy = system_bus_->GetObjectProxyWithOptions( - kNetworkManagerServiceName, dbus::ObjectPath(kNetworkManagerPath), - dbus::ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS); - - // Get the initial state asynchronously. - dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get"); - dbus::MessageWriter builder(&method_call); - builder.AppendString(kNetworkManagerInterface); - builder.AppendString("State"); - proxy->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, - base::Bind(&NetworkManagerApi::OnInitialResponse, - ptr_factory_.GetWeakPtr())); - - // And sign up for notifications. - proxy->ConnectToSignal( - kNetworkManagerInterface, - "StateChanged", - base::Bind(&NetworkManagerApi::OnSignaled, ptr_factory_.GetWeakPtr()), - base::Bind(&NetworkManagerApi::OnConnected, ptr_factory_.GetWeakPtr())); -} - -void NetworkManagerApi::CleanUp() { - DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId()); - ptr_factory_.InvalidateWeakPtrs(); -} - -void NetworkManagerApi::HandleResponse(dbus::Response* response) { - DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId()); - if (!response) { - DVLOG(1) << "No response received for initial state request"; - return; - } - dbus::MessageReader reader(response); - uint32 state = 0; - if (!reader.PopVariantOfUint32(&state)) { - DLOG(WARNING) << "Unexpected response for NetworkManager State request: " - << response->ToString(); - return; - } - { - base::AutoLock lock(is_offline_lock_); - is_offline_ = StateIsOffline(state); - } -} - -void NetworkManagerApi::OnSignaled(dbus::Signal* signal) { - DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId()); - dbus::MessageReader reader(signal); - uint32 state = 0; - if (!reader.PopUint32(&state)) { - DLOG(WARNING) << "Unexpected signal for NetworkManager StateChanged: " - << signal->ToString(); - return; - } - bool new_is_offline = StateIsOffline(state); - { - base::AutoLock lock(is_offline_lock_); - if (is_offline_ != new_is_offline) - is_offline_ = new_is_offline; - else - return; - } - notification_callback_.Run(); -} - -bool NetworkManagerApi::StateIsOffline(uint32 state) { - switch (state) { - case NM_LEGACY_STATE_CONNECTED: - case NM_STATE_CONNECTED_SITE: - case NM_STATE_CONNECTED_GLOBAL: - // Definitely connected - return false; - case NM_LEGACY_STATE_DISCONNECTED: - case NM_STATE_DISCONNECTED: - // Definitely disconnected - return true; - case NM_STATE_CONNECTED_LOCAL: - // Local networking only; I'm treating this as offline (keybuk) - return true; - case NM_LEGACY_STATE_CONNECTING: - case NM_STATE_DISCONNECTING: - case NM_STATE_CONNECTING: - // In-flight change to connection status currently underway - return true; - case NM_LEGACY_STATE_ASLEEP: - case NM_STATE_ASLEEP: - // Networking disabled or no devices on system - return true; - default: - // Unknown status - return false; - } -} - -NetworkChangeNotifier::ConnectionType -NetworkManagerApi::GetCurrentConnectionType() { - // http://crbug.com/125097 - base::ThreadRestrictions::ScopedAllowWait allow_wait; - offline_state_initialized_.Wait(); - base::AutoLock lock(is_offline_lock_); - // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. - return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : - NetworkChangeNotifier::CONNECTION_UNKNOWN; -} - class NetworkChangeNotifierLinux::Thread : public base::Thread { public: - explicit Thread(dbus::Bus* bus); + Thread(); virtual ~Thread(); // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType. // Safe to call from any thread. NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() { - return network_manager_api_.GetCurrentConnectionType(); + return address_tracker_.GetCurrentConnectionType(); } const internal::AddressTrackerLinux* address_tracker() const { @@ -259,24 +33,20 @@ class NetworkChangeNotifierLinux::Thread : public base::Thread { virtual void CleanUp() OVERRIDE; private: - // Used to detect online/offline state changes. - NetworkManagerApi network_manager_api_; - scoped_ptr<DnsConfigService> dns_config_service_; + // Used to detect online/offline state and IP address changes. internal::AddressTrackerLinux address_tracker_; DISALLOW_COPY_AND_ASSIGN(Thread); }; -NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) +NetworkChangeNotifierLinux::Thread::Thread() : base::Thread("NetworkChangeNotifier"), - network_manager_api_( - base::Bind(&NetworkChangeNotifier:: - NotifyObserversOfConnectionTypeChange), - bus), address_tracker_( base::Bind(&NetworkChangeNotifier:: - NotifyObserversOfIPAddressChange)) { + NotifyObserversOfIPAddressChange), + base::Bind(&NetworkChangeNotifier:: + NotifyObserversOfConnectionTypeChange)) { } NetworkChangeNotifierLinux::Thread::~Thread() { @@ -284,7 +54,6 @@ NetworkChangeNotifierLinux::Thread::~Thread() { } void NetworkChangeNotifierLinux::Thread::Init() { - network_manager_api_.Init(); address_tracker_.Init(); dns_config_service_ = DnsConfigService::CreateSystemService(); dns_config_service_->WatchConfig( @@ -292,21 +61,15 @@ void NetworkChangeNotifierLinux::Thread::Init() { } void NetworkChangeNotifierLinux::Thread::CleanUp() { - network_manager_api_.CleanUp(); dns_config_service_.reset(); } NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { - return new NetworkChangeNotifierLinux(NULL); -} - -NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( - dbus::Bus* bus) { - return new NetworkChangeNotifierLinux(bus); + return new NetworkChangeNotifierLinux(); } -NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus) - : notifier_thread_(new Thread(bus)) { +NetworkChangeNotifierLinux::NetworkChangeNotifierLinux() + : 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. diff --git a/net/base/network_change_notifier_linux.h b/net/base/network_change_notifier_linux.h index 1b3f376..6e10854 100644 --- a/net/base/network_change_notifier_linux.h +++ b/net/base/network_change_notifier_linux.h @@ -11,10 +11,6 @@ #include "net/base/net_export.h" #include "net/base/network_change_notifier.h" -namespace dbus { -class Bus; -} - namespace net { class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux @@ -22,13 +18,10 @@ class NET_EXPORT_PRIVATE NetworkChangeNotifierLinux public: static NetworkChangeNotifierLinux* Create(); - // Unittests inject a mock bus. - static NetworkChangeNotifierLinux* CreateForTest(dbus::Bus* bus); - private: class Thread; - explicit NetworkChangeNotifierLinux(dbus::Bus* bus); + NetworkChangeNotifierLinux(); virtual ~NetworkChangeNotifierLinux(); // NetworkChangeNotifier: diff --git a/net/base/network_change_notifier_linux_unittest.cc b/net/base/network_change_notifier_linux_unittest.cc deleted file mode 100644 index 8fc9631..0000000 --- a/net/base/network_change_notifier_linux_unittest.cc +++ /dev/null @@ -1,243 +0,0 @@ -// 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/message_loop_proxy.h" -#include "base/synchronization/waitable_event.h" -#include "dbus/mock_bus.h" -#include "dbus/mock_object_proxy.h" -#include "dbus/message.h" -#include "dbus/object_path.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { - -using testing::_; -using testing::DoAll; -using testing::InvokeWithoutArgs; -using testing::Return; -using testing::SaveArg; - -class NetworkChangeNotifierLinuxTest : public testing::Test { - protected: - // A subset of the NetworkManager-defined constants used in - // the tests below. See network_change_notifier_linux.cc - // for the full list. - enum { - NM_STATE_DISCONNECTED = 20, - NM_STATE_DISCONNECTING = 30, - NM_STATE_CONNECTED_SITE = 70, - NM_STATE_CONNECTED_GLOBAL = 70 - }; - - NetworkChangeNotifierLinuxTest() - : initialized_(false, false) {} - - virtual void SetUp() { - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SYSTEM; - mock_bus_ = new dbus::MockBus(options); - - mock_object_proxy_ = new dbus::MockObjectProxy( - mock_bus_.get(), - "service_name", - dbus::ObjectPath("service_path")); - EXPECT_CALL(*mock_bus_, GetObjectProxyWithOptions(_, _, _)) - .WillOnce(Return(mock_object_proxy_.get())); - - EXPECT_CALL(*mock_object_proxy_, CallMethod(_, _, _)) - .WillOnce(SaveArg<2>(&response_callback_)); - EXPECT_CALL(*mock_object_proxy_, ConnectToSignal(_, _, _, _)) - .WillOnce( - DoAll( - SaveArg<2>(&signal_callback_), - InvokeWithoutArgs( - this, - &NetworkChangeNotifierLinuxTest::Initialize))); - - notifier_.reset(NetworkChangeNotifierLinux::CreateForTest(mock_bus_.get())); - - initialized_.Wait(); - } - - void Initialize() { - notifier_thread_proxy_ = base::MessageLoopProxy::current(); - initialized_.Signal(); - } - - void RunOnNotifierThread(const base::Closure& callback) { - base::WaitableEvent event(false, false); - notifier_thread_proxy_->PostTask(FROM_HERE, base::Bind( - &RunOnNotifierThreadHelper, callback, &event)); - event.Wait(); - // Run any tasks queued on the main thread, e.g. by - // ObserverListThreadSafe. - MessageLoop::current()->RunUntilIdle(); - } - - void SendResponse(uint32 state) { - scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); - dbus::MessageWriter writer(response.get()); - writer.AppendVariantOfUint32(state); - RunOnNotifierThread(base::Bind(response_callback_, response.get())); - } - - void SendSignal(uint32 state) { - dbus::Signal signal("org.freedesktop.NetworkManager", "StateChanged"); - dbus::MessageWriter writer(&signal); - writer.AppendUint32(state); - RunOnNotifierThread(base::Bind(signal_callback_, &signal)); - } - - dbus::ObjectProxy::ResponseCallback response_callback_; - dbus::ObjectProxy::SignalCallback signal_callback_; - - // Allows creating a new NetworkChangeNotifier. Must be created before - // |notifier_| and destroyed after it to avoid DCHECK failures. - NetworkChangeNotifier::DisableForTest disable_for_test_; - scoped_ptr<NetworkChangeNotifier> notifier_; - - private: - static void RunOnNotifierThreadHelper(const base::Closure& callback, - base::WaitableEvent* event) { - callback.Run(); - event->Signal(); - } - - base::WaitableEvent initialized_; - - // Valid only after initialized_ is signaled. - scoped_refptr<base::MessageLoopProxy> notifier_thread_proxy_; - - scoped_refptr<dbus::MockBus> mock_bus_; - scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_; -}; - -namespace { - -class OfflineObserver : public NetworkChangeNotifier::ConnectionTypeObserver { - public: - OfflineObserver() - : notification_count(0), - last_online_value(true) { - NetworkChangeNotifier::AddConnectionTypeObserver(this); - } - - ~OfflineObserver() { - NetworkChangeNotifier::RemoveConnectionTypeObserver(this); - } - - virtual void OnConnectionTypeChanged( - NetworkChangeNotifier::ConnectionType type) OVERRIDE { - notification_count++; - last_online_value = type != NetworkChangeNotifier::CONNECTION_NONE; - } - - int notification_count; - bool last_online_value; -}; - -TEST_F(NetworkChangeNotifierLinuxTest, Offline) { - SendResponse(NM_STATE_DISCONNECTED); - EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); -} - -TEST_F(NetworkChangeNotifierLinuxTest, Online) { - SendResponse(NM_STATE_CONNECTED_GLOBAL); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType());} - -TEST_F(NetworkChangeNotifierLinuxTest, OfflineThenOnline) { - OfflineObserver observer; - - SendResponse(NM_STATE_DISCONNECTED); - EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); - - SendSignal(NM_STATE_CONNECTED_GLOBAL); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(1, observer.notification_count); - EXPECT_TRUE(observer.last_online_value); -} - -TEST_F(NetworkChangeNotifierLinuxTest, MultipleStateChanges) { - OfflineObserver observer; - - SendResponse(NM_STATE_CONNECTED_GLOBAL); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); - - SendSignal(NM_STATE_DISCONNECTED); - EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(1, observer.notification_count); - EXPECT_FALSE(observer.last_online_value); - - SendSignal(NM_STATE_CONNECTED_GLOBAL); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(2, observer.notification_count); - EXPECT_TRUE(observer.last_online_value); -} - -TEST_F(NetworkChangeNotifierLinuxTest, IgnoreContinuedOnlineState) { - OfflineObserver observer; - - SendResponse(NM_STATE_CONNECTED_SITE); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); - - SendSignal(NM_STATE_CONNECTED_GLOBAL); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); -} - -TEST_F(NetworkChangeNotifierLinuxTest, IgnoreContinuedOfflineState) { - OfflineObserver observer; - - SendResponse(NM_STATE_DISCONNECTING); - EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); - - SendSignal(NM_STATE_DISCONNECTED); - EXPECT_EQ(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); - EXPECT_EQ(0, observer.notification_count); -} - -TEST_F(NetworkChangeNotifierLinuxTest, NullResponse) { - RunOnNotifierThread(base::Bind( - response_callback_, static_cast<dbus::Response*>(NULL))); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); -} - -TEST_F(NetworkChangeNotifierLinuxTest, EmptyResponse) { - scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); - RunOnNotifierThread(base::Bind(response_callback_, response.get())); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); -} - -TEST_F(NetworkChangeNotifierLinuxTest, InvalidResponse) { - scoped_ptr<dbus::Response> response(dbus::Response::CreateEmpty()); - dbus::MessageWriter writer(response.get()); - writer.AppendUint16(20); // Uint16 instead of the expected Uint32 - RunOnNotifierThread(base::Bind(response_callback_, response.get())); - EXPECT_NE(NetworkChangeNotifier::CONNECTION_NONE, - NetworkChangeNotifier::GetConnectionType()); -} - -} // namespace -} // namespace net |