network_change_notifier_chromeos.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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.
4
5#include <string>
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/strings/string_util.h"
10#include "base/strings/stringprintf.h"
11#include "chromeos/dbus/dbus_thread_manager.h"
12#include "chromeos/network/network_change_notifier_chromeos.h"
13#include "chromeos/network/network_event_log.h"
14#include "chromeos/network/network_state.h"
15#include "chromeos/network/network_state_handler.h"
16#include "net/base/network_change_notifier.h"
17#include "net/dns/dns_config_service_posix.h"
18#include "third_party/cros_system_api/dbus/service_constants.h"
19
20namespace chromeos {
21
22// DNS config services on Chrome OS are signalled by the network state handler
23// rather than relying on watching files in /etc.
24class NetworkChangeNotifierChromeos::DnsConfigService
25    : public net::internal::DnsConfigServicePosix {
26 public:
27  DnsConfigService();
28  virtual ~DnsConfigService();
29
30  // net::internal::DnsConfigService() overrides.
31  virtual bool StartWatching() OVERRIDE;
32
33  virtual void OnNetworkChange();
34};
35
36NetworkChangeNotifierChromeos::DnsConfigService::DnsConfigService() {
37}
38
39NetworkChangeNotifierChromeos::DnsConfigService::~DnsConfigService() {
40}
41
42bool NetworkChangeNotifierChromeos::DnsConfigService::StartWatching() {
43  // DNS config changes are handled and notified by the network state handlers.
44  return true;
45}
46
47void NetworkChangeNotifierChromeos::DnsConfigService::OnNetworkChange() {
48  InvalidateConfig();
49  InvalidateHosts();
50  ReadNow();
51}
52
53NetworkChangeNotifierChromeos::NetworkChangeNotifierChromeos()
54    : NetworkChangeNotifier(NetworkChangeCalculatorParamsChromeos()),
55      connection_type_(CONNECTION_NONE) {
56}
57
58NetworkChangeNotifierChromeos::~NetworkChangeNotifierChromeos() {
59}
60
61void NetworkChangeNotifierChromeos::Initialize() {
62  DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
63  NetworkHandler::Get()->network_state_handler()->AddObserver(this, FROM_HERE);
64
65  dns_config_service_.reset(new DnsConfigService());
66  dns_config_service_->WatchConfig(
67      base::Bind(net::NetworkChangeNotifier::SetDnsConfig));
68
69  // Update initial connection state.
70  DefaultNetworkChanged(
71      NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
72}
73
74void NetworkChangeNotifierChromeos::Shutdown() {
75  dns_config_service_.reset();
76  NetworkHandler::Get()->network_state_handler()->RemoveObserver(
77      this, FROM_HERE);
78  DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
79}
80
81net::NetworkChangeNotifier::ConnectionType
82NetworkChangeNotifierChromeos::GetCurrentConnectionType() const {
83  return connection_type_;
84}
85
86void NetworkChangeNotifierChromeos::SystemResumed(
87    const base::TimeDelta& sleep_duration) {
88  // Force invalidation of network resources on resume.
89  NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
90}
91
92
93void NetworkChangeNotifierChromeos::DefaultNetworkChanged(
94    const chromeos::NetworkState* default_network) {
95  bool connection_type_changed = false;
96  bool ip_address_changed = false;
97  bool dns_changed = false;
98
99  UpdateState(default_network, &connection_type_changed,
100              &ip_address_changed, &dns_changed);
101
102  if (connection_type_changed)
103    NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
104  if (ip_address_changed)
105    NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
106  if (dns_changed)
107    dns_config_service_->OnNetworkChange();
108}
109
110void NetworkChangeNotifierChromeos::UpdateState(
111    const chromeos::NetworkState* default_network,
112    bool* connection_type_changed,
113    bool* ip_address_changed,
114    bool* dns_changed) {
115  *connection_type_changed = false;
116  *ip_address_changed = false;
117  *dns_changed = false;
118  if (!default_network || !default_network->IsConnectedState()) {
119    // If we lost a default network, we must update our state and notify
120    // observers, otherwise we have nothing to do. (Under normal circumstances,
121    // we should never get duplicate no default network notifications).
122    if (connection_type_ != CONNECTION_NONE) {
123      NET_LOG_EVENT("NCNDefaultNetworkLost", service_path_);
124      *ip_address_changed = true;
125      *dns_changed = true;
126      *connection_type_changed = true;
127      connection_type_ = CONNECTION_NONE;
128      service_path_.clear();
129      ip_address_.clear();
130      dns_servers_.clear();
131    }
132    return;
133  }
134
135  // We do have a default network and it is connected.
136  net::NetworkChangeNotifier::ConnectionType new_connection_type =
137      ConnectionTypeFromShill(default_network->type(),
138                              default_network->network_technology());
139  if (new_connection_type != connection_type_) {
140    NET_LOG_EVENT(
141        "NCNDefaultConnectionTypeChanged",
142        base::StringPrintf("%s -> %s",
143                           ConnectionTypeToString(connection_type_),
144                           ConnectionTypeToString(new_connection_type)));
145    *connection_type_changed = true;
146  }
147  if (default_network->path() != service_path_) {
148    NET_LOG_EVENT(
149        "NCNDefaultNetworkServicePathChanged",
150        base::StringPrintf("%s -> %s",
151                           service_path_.c_str(),
152                           default_network->path().c_str()));
153
154    // If we had a default network service change, network resources
155    // must always be invalidated.
156    *ip_address_changed = true;
157    *dns_changed = true;
158  }
159  if (default_network->ip_address() != ip_address_) {
160    // Is this a state update with an online->online transition?
161    bool stayed_online = (!*connection_type_changed &&
162                          connection_type_ != CONNECTION_NONE);
163
164    bool is_suppressed = true;
165    // Suppress IP address change signalling on online->online transitions
166    // when getting an IP address update for the first time.
167    if (!(stayed_online && ip_address_.empty())) {
168      is_suppressed = false;
169      *ip_address_changed = true;
170    }
171    NET_LOG_EVENT(
172        base::StringPrintf("%s%s",
173                           "NCNDefaultIPAddressChanged",
174                           is_suppressed ? " (Suppressed)" : "" ),
175        base::StringPrintf("%s -> %s",
176                           ip_address_.c_str(),
177                           default_network->ip_address().c_str()));
178  }
179  if (default_network->dns_servers() != dns_servers_) {
180    NET_LOG_EVENT(
181        "NCNDefaultDNSServerChanged",
182        base::StringPrintf(
183            "%s -> %s",
184            JoinString(dns_servers_, ",").c_str(),
185            JoinString(default_network->dns_servers(), ",").c_str()));
186    *dns_changed = true;
187  }
188
189  connection_type_ = new_connection_type;
190  service_path_ = default_network->path();
191  ip_address_ = default_network->ip_address();
192  dns_servers_ = default_network->dns_servers();
193}
194
195// static
196net::NetworkChangeNotifier::ConnectionType
197NetworkChangeNotifierChromeos::ConnectionTypeFromShill(
198    const std::string& type, const std::string& technology) {
199  if (type == flimflam::kTypeEthernet)
200    return CONNECTION_ETHERNET;
201  else if (type == flimflam::kTypeWifi)
202    return CONNECTION_WIFI;
203  else if (type == flimflam::kTypeWimax)
204    return CONNECTION_4G;
205
206  if (type != flimflam::kTypeCellular)
207    return CONNECTION_UNKNOWN;
208
209  // For cellular types, mapping depends on the technology.
210  if (technology == flimflam::kNetworkTechnologyEvdo ||
211      technology == flimflam::kNetworkTechnologyGsm ||
212      technology == flimflam::kNetworkTechnologyUmts ||
213      technology == flimflam::kNetworkTechnologyHspa) {
214    return CONNECTION_3G;
215  } else if (technology == flimflam::kNetworkTechnologyHspaPlus ||
216             technology == flimflam::kNetworkTechnologyLte ||
217             technology == flimflam::kNetworkTechnologyLteAdvanced) {
218    return CONNECTION_4G;
219  } else {
220    return CONNECTION_2G;  // Default cellular type is 2G.
221  }
222}
223
224// static
225net::NetworkChangeNotifier::NetworkChangeCalculatorParams
226NetworkChangeNotifierChromeos::NetworkChangeCalculatorParamsChromeos() {
227  NetworkChangeCalculatorParams params;
228  // Delay values arrived at by simple experimentation and adjusted so as to
229  // produce a single signal when switching between network connections.
230  params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(4000);
231  params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(1000);
232  params.connection_type_offline_delay_ =
233      base::TimeDelta::FromMilliseconds(500);
234  params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
235  return params;
236}
237
238}  // namespace chromeos
239