blob: d10bf36b3b7209bb49d858e0ed0ff981bff8a175 [file] [log] [blame]
Ben Chan99c8a4d2012-05-01 08:11:53 -07001// Copyright (c) 2012 The Chromium OS 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 "shill/wimax.h"
6
Darin Petkov912f0de2012-05-16 14:12:14 +02007#include <base/bind.h>
Ben Chana0ddf462014-02-06 11:32:42 -08008#include <base/strings/string_util.h>
9#include <base/strings/stringprintf.h>
Darin Petkov912f0de2012-05-16 14:12:14 +020010
Darin Petkov25665aa2012-05-21 14:08:12 +020011#include "shill/key_value_store.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070012#include "shill/logging.h"
Darin Petkov912f0de2012-05-16 14:12:14 +020013#include "shill/manager.h"
Darin Petkovb72b62e2012-05-15 16:55:36 +020014#include "shill/proxy_factory.h"
Darin Petkovb72b62e2012-05-15 16:55:36 +020015#include "shill/wimax_device_proxy_interface.h"
Darin Petkov912f0de2012-05-16 14:12:14 +020016#include "shill/wimax_service.h"
Darin Petkovb72b62e2012-05-15 16:55:36 +020017
Darin Petkov912f0de2012-05-16 14:12:14 +020018using base::Bind;
Darin Petkovd1cd7972012-05-22 15:26:15 +020019using std::set;
Ben Chan99c8a4d2012-05-01 08:11:53 -070020using std::string;
21
22namespace shill {
23
Ben Chan92ca56c2013-08-16 17:01:11 -070024namespace {
25
26const char *DeviceStatusToString(wimax_manager::DeviceStatus status) {
27 switch (status) {
28 case wimax_manager::kDeviceStatusUninitialized:
29 return "Uninitialized";
30 case wimax_manager::kDeviceStatusDisabled:
31 return "Disabled";
32 case wimax_manager::kDeviceStatusReady:
33 return "Ready";
34 case wimax_manager::kDeviceStatusScanning:
35 return "Scanning";
36 case wimax_manager::kDeviceStatusConnecting:
37 return "Connecting";
38 case wimax_manager::kDeviceStatusConnected:
39 return "Connected";
40 default:
41 return "Unknown";
42 }
43}
44
45} // namespace
46
Darin Petkov3a4100c2012-06-14 11:36:59 +020047const int WiMax::kDefaultConnectTimeoutSeconds = 60;
48const int WiMax::kDefaultRPCTimeoutSeconds = 30;
Darin Petkov912f0de2012-05-16 14:12:14 +020049
Ben Chan99c8a4d2012-05-01 08:11:53 -070050WiMax::WiMax(ControlInterface *control,
51 EventDispatcher *dispatcher,
52 Metrics *metrics,
53 Manager *manager,
54 const string &link_name,
Ben Chan4e64d2d2012-05-16 00:02:25 -070055 const string &address,
Darin Petkovb72b62e2012-05-15 16:55:36 +020056 int interface_index,
57 const RpcIdentifier &path)
Ben Chan4e64d2d2012-05-16 00:02:25 -070058 : Device(control, dispatcher, metrics, manager, link_name, address,
Darin Petkovb72b62e2012-05-15 16:55:36 +020059 interface_index, Technology::kWiMax),
60 path_(path),
Darin Petkov3a4100c2012-06-14 11:36:59 +020061 weak_ptr_factory_(this),
Darin Petkov9893d9c2012-05-17 15:27:31 -070062 scanning_(false),
Darin Petkov3a4100c2012-06-14 11:36:59 +020063 status_(wimax_manager::kDeviceStatusUninitialized),
64 proxy_factory_(ProxyFactory::GetInstance()),
65 connect_timeout_seconds_(kDefaultConnectTimeoutSeconds) {
Darin Petkovb96a4512012-06-04 11:02:49 +020066 LOG(INFO) << "WiMAX device created: " << link_name << " @ " << path;
Darin Petkov9893d9c2012-05-17 15:27:31 -070067 PropertyStore *store = mutable_store();
Ben Chan4276cca2013-09-20 10:07:04 -070068 store->RegisterConstBool(kScanningProperty, &scanning_);
Darin Petkovb72b62e2012-05-15 16:55:36 +020069}
Ben Chan99c8a4d2012-05-01 08:11:53 -070070
Darin Petkovb72b62e2012-05-15 16:55:36 +020071WiMax::~WiMax() {
Darin Petkovb96a4512012-06-04 11:02:49 +020072 LOG(INFO) << "WiMAX device destroyed: " << link_name();
Darin Petkovb72b62e2012-05-15 16:55:36 +020073}
Ben Chan99c8a4d2012-05-01 08:11:53 -070074
75void WiMax::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkovb72b62e2012-05-15 16:55:36 +020076 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -070077 scanning_ = false;
Darin Petkovb72b62e2012-05-15 16:55:36 +020078 proxy_.reset(proxy_factory_->CreateWiMaxDeviceProxy(path_));
Darin Petkov9893d9c2012-05-17 15:27:31 -070079 proxy_->set_networks_changed_callback(
80 Bind(&WiMax::OnNetworksChanged, Unretained(this)));
Darin Petkov8ea0eaf2012-05-29 11:21:33 +020081 proxy_->set_status_changed_callback(
82 Bind(&WiMax::OnStatusChanged, Unretained(this)));
Darin Petkov912f0de2012-05-16 14:12:14 +020083 proxy_->Enable(
Darin Petkov3a4100c2012-06-14 11:36:59 +020084 error, Bind(&WiMax::OnEnableComplete, this, callback),
85 kDefaultRPCTimeoutSeconds * 1000);
Ben Chan99c8a4d2012-05-01 08:11:53 -070086}
87
88void WiMax::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkovb72b62e2012-05-15 16:55:36 +020089 SLOG(WiMax, 2) << __func__;
Darin Petkov3a4100c2012-06-14 11:36:59 +020090 StopConnectTimeout();
91 if (pending_service_) {
92 pending_service_->SetState(Service::kStateIdle);
93 pending_service_ = NULL;
94 }
Darin Petkovc63dcf02012-05-24 11:51:43 +020095 if (selected_service()) {
96 Error error;
97 DisconnectFrom(selected_service(), &error);
98 }
Darin Petkovb96a4512012-06-04 11:02:49 +020099 scanning_ = false;
Darin Petkovd1cd7972012-05-22 15:26:15 +0200100 networks_.clear();
Darin Petkovc63dcf02012-05-24 11:51:43 +0200101 manager()->wimax_provider()->OnNetworksChanged();
Darin Petkovb96a4512012-06-04 11:02:49 +0200102 if (proxy_.get()) {
103 proxy_->Disable(
104 error, Bind(&WiMax::OnDisableComplete, this, callback),
Darin Petkov3a4100c2012-06-14 11:36:59 +0200105 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkovb96a4512012-06-04 11:02:49 +0200106 } else {
107 OnDisableComplete(callback, Error());
108 }
Ben Chan99c8a4d2012-05-01 08:11:53 -0700109}
110
Wade Guthrie4823f4f2013-07-25 10:03:03 -0700111void WiMax::Scan(ScanType /*scan_type*/, Error *error,
112 const string &/*reason*/) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200113 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700114 if (scanning_) {
115 Error::PopulateAndLog(
116 error, Error::kInProgress, "Scan already in progress.");
117 return;
118 }
119 scanning_ = true;
120 proxy_->ScanNetworks(
Darin Petkov3a4100c2012-06-14 11:36:59 +0200121 error, Bind(&WiMax::OnScanNetworksComplete, this),
122 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700123 if (error->IsFailure()) {
124 OnScanNetworksComplete(*error);
125 }
Ben Chan99c8a4d2012-05-01 08:11:53 -0700126}
127
Darin Petkov9893d9c2012-05-17 15:27:31 -0700128void WiMax::ConnectTo(const WiMaxServiceRefPtr &service, Error *error) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200129 SLOG(WiMax, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700130 if (pending_service_) {
131 Error::PopulateAndLog(
132 error, Error::kInProgress,
133 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100134 "Pending connect to service %s, ignoring connect request to %s.",
135 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200136 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700137 return;
138 }
139 service->SetState(Service::kStateAssociating);
140 pending_service_ = service;
Ben Chan4e5c1312012-05-18 18:45:38 -0700141
Darin Petkov3a4100c2012-06-14 11:36:59 +0200142 // We use the RPC device status to determine the outcome of the connect
143 // operation by listening for status updates in OnStatusChanged. A transition
144 // to Connected means success. A transition to Connecting and then to a status
145 // different than Connected means failure. Also, schedule a connect timeout to
146 // guard against the RPC device never transitioning to a Connecting or a
147 // Connected state.
148 status_ = wimax_manager::kDeviceStatusUninitialized;
149 StartConnectTimeout();
150
Darin Petkov25665aa2012-05-21 14:08:12 +0200151 KeyValueStore parameters;
Ben Chan4e5c1312012-05-18 18:45:38 -0700152 service->GetConnectParameters(&parameters);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700153 proxy_->Connect(
Ben Chan4e5c1312012-05-18 18:45:38 -0700154 service->GetNetworkObjectPath(), parameters,
Darin Petkov3a4100c2012-06-14 11:36:59 +0200155 error, Bind(&WiMax::OnConnectComplete, this),
156 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700157 if (error->IsFailure()) {
158 OnConnectComplete(*error);
159 }
160}
161
Darin Petkovc63dcf02012-05-24 11:51:43 +0200162void WiMax::DisconnectFrom(const ServiceRefPtr &service, Error *error) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200163 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700164 if (pending_service_) {
165 Error::PopulateAndLog(
166 error, Error::kInProgress,
167 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100168 "Pending connect to service %s, "
169 "ignoring disconnect request from %s.",
170 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200171 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700172 return;
173 }
174 if (selected_service() && service != selected_service()) {
175 Error::PopulateAndLog(
176 error, Error::kNotConnected,
177 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100178 "Current service is %s, ignoring disconnect request from %s.",
179 selected_service()->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200180 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700181 return;
182 }
Darin Petkovc63dcf02012-05-24 11:51:43 +0200183 DropConnection();
Darin Petkov912f0de2012-05-16 14:12:14 +0200184 proxy_->Disconnect(
Darin Petkov3a4100c2012-06-14 11:36:59 +0200185 error, Bind(&WiMax::OnDisconnectComplete, this),
186 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700187 if (error->IsFailure()) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200188 OnDisconnectComplete(*error);
189 }
190}
191
Darin Petkov6b9b2e12012-07-10 15:51:42 +0200192bool WiMax::IsIdle() const {
193 return !pending_service_ && !selected_service();
194}
195
Darin Petkovc63dcf02012-05-24 11:51:43 +0200196void WiMax::OnServiceStopped(const WiMaxServiceRefPtr &service) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200197 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200198 if (service == selected_service()) {
199 DropConnection();
200 }
Darin Petkovc1e52732012-05-25 15:23:45 +0200201 if (service == pending_service_) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200202 pending_service_ = NULL;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700203 }
204}
205
Darin Petkovb96a4512012-06-04 11:02:49 +0200206void WiMax::OnDeviceVanished() {
207 LOG(INFO) << "WiMAX device vanished: " << link_name();
208 proxy_.reset();
209 DropService(Service::kStateIdle);
210 // Disable the device. This will also clear any relevant properties such as
211 // the live network set.
212 SetEnabled(false);
213}
214
Darin Petkov9893d9c2012-05-17 15:27:31 -0700215void WiMax::OnScanNetworksComplete(const Error &/*error*/) {
216 SLOG(WiMax, 2) << __func__;
217 scanning_ = false;
218 // The networks are updated when the NetworksChanged signal is received.
Darin Petkov912f0de2012-05-16 14:12:14 +0200219}
220
221void WiMax::OnConnectComplete(const Error &error) {
222 SLOG(WiMax, 2) << __func__;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200223 if (error.IsSuccess()) {
224 // Nothing to do -- the connection process is resumed on the StatusChanged
225 // signal.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700226 return;
227 }
Darin Petkovb96a4512012-06-04 11:02:49 +0200228 DropService(Service::kStateFailure);
Darin Petkov912f0de2012-05-16 14:12:14 +0200229}
230
Darin Petkov9893d9c2012-05-17 15:27:31 -0700231void WiMax::OnDisconnectComplete(const Error &/*error*/) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200232 SLOG(WiMax, 2) << __func__;
Darin Petkov912f0de2012-05-16 14:12:14 +0200233}
234
235void WiMax::OnEnableComplete(const EnabledStateChangedCallback &callback,
236 const Error &error) {
237 SLOG(WiMax, 2) << __func__;
238 if (error.IsFailure()) {
239 proxy_.reset();
240 } else {
Darin Petkov59f2d692012-06-07 15:57:46 +0200241 LOG(INFO) << "WiMAX device " << link_name() << " enabled.";
242 // Updates the live networks based on the current WiMaxManager.Device
243 // networks. The RPC device will signal when the network set changes.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700244 Error e;
Darin Petkov59f2d692012-06-07 15:57:46 +0200245 OnNetworksChanged(proxy_->Networks(&e));
Darin Petkov912f0de2012-05-16 14:12:14 +0200246 }
247 callback.Run(error);
Darin Petkov912f0de2012-05-16 14:12:14 +0200248}
249
250void WiMax::OnDisableComplete(const EnabledStateChangedCallback &callback,
251 const Error &error) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200252 LOG(INFO) << "WiMAX device " << link_name() << " disabled.";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700253 proxy_.reset();
Darin Petkov912f0de2012-05-16 14:12:14 +0200254 callback.Run(error);
Ben Chan99c8a4d2012-05-01 08:11:53 -0700255}
256
Darin Petkov9893d9c2012-05-17 15:27:31 -0700257void WiMax::OnNetworksChanged(const RpcIdentifiers &networks) {
258 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200259 networks_.clear();
260 networks_.insert(networks.begin(), networks.end());
261 manager()->wimax_provider()->OnNetworksChanged();
Darin Petkovd1cd7972012-05-22 15:26:15 +0200262}
263
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200264void WiMax::OnStatusChanged(wimax_manager::DeviceStatus status) {
Ben Chan92ca56c2013-08-16 17:01:11 -0700265 SLOG(WiMax, 2) << "WiMAX device " << link_name()
266 << " status: " << DeviceStatusToString(status);
Darin Petkov3a4100c2012-06-14 11:36:59 +0200267 wimax_manager::DeviceStatus old_status = status_;
268 status_ = status;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200269 switch (status) {
270 case wimax_manager::kDeviceStatusConnected:
271 if (!pending_service_) {
272 LOG(WARNING) << "Unexpected status change; ignored.";
273 return;
274 }
Darin Petkov3a4100c2012-06-14 11:36:59 +0200275 // Stops the connect timeout -- the DHCP provider has a separate timeout.
276 StopConnectTimeout();
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200277 if (AcquireIPConfig()) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200278 LOG(INFO) << "WiMAX device " << link_name() << " connected to "
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200279 << pending_service_->GetStorageIdentifier();
280 SelectService(pending_service_);
Darin Petkovb96a4512012-06-04 11:02:49 +0200281 pending_service_ = NULL;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200282 SetServiceState(Service::kStateConfiguring);
283 } else {
Darin Petkovb96a4512012-06-04 11:02:49 +0200284 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200285 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200286 break;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200287 case wimax_manager::kDeviceStatusConnecting:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200288 LOG(INFO) << "WiMAX device " << link_name() << " connecting...";
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200289 // Nothing to do.
290 break;
291 default:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200292 // We may receive a queued up status update (e.g., to Scanning) before
293 // receiving the status update to Connecting, so be careful to fail the
294 // service only on the right status transition.
295 if (old_status == wimax_manager::kDeviceStatusConnecting ||
296 old_status == wimax_manager::kDeviceStatusConnected) {
297 LOG(INFO) << "WiMAX device " << link_name()
Ben Chan92ca56c2013-08-16 17:01:11 -0700298 << " status: " << DeviceStatusToString(old_status)
299 << " -> " << DeviceStatusToString(status);
Ben Chane494e312013-08-23 12:56:05 -0700300 // TODO(benchan): Investigate a method to determine if the connection
301 // failure is due to incorrect EAP credentials and indicate that via
302 // Service::kFailureBadPassphrase (crosbug.com/p/16324).
Darin Petkov3a4100c2012-06-14 11:36:59 +0200303 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200304 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200305 break;
306 }
307}
308
Darin Petkovb96a4512012-06-04 11:02:49 +0200309void WiMax::DropService(Service::ConnectState state) {
310 SLOG(WiMax, 2) << __func__
311 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov3a4100c2012-06-14 11:36:59 +0200312 StopConnectTimeout();
Darin Petkovb96a4512012-06-04 11:02:49 +0200313 if (pending_service_) {
314 LOG(WARNING) << "Unable to initiate connection to: "
315 << pending_service_->GetStorageIdentifier();
316 pending_service_->SetState(state);
317 pending_service_ = NULL;
318 }
319 if (selected_service()) {
320 LOG(WARNING) << "Service disconnected: "
321 << selected_service()->GetStorageIdentifier();
322 selected_service()->SetState(state);
323 DropConnection();
324 }
325}
326
Darin Petkov3a4100c2012-06-14 11:36:59 +0200327void WiMax::StartConnectTimeout() {
328 SLOG(WiMax, 2) << __func__;
329 if (IsConnectTimeoutStarted()) {
330 return;
331 }
332 connect_timeout_callback_.Reset(
333 Bind(&WiMax::OnConnectTimeout, weak_ptr_factory_.GetWeakPtr()));
334 dispatcher()->PostDelayedTask(
335 connect_timeout_callback_.callback(), connect_timeout_seconds_ * 1000);
336}
337
338void WiMax::StopConnectTimeout() {
339 SLOG(WiMax, 2) << __func__;
340 connect_timeout_callback_.Cancel();
341}
342
343bool WiMax::IsConnectTimeoutStarted() const {
344 return !connect_timeout_callback_.IsCancelled();
345}
346
347void WiMax::OnConnectTimeout() {
348 LOG(ERROR) << "WiMAX device " << link_name() << ": connect timeout.";
349 StopConnectTimeout();
350 DropService(Service::kStateFailure);
351}
352
Ben Chan99c8a4d2012-05-01 08:11:53 -0700353} // namespace shill