blob: 661f537a86c0fa84ff62187a9a78f91da615f2a4 [file] [log] [blame]
// 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 "chrome/browser/chromeos/net/network_change_notifier_chromeos.h"
#include "base/bind.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "content/public/browser/browser_thread.h"
#include "net/dns/dns_config_service_posix.h"
using content::BrowserThread;
namespace {
// Delay for online change notification reporting.
const int kOnlineNotificationDelayMS = 500;
const int kInitialNotificationCheckDelayMS = 1000;
bool IsOnline(chromeos::ConnectionState state) {
return state == chromeos::STATE_ONLINE ||
state == chromeos::STATE_PORTAL;
}
}
namespace chromeos {
class NetworkChangeNotifierChromeos::DnsConfigServiceChromeos
: public net::internal::DnsConfigServicePosix {
public:
DnsConfigServiceChromeos() {}
virtual ~DnsConfigServiceChromeos() {}
// net::DnsConfigServicePosix:
virtual bool StartWatching() OVERRIDE {
// Notifications from NetworkLibrary are sent to
// NetworkChangeNotifierChromeos.
return true;
}
void OnNetworkChange() {
InvalidateConfig();
InvalidateHosts();
ReadNow();
}
};
NetworkChangeNotifierChromeos::NetworkChangeNotifierChromeos()
: has_active_network_(false),
connection_state_(chromeos::STATE_UNKNOWN),
connection_type_(CONNECTION_NONE),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
BrowserThread::PostDelayedTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&NetworkChangeNotifierChromeos::UpdateInitialState, this),
base::TimeDelta::FromMilliseconds(kInitialNotificationCheckDelayMS));
}
NetworkChangeNotifierChromeos::~NetworkChangeNotifierChromeos() {
}
void NetworkChangeNotifierChromeos::Init() {
chromeos::NetworkLibrary* network_library =
chromeos::CrosLibrary::Get()->GetNetworkLibrary();
network_library->AddNetworkManagerObserver(this);
DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
dns_config_service_.reset(new DnsConfigServiceChromeos());
dns_config_service_->WatchConfig(
base::Bind(NetworkChangeNotifier::SetDnsConfig));
UpdateNetworkState(network_library);
}
void NetworkChangeNotifierChromeos::Shutdown() {
weak_factory_.InvalidateWeakPtrs();
dns_config_service_.reset();
if (!chromeos::CrosLibrary::Get())
return;
chromeos::NetworkLibrary* lib =
chromeos::CrosLibrary::Get()->GetNetworkLibrary();
lib->RemoveNetworkManagerObserver(this);
lib->RemoveObserverForAllNetworks(this);
DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
}
void NetworkChangeNotifierChromeos::PowerChanged(
const PowerSupplyStatus& status) {
}
void NetworkChangeNotifierChromeos::SystemResumed() {
// Force invalidation of various net resources on system resume.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
&NetworkChangeNotifier::NotifyObserversOfIPAddressChange));
}
void NetworkChangeNotifierChromeos::OnNetworkManagerChanged(
chromeos::NetworkLibrary* cros) {
UpdateNetworkState(cros);
}
net::NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierChromeos::GetCurrentConnectionType() const {
return connection_type_;
}
void NetworkChangeNotifierChromeos::OnNetworkChanged(
chromeos::NetworkLibrary* cros,
const chromeos::Network* network) {
CHECK(network);
// Active network changed?
if (network->service_path() != service_path_)
UpdateNetworkState(cros);
else
UpdateConnectivityState(network);
}
void NetworkChangeNotifierChromeos::UpdateNetworkState(
chromeos::NetworkLibrary* lib) {
const chromeos::Network* network = lib->active_network();
if (network) {
VLOG(1) << "UpdateNetworkState: " << network->name()
<< ", type= " << network->type()
<< ", device= " << network->device_path()
<< ", state= " << network->state();
}
// Check if active network was added, removed or changed.
if ((!network && has_active_network_) ||
(network && (!has_active_network_ ||
network->service_path() != service_path_ ||
network->ip_address() != ip_address_))) {
if (has_active_network_)
lib->RemoveObserverForAllNetworks(this);
if (!network) {
has_active_network_ = false;
service_path_.clear();
ip_address_.clear();
} else {
has_active_network_ = true;
service_path_ = network->service_path();
ip_address_ = network->ip_address();
}
// TODO(szym): detect user DNS changes. http://crbug.com/148394
dns_config_service_->OnNetworkChange();
UpdateConnectivityState(network);
// If there is an active network, add observer to track its changes.
if (network)
lib->AddNetworkObserver(network->service_path(), this);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
&NetworkChangeNotifier::NotifyObserversOfIPAddressChange));
}
}
void NetworkChangeNotifierChromeos::UpdateConnectivityState(
const chromeos::Network* network) {
if (network) {
VLOG(1) << "UpdateConnectivityState: " << network->name()
<< ", type= " << network->type()
<< ", device= " << network->device_path()
<< ", state= " << network->state()
<< ", connect= " << connection_state_
<< ", type= " << connection_type_;
}
// We don't care about all transitions of ConnectionState. OnlineStateChange
// notification should trigger if ConnectionType is changed.
chromeos::ConnectionState new_connection_state =
network ? network->connection_state() : chromeos::STATE_UNKNOWN;
ConnectionType prev_connection_type = connection_type_;
ConnectionType new_connection_type = GetNetworkConnectionType(network);
bool is_online = (new_connection_state == chromeos::STATE_ONLINE);
bool was_online = (connection_state_ == chromeos::STATE_ONLINE);
bool is_portal = (new_connection_state == chromeos::STATE_PORTAL);
bool was_portal = (connection_state_ == chromeos::STATE_PORTAL);
VLOG(2) << " UpdateConnectivityState2: "
<< "new_cs = " << new_connection_state
<< ", is_online = " << is_online
<< ", was_online = " << was_online
<< ", is_portal = " << is_portal
<< ", was_portal = " << was_portal;
connection_state_ = new_connection_state;
connection_type_ = new_connection_type;
if (new_connection_type != prev_connection_type) {
VLOG(1) << "UpdateConnectivityState3: "
<< "prev_connection_type = " << prev_connection_type
<< ", new_connection_type = " << new_connection_type;
ReportConnectionChange();
}
VLOG(2) << " UpdateConnectivityState4: "
<< "new_cs = " << new_connection_state
<< ", end_cs_ = " << connection_state_
<< "prev_type = " << prev_connection_type
<< ", new_type_ = " << new_connection_type;
}
void NetworkChangeNotifierChromeos::ReportConnectionChange() {
if (weak_factory_.HasWeakPtrs()) {
// If we have a pending task, cancel it.
DVLOG(1) << "ReportConnectionChange: has pending task";
weak_factory_.InvalidateWeakPtrs();
DVLOG(1) << "ReportConnectionChange: canceled pending task";
}
// Posting task with delay allows us to cancel it when connection type is
// changed frequently. This should help us avoid transient edge reporting
// while switching between connection types (e.g. ethernet->wifi).
BrowserThread::PostDelayedTask(
BrowserThread::UI, FROM_HERE,
base::Bind(
&NetworkChangeNotifierChromeos::ReportConnectionChangeOnUIThread,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kOnlineNotificationDelayMS));
}
void NetworkChangeNotifierChromeos::ReportConnectionChangeOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
&NetworkChangeNotifierChromeos::
NotifyObserversOfConnectionTypeChange));
}
// static
void NetworkChangeNotifierChromeos::UpdateInitialState(
NetworkChangeNotifierChromeos* self) {
chromeos::NetworkLibrary* net =
chromeos::CrosLibrary::Get()->GetNetworkLibrary();
self->UpdateNetworkState(net);
}
// static
net::NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierChromeos::GetNetworkConnectionType(
const chromeos::Network* network) {
if (!network || !IsOnline(network->connection_state()))
return net::NetworkChangeNotifier::CONNECTION_NONE;
switch (network->type()) {
case chromeos::TYPE_ETHERNET:
return CONNECTION_ETHERNET;
case chromeos::TYPE_WIFI:
return CONNECTION_WIFI;
case chromeos::TYPE_WIMAX:
return CONNECTION_4G;
case chromeos::TYPE_CELLULAR:
switch (static_cast<const chromeos::CellularNetwork*>(
network)->network_technology()) {
case chromeos::NETWORK_TECHNOLOGY_UNKNOWN:
case chromeos::NETWORK_TECHNOLOGY_1XRTT:
case chromeos::NETWORK_TECHNOLOGY_GPRS:
case chromeos::NETWORK_TECHNOLOGY_EDGE:
return CONNECTION_2G;
case chromeos::NETWORK_TECHNOLOGY_GSM:
case chromeos::NETWORK_TECHNOLOGY_UMTS:
case chromeos::NETWORK_TECHNOLOGY_EVDO:
case chromeos::NETWORK_TECHNOLOGY_HSPA:
return CONNECTION_3G;
case chromeos::NETWORK_TECHNOLOGY_HSPA_PLUS:
case chromeos::NETWORK_TECHNOLOGY_LTE:
case chromeos::NETWORK_TECHNOLOGY_LTE_ADVANCED:
return CONNECTION_4G;
}
case chromeos::TYPE_BLUETOOTH:
case chromeos::TYPE_VPN:
case chromeos::TYPE_UNKNOWN:
break;
}
return net::NetworkChangeNotifier::CONNECTION_UNKNOWN;
}
} // namespace chromeos