shell_network_controller_chromeos.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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 "extensions/shell/browser/shell_network_controller_chromeos.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/logging.h"
10#include "base/strings/stringprintf.h"
11#include "base/time/time.h"
12#include "chromeos/network/network_connection_handler.h"
13#include "chromeos/network/network_handler.h"
14#include "chromeos/network/network_handler_callbacks.h"
15#include "chromeos/network/network_state.h"
16#include "chromeos/network/network_state_handler.h"
17#include "third_party/cros_system_api/dbus/service_constants.h"
18
19namespace extensions {
20
21namespace {
22
23// Frequency at which networks should be scanned when not connected to a network
24// or when connected to a non-preferred network.
25const int kScanIntervalSec = 10;
26
27void HandleEnableWifiError(const std::string& error_name,
28                           scoped_ptr<base::DictionaryValue> error_data) {
29  LOG(WARNING) << "Unable to enable wifi: " << error_name;
30}
31
32// Returns a human-readable name for the network described by |network|.
33std::string GetNetworkName(const chromeos::NetworkState& network) {
34  return !network.name().empty()
35             ? network.name()
36             : base::StringPrintf("[%s]", network.type().c_str());
37}
38
39// Returns true if shill is either connected or connecting to a network.
40bool IsConnectedOrConnecting() {
41  chromeos::NetworkStateHandler* state_handler =
42      chromeos::NetworkHandler::Get()->network_state_handler();
43  return state_handler->ConnectedNetworkByType(
44             chromeos::NetworkTypePattern::Default()) ||
45         state_handler->ConnectingNetworkByType(
46             chromeos::NetworkTypePattern::Default());
47}
48
49}  // namespace
50
51ShellNetworkController::ShellNetworkController(
52    const std::string& preferred_network_name)
53    : state_(STATE_IDLE),
54      preferred_network_name_(preferred_network_name),
55      preferred_network_is_active_(false),
56      weak_ptr_factory_(this) {
57  chromeos::NetworkHandler::Initialize();
58  chromeos::NetworkStateHandler* state_handler =
59      chromeos::NetworkHandler::Get()->network_state_handler();
60  state_handler->AddObserver(this, FROM_HERE);
61  state_handler->SetTechnologyEnabled(
62      chromeos::NetworkTypePattern::Primitive(shill::kTypeWifi),
63      true,
64      base::Bind(&HandleEnableWifiError));
65
66  // If we're unconnected, trigger a connection attempt and start scanning.
67  NetworkConnectionStateChanged(NULL);
68}
69
70ShellNetworkController::~ShellNetworkController() {
71  chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
72      this, FROM_HERE);
73  chromeos::NetworkHandler::Shutdown();
74}
75
76void ShellNetworkController::NetworkListChanged() {
77  VLOG(1) << "Network list changed";
78  ConnectIfUnconnected();
79}
80
81void ShellNetworkController::NetworkConnectionStateChanged(
82    const chromeos::NetworkState* network) {
83  if (network) {
84    VLOG(1) << "Network connection state changed:"
85            << " name=" << GetNetworkName(*network)
86            << " type=" << network->type() << " path=" << network->path()
87            << " state=" << network->connection_state();
88  } else {
89    VLOG(1) << "Network connection state changed: [none]";
90  }
91
92  const chromeos::NetworkState* wifi_network = GetActiveWiFiNetwork();
93  preferred_network_is_active_ =
94      wifi_network && wifi_network->name() == preferred_network_name_;
95  VLOG(2) << "Active WiFi network is "
96          << (wifi_network ? wifi_network->name() : std::string("[none]"));
97
98  if (preferred_network_is_active_ ||
99      (preferred_network_name_.empty() && wifi_network)) {
100    SetScanningEnabled(false);
101  } else {
102    SetScanningEnabled(true);
103    ConnectIfUnconnected();
104  }
105}
106
107const chromeos::NetworkState* ShellNetworkController::GetActiveWiFiNetwork() {
108  chromeos::NetworkStateHandler* state_handler =
109      chromeos::NetworkHandler::Get()->network_state_handler();
110  const chromeos::NetworkState* network = state_handler->FirstNetworkByType(
111      chromeos::NetworkTypePattern::Primitive(shill::kTypeWifi));
112  return network &&
113                 (network->IsConnectedState() || network->IsConnectingState())
114             ? network
115             : NULL;
116}
117
118void ShellNetworkController::SetScanningEnabled(bool enabled) {
119  const bool currently_enabled = scan_timer_.IsRunning();
120  if (enabled == currently_enabled)
121    return;
122
123  VLOG(1) << (enabled ? "Starting" : "Stopping") << " scanning";
124  if (enabled) {
125    RequestScan();
126    scan_timer_.Start(FROM_HERE,
127                      base::TimeDelta::FromSeconds(kScanIntervalSec),
128                      this,
129                      &ShellNetworkController::RequestScan);
130  } else {
131    scan_timer_.Stop();
132  }
133}
134
135void ShellNetworkController::RequestScan() {
136  VLOG(1) << "Requesting scan";
137  chromeos::NetworkHandler::Get()->network_state_handler()->RequestScan();
138}
139
140void ShellNetworkController::ConnectIfUnconnected() {
141  // Don't do anything if the default network is already the preferred one or if
142  // we have a pending request to connect to it.
143  if (preferred_network_is_active_ ||
144      state_ == STATE_WAITING_FOR_PREFERRED_RESULT)
145    return;
146
147  const chromeos::NetworkState* best_network = NULL;
148  bool can_connect_to_preferred_network = false;
149
150  chromeos::NetworkHandler* handler = chromeos::NetworkHandler::Get();
151  chromeos::NetworkStateHandler::NetworkStateList network_list;
152  handler->network_state_handler()->GetVisibleNetworkListByType(
153      chromeos::NetworkTypePattern::WiFi(), &network_list);
154  for (chromeos::NetworkStateHandler::NetworkStateList::const_iterator it =
155           network_list.begin();
156       it != network_list.end();
157       ++it) {
158    const chromeos::NetworkState* network = *it;
159    if (!network->connectable())
160      continue;
161
162    if (!preferred_network_name_.empty() &&
163        network->name() == preferred_network_name_) {
164      best_network = network;
165      can_connect_to_preferred_network = true;
166      break;
167    } else if (!best_network) {
168      best_network = network;
169    }
170  }
171
172  // Don't switch networks if we're already connecting/connected and wouldn't be
173  // switching to the preferred network.
174  if ((IsConnectedOrConnecting() || state_ != STATE_IDLE) &&
175      !can_connect_to_preferred_network)
176    return;
177
178  if (!best_network) {
179    VLOG(1) << "Didn't find any connectable networks";
180    return;
181  }
182
183  VLOG(1) << "Connecting to network " << GetNetworkName(*best_network)
184          << " with path " << best_network->path() << " and strength "
185          << best_network->signal_strength();
186  state_ = can_connect_to_preferred_network
187               ? STATE_WAITING_FOR_PREFERRED_RESULT
188               : STATE_WAITING_FOR_NON_PREFERRED_RESULT;
189  handler->network_connection_handler()->ConnectToNetwork(
190      best_network->path(),
191      base::Bind(&ShellNetworkController::HandleConnectionSuccess,
192                 weak_ptr_factory_.GetWeakPtr()),
193      base::Bind(&ShellNetworkController::HandleConnectionError,
194                 weak_ptr_factory_.GetWeakPtr()),
195      false /* check_error_state */);
196}
197
198void ShellNetworkController::HandleConnectionSuccess() {
199  VLOG(1) << "Successfully connected to network";
200  state_ = STATE_IDLE;
201}
202
203void ShellNetworkController::HandleConnectionError(
204    const std::string& error_name,
205    scoped_ptr<base::DictionaryValue> error_data) {
206  LOG(WARNING) << "Unable to connect to network: " << error_name;
207  state_ = STATE_IDLE;
208}
209
210}  // namespace extensions
211