This source file includes following definitions.
- DnsConfigServiceThread
- DnsConfigServiceThread
- Init
- CleanUp
- last_announced_offline_
- NetworkChangeCalculatorParamsWin
- RecomputeCurrentConnectionType
- GetCurrentConnectionType
- SetCurrentConnectionType
- OnObjectSignaled
- NotifyObservers
- WatchForAddressChange
- WatchForAddressChangeInternal
- NotifyParentOfConnectionTypeChange
#include "net/base/network_change_notifier_win.h"
#include <iphlpapi.h>
#include <winsock2.h>
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "net/base/winsock_init.h"
#include "net/dns/dns_config_service.h"
#pragma comment(lib, "iphlpapi.lib")
namespace net {
namespace {
const int kWatchForAddressChangeRetryIntervalMs = 500;
}
class NetworkChangeNotifierWin::DnsConfigServiceThread : public base::Thread {
public:
DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
virtual ~DnsConfigServiceThread() {
Stop();
}
virtual void Init() OVERRIDE {
service_ = DnsConfigService::CreateSystemService();
service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
}
virtual void CleanUp() OVERRIDE {
service_.reset();
}
private:
scoped_ptr<DnsConfigService> service_;
DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
};
NetworkChangeNotifierWin::NetworkChangeNotifierWin()
: NetworkChangeNotifier(NetworkChangeCalculatorParamsWin()),
is_watching_(false),
sequential_failures_(0),
weak_factory_(this),
dns_config_service_thread_(new DnsConfigServiceThread()),
last_computed_connection_type_(RecomputeCurrentConnectionType()),
last_announced_offline_(
last_computed_connection_type_ == CONNECTION_NONE) {
memset(&addr_overlapped_, 0, sizeof addr_overlapped_);
addr_overlapped_.hEvent = WSACreateEvent();
dns_config_service_thread_->StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
}
NetworkChangeNotifierWin::~NetworkChangeNotifierWin() {
if (is_watching_) {
CancelIPChangeNotify(&addr_overlapped_);
addr_watcher_.StopWatching();
}
WSACloseEvent(addr_overlapped_.hEvent);
}
NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeNotifierWin::NetworkChangeCalculatorParamsWin() {
NetworkChangeCalculatorParams params;
params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(1500);
params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1500);
params.connection_type_offline_delay_ =
base::TimeDelta::FromMilliseconds(1500);
params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
return params;
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierWin::RecomputeCurrentConnectionType() const {
DCHECK(CalledOnValidThread());
EnsureWinsockInit();
HANDLE ws_handle;
WSAQUERYSET query_set = {0};
query_set.dwSize = sizeof(WSAQUERYSET);
query_set.dwNameSpace = NS_NLA;
if (0 != WSALookupServiceBegin(&query_set, LUP_RETURN_ALL,
&ws_handle)) {
LOG(ERROR) << "WSALookupServiceBegin failed with: " << WSAGetLastError();
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
bool found_connection = false;
char result_buffer[sizeof(WSAQUERYSET) + 256] = {0};
DWORD length = sizeof(result_buffer);
reinterpret_cast<WSAQUERYSET*>(&result_buffer[0])->dwSize =
sizeof(WSAQUERYSET);
int result = WSALookupServiceNext(
ws_handle,
LUP_RETURN_NAME,
&length,
reinterpret_cast<WSAQUERYSET*>(&result_buffer[0]));
if (result == 0) {
found_connection = true;
} else {
DCHECK_EQ(SOCKET_ERROR, result);
result = WSAGetLastError();
if (result == WSAEFAULT) {
found_connection = true;
} else if (result == WSA_E_NO_MORE || result == WSAENOMORE) {
} else {
LOG(WARNING) << "WSALookupServiceNext() failed with:" << result;
}
}
result = WSALookupServiceEnd(ws_handle);
LOG_IF(ERROR, result != 0)
<< "WSALookupServiceEnd() failed with: " << result;
return found_connection ? NetworkChangeNotifier::CONNECTION_UNKNOWN :
NetworkChangeNotifier::CONNECTION_NONE;
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierWin::GetCurrentConnectionType() const {
base::AutoLock auto_lock(last_computed_connection_type_lock_);
return last_computed_connection_type_;
}
void NetworkChangeNotifierWin::SetCurrentConnectionType(
ConnectionType connection_type) {
base::AutoLock auto_lock(last_computed_connection_type_lock_);
last_computed_connection_type_ = connection_type;
}
void NetworkChangeNotifierWin::OnObjectSignaled(HANDLE object) {
DCHECK(CalledOnValidThread());
DCHECK(is_watching_);
is_watching_ = false;
WatchForAddressChange();
NotifyObservers();
}
void NetworkChangeNotifierWin::NotifyObservers() {
DCHECK(CalledOnValidThread());
SetCurrentConnectionType(RecomputeCurrentConnectionType());
NotifyObserversOfIPAddressChange();
offline_polls_ = 0;
timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
&NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange);
}
void NetworkChangeNotifierWin::WatchForAddressChange() {
DCHECK(CalledOnValidThread());
DCHECK(!is_watching_);
if (!WatchForAddressChangeInternal()) {
++sequential_failures_;
if (sequential_failures_ == 2000) {
UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures",
sequential_failures_);
}
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&NetworkChangeNotifierWin::WatchForAddressChange,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(
kWatchForAddressChangeRetryIntervalMs));
return;
}
if (sequential_failures_ > 0)
NotifyObservers();
if (sequential_failures_ < 2000) {
UMA_HISTOGRAM_COUNTS_10000("Net.NotifyAddrChangeFailures",
sequential_failures_);
}
is_watching_ = true;
sequential_failures_ = 0;
}
bool NetworkChangeNotifierWin::WatchForAddressChangeInternal() {
DCHECK(CalledOnValidThread());
HANDLE handle = NULL;
DWORD ret = NotifyAddrChange(&handle, &addr_overlapped_);
if (ret != ERROR_IO_PENDING)
return false;
addr_watcher_.StartWatching(addr_overlapped_.hEvent, this);
return true;
}
void NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange() {
SetCurrentConnectionType(RecomputeCurrentConnectionType());
bool current_offline = IsOffline();
offline_polls_++;
if (last_announced_offline_ && current_offline && offline_polls_ <= 20) {
timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this,
&NetworkChangeNotifierWin::NotifyParentOfConnectionTypeChange);
return;
}
if (last_announced_offline_)
UMA_HISTOGRAM_CUSTOM_COUNTS("NCN.OfflinePolls", offline_polls_, 1, 50, 50);
last_announced_offline_ = current_offline;
NotifyObserversOfConnectionTypeChange();
}
}