1010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// found in the LICENSE file.
4010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/shell/browser/shell_network_controller_chromeos.h"
6010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
7010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/bind.h"
8010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/location.h"
9010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/logging.h"
10010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/strings/stringprintf.h"
11010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "base/time/time.h"
12010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chromeos/network/network_connection_handler.h"
13010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chromeos/network/network_handler.h"
14010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chromeos/network/network_handler_callbacks.h"
15010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chromeos/network/network_state.h"
16010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "chromeos/network/network_state_handler.h"
17010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "third_party/cros_system_api/dbus/service_constants.h"
18010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)namespace extensions {
20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
21010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)namespace {
22010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
23116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Frequency at which networks should be scanned when not connected to a network
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// or when connected to a non-preferred network.
25010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)const int kScanIntervalSec = 10;
26010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void HandleEnableWifiError(const std::string& error_name,
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                           scoped_ptr<base::DictionaryValue> error_data) {
29010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  LOG(WARNING) << "Unable to enable wifi: " << error_name;
30010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
31010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
32116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// Returns a human-readable name for the network described by |network|.
33116680a4aac90f2aa7413d9095a592090648e557Ben Murdochstd::string GetNetworkName(const chromeos::NetworkState& network) {
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return !network.name().empty()
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             ? network.name()
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             : base::StringPrintf("[%s]", network.type().c_str());
37010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
38010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
39010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Returns true if shill is either connected or connecting to a network.
40010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)bool IsConnectedOrConnecting() {
41010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  chromeos::NetworkStateHandler* state_handler =
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      chromeos::NetworkHandler::Get()->network_state_handler();
43010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  return state_handler->ConnectedNetworkByType(
44010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)             chromeos::NetworkTypePattern::Default()) ||
45010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)         state_handler->ConnectingNetworkByType(
46010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)             chromeos::NetworkTypePattern::Default());
47010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
48010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
49010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}  // namespace
50010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
51116680a4aac90f2aa7413d9095a592090648e557Ben MurdochShellNetworkController::ShellNetworkController(
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const std::string& preferred_network_name)
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    : state_(STATE_IDLE),
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      preferred_network_name_(preferred_network_name),
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      preferred_network_is_active_(false),
56010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      weak_ptr_factory_(this) {
57010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  chromeos::NetworkStateHandler* state_handler =
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      chromeos::NetworkHandler::Get()->network_state_handler();
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  state_handler->AddObserver(this, FROM_HERE);
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  state_handler->SetTechnologyEnabled(
61010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      chromeos::NetworkTypePattern::Primitive(shill::kTypeWifi),
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      true,
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      base::Bind(&HandleEnableWifiError));
64010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
65116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // If we're unconnected, trigger a connection attempt and start scanning.
66116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  NetworkConnectionStateChanged(NULL);
67010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
68010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
69010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)ShellNetworkController::~ShellNetworkController() {
70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  chromeos::NetworkHandler::Get()->network_state_handler()->RemoveObserver(
71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      this, FROM_HERE);
72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
74010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::NetworkListChanged() {
75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  VLOG(1) << "Network list changed";
76010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  ConnectIfUnconnected();
77010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
78010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid ShellNetworkController::NetworkConnectionStateChanged(
80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const chromeos::NetworkState* network) {
81116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (network) {
82116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    VLOG(1) << "Network connection state changed:"
83116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            << " name=" << GetNetworkName(*network)
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            << " type=" << network->type() << " path=" << network->path()
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            << " state=" << network->connection_state();
86010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  } else {
87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    VLOG(1) << "Network connection state changed: [none]";
88010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
90116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const chromeos::NetworkState* wifi_network = GetActiveWiFiNetwork();
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  preferred_network_is_active_ =
92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      wifi_network && wifi_network->name() == preferred_network_name_;
93116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(2) << "Active WiFi network is "
94116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << (wifi_network ? wifi_network->name() : std::string("[none]"));
95116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (preferred_network_is_active_ ||
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      (preferred_network_name_.empty() && wifi_network)) {
98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    SetScanningEnabled(false);
99010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  } else {
100010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    SetScanningEnabled(true);
101010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    ConnectIfUnconnected();
102010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
103010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
104010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const chromeos::NetworkState* ShellNetworkController::GetActiveWiFiNetwork() {
106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  chromeos::NetworkStateHandler* state_handler =
107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      chromeos::NetworkHandler::Get()->network_state_handler();
108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const chromeos::NetworkState* network = state_handler->FirstNetworkByType(
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      chromeos::NetworkTypePattern::Primitive(shill::kTypeWifi));
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return network &&
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 (network->IsConnectedState() || network->IsConnectingState())
1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             ? network
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             : NULL;
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
116010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::SetScanningEnabled(bool enabled) {
117010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  const bool currently_enabled = scan_timer_.IsRunning();
118010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (enabled == currently_enabled)
119010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
120010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
121010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  VLOG(1) << (enabled ? "Starting" : "Stopping") << " scanning";
122010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (enabled) {
123116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    RequestScan();
124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    scan_timer_.Start(FROM_HERE,
125010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                      base::TimeDelta::FromSeconds(kScanIntervalSec),
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      this,
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      &ShellNetworkController::RequestScan);
128010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  } else {
129010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    scan_timer_.Stop();
130010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
131010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
132010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
133010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::RequestScan() {
134010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  VLOG(1) << "Requesting scan";
135010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  chromeos::NetworkHandler::Get()->network_state_handler()->RequestScan();
136010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
137010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
138010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::ConnectIfUnconnected() {
139116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Don't do anything if the default network is already the preferred one or if
140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // we have a pending request to connect to it.
141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (preferred_network_is_active_ ||
142116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      state_ == STATE_WAITING_FOR_PREFERRED_RESULT)
143010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
144010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  const chromeos::NetworkState* best_network = NULL;
146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool can_connect_to_preferred_network = false;
147116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
148116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  chromeos::NetworkHandler* handler = chromeos::NetworkHandler::Get();
149116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  chromeos::NetworkStateHandler::NetworkStateList network_list;
150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  handler->network_state_handler()->GetVisibleNetworkListByType(
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      chromeos::NetworkTypePattern::WiFi(), &network_list);
152010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  for (chromeos::NetworkStateHandler::NetworkStateList::const_iterator it =
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)           network_list.begin();
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       it != network_list.end();
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       ++it) {
156116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const chromeos::NetworkState* network = *it;
157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!network->connectable())
158010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      continue;
159010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!preferred_network_name_.empty() &&
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        network->name() == preferred_network_name_) {
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      best_network = network;
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      can_connect_to_preferred_network = true;
164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      break;
165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    } else if (!best_network) {
166116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      best_network = network;
167116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
168116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
169116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
170116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Don't switch networks if we're already connecting/connected and wouldn't be
171116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // switching to the preferred network.
172116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if ((IsConnectedOrConnecting() || state_ != STATE_IDLE) &&
173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      !can_connect_to_preferred_network)
174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
175010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!best_network) {
177116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    VLOG(1) << "Didn't find any connectable networks";
178010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    return;
179010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
180010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
181116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "Connecting to network " << GetNetworkName(*best_network)
182116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << " with path " << best_network->path() << " and strength "
183116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << best_network->signal_strength();
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  state_ = can_connect_to_preferred_network
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               ? STATE_WAITING_FOR_PREFERRED_RESULT
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)               : STATE_WAITING_FOR_NON_PREFERRED_RESULT;
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  handler->network_connection_handler()->ConnectToNetwork(
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      best_network->path(),
189116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::Bind(&ShellNetworkController::HandleConnectionSuccess,
190116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                 weak_ptr_factory_.GetWeakPtr()),
191116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::Bind(&ShellNetworkController::HandleConnectionError,
192116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                 weak_ptr_factory_.GetWeakPtr()),
193116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      false /* check_error_state */);
194010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
195010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
196010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::HandleConnectionSuccess() {
197010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  VLOG(1) << "Successfully connected to network";
198116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  state_ = STATE_IDLE;
199010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
200010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
201010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)void ShellNetworkController::HandleConnectionError(
202010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const std::string& error_name,
203010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    scoped_ptr<base::DictionaryValue> error_data) {
204010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  LOG(WARNING) << "Unable to connect to network: " << error_name;
205116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  state_ = STATE_IDLE;
206010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}
207010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
2085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}  // namespace extensions
209