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