1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/network_change_notifier.h"
7 #include "base/metrics/histogram.h"
8 #include "base/synchronization/lock.h"
9 #include "build/build_config.h"
10 #include "googleurl/src/gurl.h"
11 #include "net/base/net_util.h"
12 #include "net/base/network_change_notifier_factory.h"
13 #include "net/dns/dns_config_service.h"
16 #include "net/base/network_change_notifier_win.h"
17 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
18 #include "net/base/network_change_notifier_linux.h"
19 #elif defined(OS_MACOSX)
20 #include "net/base/network_change_notifier_mac.h"
27 // The actual singleton notifier. The class contract forbids usage of the API
28 // in ways that would require us to place locks around access to this object.
29 // (The prohibition on global non-POD objects makes it tricky to do such a thing
31 NetworkChangeNotifier
* g_network_change_notifier
= NULL
;
33 // Class factory singleton.
34 NetworkChangeNotifierFactory
* g_network_change_notifier_factory
= NULL
;
36 class MockNetworkChangeNotifier
: public NetworkChangeNotifier
{
38 virtual ConnectionType
GetCurrentConnectionType() const OVERRIDE
{
39 return CONNECTION_UNKNOWN
;
45 // The main observer class that records UMAs for network events.
46 class HistogramWatcher
47 : public NetworkChangeNotifier::ConnectionTypeObserver
,
48 public NetworkChangeNotifier::IPAddressObserver
,
49 public NetworkChangeNotifier::DNSObserver
{
52 : last_ip_address_change_(base::TimeTicks::Now()),
53 last_connection_change_(base::TimeTicks::Now()),
54 last_dns_change_(base::TimeTicks::Now()),
55 last_connection_type_(NetworkChangeNotifier::CONNECTION_UNKNOWN
),
56 offline_packets_received_(0) {}
58 // Registers our three Observer implementations. This is called from the
59 // network thread so that our Observer implementations are also called
60 // from the network thread. This avoids multi-threaded race conditions
61 // because the only other interface, |NotifyDataReceived| is also
62 // only called from the network thread.
64 NetworkChangeNotifier::AddConnectionTypeObserver(this);
65 NetworkChangeNotifier::AddIPAddressObserver(this);
66 NetworkChangeNotifier::AddDNSObserver(this);
69 virtual ~HistogramWatcher() {}
71 // NetworkChangeNotifier::IPAddressObserver implementation.
72 virtual void OnIPAddressChanged() OVERRIDE
{
73 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.IPAddressChange",
74 SinceLast(&last_ip_address_change_
));
77 // NetworkChangeNotifier::ConnectionTypeObserver implementation.
78 virtual void OnConnectionTypeChanged(
79 NetworkChangeNotifier::ConnectionType type
) OVERRIDE
{
80 if (type
!= NetworkChangeNotifier::CONNECTION_NONE
) {
81 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OnlineChange",
82 SinceLast(&last_connection_change_
));
84 if (offline_packets_received_
) {
85 if ((last_connection_change_
- last_offline_packet_received_
) <
86 base::TimeDelta::FromSeconds(5)) {
87 // We can compare this sum with the sum of NCN.OfflineDataRecv.
88 UMA_HISTOGRAM_COUNTS_10000(
89 "NCN.OfflineDataRecvAny5sBeforeOnline",
90 offline_packets_received_
);
93 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecvUntilOnline",
94 last_connection_change_
-
95 last_offline_packet_received_
);
98 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineChange",
99 SinceLast(&last_connection_change_
));
102 offline_packets_received_
= 0;
103 last_connection_type_
= type
;
104 polling_interval_
= base::TimeDelta::FromSeconds(1);
107 // NetworkChangeNotifier::DNSObserver implementation.
108 virtual void OnDNSChanged() OVERRIDE
{
109 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.DNSConfigChange",
110 SinceLast(&last_dns_change_
));
113 // Record histogram data whenever we receive a packet but think we're
114 // offline. Should only be called from the network thread.
115 void NotifyDataReceived(const GURL
& source
) {
116 if (last_connection_type_
!= NetworkChangeNotifier::CONNECTION_NONE
||
117 IsLocalhost(source
.host()) ||
118 !(source
.SchemeIs("http") || source
.SchemeIs("https"))) {
122 base::TimeTicks current_time
= base::TimeTicks::Now();
123 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.OfflineDataRecv",
124 current_time
- last_connection_change_
);
125 offline_packets_received_
++;
126 last_offline_packet_received_
= current_time
;
128 if ((current_time
- last_polled_connection_
) > polling_interval_
) {
129 polling_interval_
*= 2;
130 last_polled_connection_
= current_time
;
131 base::TimeTicks started_get_connection_type
= base::TimeTicks::Now();
132 last_polled_connection_type_
=
133 NetworkChangeNotifier::GetConnectionType();
134 UMA_HISTOGRAM_TIMES("NCN.GetConnectionTypeTime",
135 base::TimeTicks::Now() -
136 started_get_connection_type
);
138 if (last_polled_connection_type_
==
139 NetworkChangeNotifier::CONNECTION_NONE
) {
140 UMA_HISTOGRAM_MEDIUM_TIMES("NCN.PollingOfflineDataRecv",
141 current_time
- last_connection_change_
);
146 static base::TimeDelta
SinceLast(base::TimeTicks
*last_time
) {
147 base::TimeTicks current_time
= base::TimeTicks::Now();
148 base::TimeDelta delta
= current_time
- *last_time
;
149 *last_time
= current_time
;
153 base::TimeTicks last_ip_address_change_
;
154 base::TimeTicks last_connection_change_
;
155 base::TimeTicks last_dns_change_
;
156 base::TimeTicks last_offline_packet_received_
;
157 base::TimeTicks last_polled_connection_
;
158 // |polling_interval_| is initialized by |OnConnectionTypeChanged| on our
159 // first transition to offline and on subsequent transitions. Once offline,
160 // |polling_interval_| doubles as offline data is received and we poll
161 // with |NetworkChangeNotifier::GetConnectionType| to verify the connection
163 base::TimeDelta polling_interval_
;
164 // |last_connection_type_| is the last value passed to
165 // |OnConnectionTypeChanged|.
166 NetworkChangeNotifier::ConnectionType last_connection_type_
;
167 // |last_polled_connection_type_| is last result from calling
168 // |NetworkChangeNotifier::GetConnectionType| in |NotifyDataReceived|.
169 NetworkChangeNotifier::ConnectionType last_polled_connection_type_
;
170 int32 offline_packets_received_
;
172 DISALLOW_COPY_AND_ASSIGN(HistogramWatcher
);
175 // NetworkState is thread safe.
176 class NetworkChangeNotifier::NetworkState
{
181 void GetDnsConfig(DnsConfig
* config
) const {
182 base::AutoLock
lock(lock_
);
183 *config
= dns_config_
;
186 void SetDnsConfig(const DnsConfig
& dns_config
) {
187 base::AutoLock
lock(lock_
);
188 dns_config_
= dns_config
;
192 mutable base::Lock lock_
;
193 DnsConfig dns_config_
;
196 NetworkChangeNotifier::~NetworkChangeNotifier() {
197 DCHECK_EQ(this, g_network_change_notifier
);
198 g_network_change_notifier
= NULL
;
202 void NetworkChangeNotifier::SetFactory(
203 NetworkChangeNotifierFactory
* factory
) {
204 CHECK(!g_network_change_notifier_factory
);
205 g_network_change_notifier_factory
= factory
;
209 NetworkChangeNotifier
* NetworkChangeNotifier::Create() {
210 if (g_network_change_notifier_factory
)
211 return g_network_change_notifier_factory
->CreateInstance();
214 NetworkChangeNotifierWin
* network_change_notifier
=
215 new NetworkChangeNotifierWin();
216 network_change_notifier
->WatchForAddressChange();
217 return network_change_notifier
;
218 #elif defined(OS_CHROMEOS) || defined(OS_ANDROID)
219 // ChromeOS and Android builds MUST use their own class factory.
220 #if !defined(OS_CHROMEOS)
221 // TODO(oshima): ash_shell do not have access to chromeos'es
222 // notifier yet. Re-enable this when chromeos'es notifier moved to
223 // chromeos root directory. crbug.com/119298.
227 #elif defined(OS_LINUX)
228 return NetworkChangeNotifierLinux::Create();
229 #elif defined(OS_MACOSX)
230 return new NetworkChangeNotifierMac();
238 NetworkChangeNotifier::ConnectionType
239 NetworkChangeNotifier::GetConnectionType() {
240 return g_network_change_notifier
?
241 g_network_change_notifier
->GetCurrentConnectionType() :
246 void NetworkChangeNotifier::GetDnsConfig(DnsConfig
* config
) {
247 if (!g_network_change_notifier
) {
248 *config
= DnsConfig();
250 g_network_change_notifier
->network_state_
->GetDnsConfig(config
);
255 const char* NetworkChangeNotifier::ConnectionTypeToString(
256 ConnectionType type
) {
257 static const char* kConnectionTypeNames
[] = {
258 "CONNECTION_UNKNOWN",
259 "CONNECTION_ETHERNET",
267 arraysize(kConnectionTypeNames
) ==
268 NetworkChangeNotifier::CONNECTION_NONE
+ 1,
269 ConnectionType_name_count_mismatch
);
270 if (type
< CONNECTION_UNKNOWN
|| type
> CONNECTION_NONE
) {
272 return "CONNECTION_INVALID";
274 return kConnectionTypeNames
[type
];
278 void NetworkChangeNotifier::NotifyDataReceived(const GURL
& source
) {
279 if (!g_network_change_notifier
)
281 g_network_change_notifier
->histogram_watcher_
->NotifyDataReceived(source
);
285 void NetworkChangeNotifier::InitHistogramWatcher() {
286 if (!g_network_change_notifier
)
288 g_network_change_notifier
->histogram_watcher_
->Init();
291 #if defined(OS_LINUX)
293 const internal::AddressTrackerLinux
*
294 NetworkChangeNotifier::GetAddressTracker() {
295 return g_network_change_notifier
?
296 g_network_change_notifier
->GetAddressTrackerInternal() : NULL
;
301 bool NetworkChangeNotifier::IsOffline() {
302 return GetConnectionType() == CONNECTION_NONE
;
306 bool NetworkChangeNotifier::IsConnectionCellular(ConnectionType type
) {
307 bool is_cellular
= false;
314 case CONNECTION_UNKNOWN
:
315 case CONNECTION_ETHERNET
:
316 case CONNECTION_WIFI
:
317 case CONNECTION_NONE
:
325 NetworkChangeNotifier
* NetworkChangeNotifier::CreateMock() {
326 return new MockNetworkChangeNotifier();
329 void NetworkChangeNotifier::AddIPAddressObserver(IPAddressObserver
* observer
) {
330 if (g_network_change_notifier
)
331 g_network_change_notifier
->ip_address_observer_list_
->AddObserver(observer
);
334 void NetworkChangeNotifier::AddConnectionTypeObserver(
335 ConnectionTypeObserver
* observer
) {
336 if (g_network_change_notifier
) {
337 g_network_change_notifier
->connection_type_observer_list_
->AddObserver(
342 void NetworkChangeNotifier::AddDNSObserver(DNSObserver
* observer
) {
343 if (g_network_change_notifier
) {
344 g_network_change_notifier
->resolver_state_observer_list_
->AddObserver(
349 void NetworkChangeNotifier::RemoveIPAddressObserver(
350 IPAddressObserver
* observer
) {
351 if (g_network_change_notifier
) {
352 g_network_change_notifier
->ip_address_observer_list_
->RemoveObserver(
357 void NetworkChangeNotifier::RemoveConnectionTypeObserver(
358 ConnectionTypeObserver
* observer
) {
359 if (g_network_change_notifier
) {
360 g_network_change_notifier
->connection_type_observer_list_
->RemoveObserver(
365 void NetworkChangeNotifier::RemoveDNSObserver(DNSObserver
* observer
) {
366 if (g_network_change_notifier
) {
367 g_network_change_notifier
->resolver_state_observer_list_
->RemoveObserver(
372 NetworkChangeNotifier::NetworkChangeNotifier()
373 : ip_address_observer_list_(
374 new ObserverListThreadSafe
<IPAddressObserver
>(
375 ObserverListBase
<IPAddressObserver
>::NOTIFY_EXISTING_ONLY
)),
376 connection_type_observer_list_(
377 new ObserverListThreadSafe
<ConnectionTypeObserver
>(
378 ObserverListBase
<ConnectionTypeObserver
>::NOTIFY_EXISTING_ONLY
)),
379 resolver_state_observer_list_(
380 new ObserverListThreadSafe
<DNSObserver
>(
381 ObserverListBase
<DNSObserver
>::NOTIFY_EXISTING_ONLY
)),
382 network_state_(new NetworkState()),
383 histogram_watcher_(new HistogramWatcher()) {
384 DCHECK(!g_network_change_notifier
);
385 g_network_change_notifier
= this;
388 #if defined(OS_LINUX)
389 const internal::AddressTrackerLinux
*
390 NetworkChangeNotifier::GetAddressTrackerInternal() const {
396 void NetworkChangeNotifier::NotifyObserversOfIPAddressChange() {
397 if (g_network_change_notifier
) {
398 g_network_change_notifier
->ip_address_observer_list_
->Notify(
399 &IPAddressObserver::OnIPAddressChanged
);
404 void NetworkChangeNotifier::NotifyObserversOfDNSChange() {
405 if (g_network_change_notifier
) {
406 g_network_change_notifier
->resolver_state_observer_list_
->Notify(
407 &DNSObserver::OnDNSChanged
);
412 void NetworkChangeNotifier::SetDnsConfig(const DnsConfig
& config
) {
413 if (!g_network_change_notifier
)
415 g_network_change_notifier
->network_state_
->SetDnsConfig(config
);
416 NotifyObserversOfDNSChange();
419 void NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange() {
420 if (g_network_change_notifier
) {
421 g_network_change_notifier
->connection_type_observer_list_
->Notify(
422 &ConnectionTypeObserver::OnConnectionTypeChanged
,
423 GetConnectionType());
427 NetworkChangeNotifier::DisableForTest::DisableForTest()
428 : network_change_notifier_(g_network_change_notifier
) {
429 DCHECK(g_network_change_notifier
);
430 g_network_change_notifier
= NULL
;
433 NetworkChangeNotifier::DisableForTest::~DisableForTest() {
434 DCHECK(!g_network_change_notifier
);
435 g_network_change_notifier
= network_change_notifier_
;