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 "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/chromeos/net/proxy_config_handler.h"
13#include "chrome/browser/prefs/proxy_config_dictionary.h"
14#include "chrome/browser/prefs/proxy_prefs.h"
15#include "chromeos/network/network_state.h"
16#include "chromeos/network/network_state_handler.h"
17#include "net/proxy/proxy_config.h"
18#include "third_party/cros_system_api/dbus/service_constants.h"
19
20namespace chromeos {
21
22namespace {
23
24const char kNetworkStateOffline[] = "offline";
25const char kNetworkStateOnline[] = "online";
26const char kNetworkStateCaptivePortal[] = "behind captive portal";
27const char kNetworkStateConnecting[] = "connecting";
28const char kNetworkStateProxyAuthRequired[] = "proxy auth required";
29
30bool HasDefaultNetworkProxyConfigured() {
31  const NetworkState* network =
32      NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
33  if (!network)
34    return false;
35  onc::ONCSource onc_source = onc::ONC_SOURCE_NONE;
36  scoped_ptr<ProxyConfigDictionary> proxy_dict =
37      proxy_config::GetProxyConfigForNetwork(
38          NULL, g_browser_process->local_state(), *network, &onc_source);
39  ProxyPrefs::ProxyMode mode;
40  return (proxy_dict && proxy_dict->GetMode(&mode) &&
41          mode == ProxyPrefs::MODE_FIXED_SERVERS);
42}
43
44NetworkStateInformer::State GetStateForDefaultNetwork() {
45  const NetworkState* network =
46      NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
47  if (!network)
48    return NetworkStateInformer::OFFLINE;
49
50  if (NetworkPortalDetector::Get()->IsEnabled()) {
51    NetworkPortalDetector::CaptivePortalState state =
52        NetworkPortalDetector::Get()->GetCaptivePortalState(network->guid());
53    NetworkPortalDetector::CaptivePortalStatus status = state.status;
54    if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
55        NetworkState::StateIsConnecting(network->connection_state())) {
56      return NetworkStateInformer::CONNECTING;
57    }
58    // For proxy-less networks rely on shill's online state if
59    // NetworkPortalDetector's state of current network is unknown.
60    if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE ||
61        (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
62         !HasDefaultNetworkProxyConfigured() &&
63         network->connection_state() == shill::kStateOnline)) {
64      return NetworkStateInformer::ONLINE;
65    }
66    if (status ==
67            NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PROXY_AUTH_REQUIRED &&
68        HasDefaultNetworkProxyConfigured()) {
69      return NetworkStateInformer::PROXY_AUTH_REQUIRED;
70    }
71    if (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL ||
72        (status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_UNKNOWN &&
73         network->connection_state() == shill::kStatePortal))
74      return NetworkStateInformer::CAPTIVE_PORTAL;
75  } else {
76    if (NetworkState::StateIsConnecting(network->connection_state()))
77      return NetworkStateInformer::CONNECTING;
78    if (network->connection_state() == shill::kStateOnline)
79      return NetworkStateInformer::ONLINE;
80    if (network->connection_state() == shill::kStatePortal)
81      return NetworkStateInformer::CAPTIVE_PORTAL;
82  }
83  return NetworkStateInformer::OFFLINE;
84}
85
86}  // namespace
87
88NetworkStateInformer::NetworkStateInformer()
89    : state_(OFFLINE),
90      weak_ptr_factory_(this) {
91}
92
93NetworkStateInformer::~NetworkStateInformer() {
94  if (NetworkHandler::IsInitialized()) {
95    NetworkHandler::Get()->network_state_handler()->RemoveObserver(
96        this, FROM_HERE);
97  }
98  NetworkPortalDetector::Get()->RemoveObserver(this);
99}
100
101void NetworkStateInformer::Init() {
102  UpdateState();
103  NetworkHandler::Get()->network_state_handler()->AddObserver(
104      this, FROM_HERE);
105
106  NetworkPortalDetector::Get()->AddAndFireObserver(this);
107
108  registrar_.Add(this,
109                 chrome::NOTIFICATION_LOGIN_PROXY_CHANGED,
110                 content::NotificationService::AllSources());
111  registrar_.Add(this,
112                 chrome::NOTIFICATION_SESSION_STARTED,
113                 content::NotificationService::AllSources());
114}
115
116void NetworkStateInformer::AddObserver(NetworkStateInformerObserver* observer) {
117  if (!observers_.HasObserver(observer))
118    observers_.AddObserver(observer);
119}
120
121void NetworkStateInformer::RemoveObserver(
122    NetworkStateInformerObserver* observer) {
123  observers_.RemoveObserver(observer);
124}
125
126void NetworkStateInformer::DefaultNetworkChanged(const NetworkState* network) {
127  UpdateStateAndNotify();
128}
129
130void NetworkStateInformer::OnPortalDetectionCompleted(
131    const NetworkState* network,
132    const NetworkPortalDetector::CaptivePortalState& state) {
133  UpdateStateAndNotify();
134}
135
136void NetworkStateInformer::Observe(
137    int type,
138    const content::NotificationSource& source,
139    const content::NotificationDetails& details) {
140  if (type == chrome::NOTIFICATION_SESSION_STARTED)
141    registrar_.RemoveAll();
142  else if (type == chrome::NOTIFICATION_LOGIN_PROXY_CHANGED)
143    SendStateToObservers(ErrorScreenActor::ERROR_REASON_PROXY_CONFIG_CHANGED);
144  else
145    NOTREACHED() << "Unknown notification: " << type;
146}
147
148void NetworkStateInformer::OnPortalDetected() {
149  UpdateStateAndNotify();
150}
151
152// static
153const char* NetworkStateInformer::StatusString(State state) {
154  switch (state) {
155    case OFFLINE:
156      return kNetworkStateOffline;
157    case ONLINE:
158      return kNetworkStateOnline;
159    case CAPTIVE_PORTAL:
160      return kNetworkStateCaptivePortal;
161    case CONNECTING:
162      return kNetworkStateConnecting;
163    case PROXY_AUTH_REQUIRED:
164      return kNetworkStateProxyAuthRequired;
165    default:
166      NOTREACHED();
167      return NULL;
168  }
169}
170
171bool NetworkStateInformer::UpdateState() {
172  const NetworkState* default_network =
173      NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
174  State new_state = GetStateForDefaultNetwork();
175  std::string new_network_path;
176  std::string new_network_type;
177  if (default_network) {
178    new_network_path = default_network->path();
179    new_network_type = default_network->type();
180  }
181
182  bool updated = (new_state != state_) ||
183      (new_network_path != network_path_) ||
184      (new_network_type != network_type_);
185  state_ = new_state;
186  network_path_ = new_network_path;
187  network_type_ = new_network_type;
188
189  if (updated && state_ == ONLINE) {
190    FOR_EACH_OBSERVER(NetworkStateInformerObserver, observers_,
191                      OnNetworkReady());
192  }
193
194  return updated;
195}
196
197void NetworkStateInformer::UpdateStateAndNotify() {
198  if (UpdateState())
199    SendStateToObservers(ErrorScreenActor::ERROR_REASON_NETWORK_STATE_CHANGED);
200  else
201    SendStateToObservers(ErrorScreenActor::ERROR_REASON_UPDATE);
202}
203
204void NetworkStateInformer::SendStateToObservers(
205    ErrorScreenActor::ErrorReason reason) {
206  FOR_EACH_OBSERVER(NetworkStateInformerObserver, observers_,
207      UpdateState(reason));
208}
209
210}  // namespace chromeos
211