blob: cbc87b75d0b0d6273056922fa562caffcf6cc690 [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>
Darin Petkovd1cd7972012-05-22 15:26:15 +02008#include <base/string_util.h>
Darin Petkov9893d9c2012-05-17 15:27:31 -07009#include <base/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
Darin Petkov3a4100c2012-06-14 11:36:59 +020024const int WiMax::kDefaultConnectTimeoutSeconds = 60;
25const int WiMax::kDefaultRPCTimeoutSeconds = 30;
Darin Petkov912f0de2012-05-16 14:12:14 +020026
Ben Chan99c8a4d2012-05-01 08:11:53 -070027WiMax::WiMax(ControlInterface *control,
28 EventDispatcher *dispatcher,
29 Metrics *metrics,
30 Manager *manager,
31 const string &link_name,
Ben Chan4e64d2d2012-05-16 00:02:25 -070032 const string &address,
Darin Petkovb72b62e2012-05-15 16:55:36 +020033 int interface_index,
34 const RpcIdentifier &path)
Ben Chan4e64d2d2012-05-16 00:02:25 -070035 : Device(control, dispatcher, metrics, manager, link_name, address,
Darin Petkovb72b62e2012-05-15 16:55:36 +020036 interface_index, Technology::kWiMax),
37 path_(path),
Darin Petkov3a4100c2012-06-14 11:36:59 +020038 weak_ptr_factory_(this),
Darin Petkov9893d9c2012-05-17 15:27:31 -070039 scanning_(false),
Darin Petkov3a4100c2012-06-14 11:36:59 +020040 status_(wimax_manager::kDeviceStatusUninitialized),
41 proxy_factory_(ProxyFactory::GetInstance()),
42 connect_timeout_seconds_(kDefaultConnectTimeoutSeconds) {
Darin Petkovb96a4512012-06-04 11:02:49 +020043 LOG(INFO) << "WiMAX device created: " << link_name << " @ " << path;
Darin Petkov9893d9c2012-05-17 15:27:31 -070044 PropertyStore *store = mutable_store();
45 store->RegisterConstBool(flimflam::kScanningProperty, &scanning_);
Darin Petkovb72b62e2012-05-15 16:55:36 +020046}
Ben Chan99c8a4d2012-05-01 08:11:53 -070047
Darin Petkovb72b62e2012-05-15 16:55:36 +020048WiMax::~WiMax() {
Darin Petkovb96a4512012-06-04 11:02:49 +020049 LOG(INFO) << "WiMAX device destroyed: " << link_name();
Darin Petkovb72b62e2012-05-15 16:55:36 +020050}
Ben Chan99c8a4d2012-05-01 08:11:53 -070051
52void WiMax::Start(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkovb72b62e2012-05-15 16:55:36 +020053 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -070054 scanning_ = false;
Darin Petkovb72b62e2012-05-15 16:55:36 +020055 proxy_.reset(proxy_factory_->CreateWiMaxDeviceProxy(path_));
Darin Petkov9893d9c2012-05-17 15:27:31 -070056 proxy_->set_networks_changed_callback(
57 Bind(&WiMax::OnNetworksChanged, Unretained(this)));
Darin Petkov8ea0eaf2012-05-29 11:21:33 +020058 proxy_->set_status_changed_callback(
59 Bind(&WiMax::OnStatusChanged, Unretained(this)));
Darin Petkov912f0de2012-05-16 14:12:14 +020060 proxy_->Enable(
Darin Petkov3a4100c2012-06-14 11:36:59 +020061 error, Bind(&WiMax::OnEnableComplete, this, callback),
62 kDefaultRPCTimeoutSeconds * 1000);
Ben Chan99c8a4d2012-05-01 08:11:53 -070063}
64
65void WiMax::Stop(Error *error, const EnabledStateChangedCallback &callback) {
Darin Petkovb72b62e2012-05-15 16:55:36 +020066 SLOG(WiMax, 2) << __func__;
Darin Petkov3a4100c2012-06-14 11:36:59 +020067 StopConnectTimeout();
68 if (pending_service_) {
69 pending_service_->SetState(Service::kStateIdle);
70 pending_service_ = NULL;
71 }
Darin Petkovc63dcf02012-05-24 11:51:43 +020072 if (selected_service()) {
73 Error error;
74 DisconnectFrom(selected_service(), &error);
75 }
Darin Petkovb96a4512012-06-04 11:02:49 +020076 scanning_ = false;
Darin Petkovd1cd7972012-05-22 15:26:15 +020077 networks_.clear();
Darin Petkovc63dcf02012-05-24 11:51:43 +020078 manager()->wimax_provider()->OnNetworksChanged();
Darin Petkovb96a4512012-06-04 11:02:49 +020079 if (proxy_.get()) {
80 proxy_->Disable(
81 error, Bind(&WiMax::OnDisableComplete, this, callback),
Darin Petkov3a4100c2012-06-14 11:36:59 +020082 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkovb96a4512012-06-04 11:02:49 +020083 } else {
84 OnDisableComplete(callback, Error());
85 }
Ben Chan99c8a4d2012-05-01 08:11:53 -070086}
87
Wade Guthrie4823f4f2013-07-25 10:03:03 -070088void WiMax::Scan(ScanType /*scan_type*/, Error *error,
89 const string &/*reason*/) {
Darin Petkov912f0de2012-05-16 14:12:14 +020090 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -070091 if (scanning_) {
92 Error::PopulateAndLog(
93 error, Error::kInProgress, "Scan already in progress.");
94 return;
95 }
96 scanning_ = true;
97 proxy_->ScanNetworks(
Darin Petkov3a4100c2012-06-14 11:36:59 +020098 error, Bind(&WiMax::OnScanNetworksComplete, this),
99 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700100 if (error->IsFailure()) {
101 OnScanNetworksComplete(*error);
102 }
Ben Chan99c8a4d2012-05-01 08:11:53 -0700103}
104
Darin Petkov9893d9c2012-05-17 15:27:31 -0700105void WiMax::ConnectTo(const WiMaxServiceRefPtr &service, Error *error) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200106 SLOG(WiMax, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700107 if (pending_service_) {
108 Error::PopulateAndLog(
109 error, Error::kInProgress,
110 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100111 "Pending connect to service %s, ignoring connect request to %s.",
112 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200113 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700114 return;
115 }
116 service->SetState(Service::kStateAssociating);
117 pending_service_ = service;
Ben Chan4e5c1312012-05-18 18:45:38 -0700118
Darin Petkov3a4100c2012-06-14 11:36:59 +0200119 // We use the RPC device status to determine the outcome of the connect
120 // operation by listening for status updates in OnStatusChanged. A transition
121 // to Connected means success. A transition to Connecting and then to a status
122 // different than Connected means failure. Also, schedule a connect timeout to
123 // guard against the RPC device never transitioning to a Connecting or a
124 // Connected state.
125 status_ = wimax_manager::kDeviceStatusUninitialized;
126 StartConnectTimeout();
127
Darin Petkov25665aa2012-05-21 14:08:12 +0200128 KeyValueStore parameters;
Ben Chan4e5c1312012-05-18 18:45:38 -0700129 service->GetConnectParameters(&parameters);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700130 proxy_->Connect(
Ben Chan4e5c1312012-05-18 18:45:38 -0700131 service->GetNetworkObjectPath(), parameters,
Darin Petkov3a4100c2012-06-14 11:36:59 +0200132 error, Bind(&WiMax::OnConnectComplete, this),
133 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700134 if (error->IsFailure()) {
135 OnConnectComplete(*error);
136 }
137}
138
Darin Petkovc63dcf02012-05-24 11:51:43 +0200139void WiMax::DisconnectFrom(const ServiceRefPtr &service, Error *error) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200140 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700141 if (pending_service_) {
142 Error::PopulateAndLog(
143 error, Error::kInProgress,
144 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100145 "Pending connect to service %s, "
146 "ignoring disconnect request from %s.",
147 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200148 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700149 return;
150 }
151 if (selected_service() && service != selected_service()) {
152 Error::PopulateAndLog(
153 error, Error::kNotConnected,
154 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100155 "Current service is %s, ignoring disconnect request from %s.",
156 selected_service()->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200157 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700158 return;
159 }
Darin Petkovc63dcf02012-05-24 11:51:43 +0200160 DropConnection();
Darin Petkov912f0de2012-05-16 14:12:14 +0200161 proxy_->Disconnect(
Darin Petkov3a4100c2012-06-14 11:36:59 +0200162 error, Bind(&WiMax::OnDisconnectComplete, this),
163 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700164 if (error->IsFailure()) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200165 OnDisconnectComplete(*error);
166 }
167}
168
Darin Petkov6b9b2e12012-07-10 15:51:42 +0200169bool WiMax::IsIdle() const {
170 return !pending_service_ && !selected_service();
171}
172
Darin Petkovc63dcf02012-05-24 11:51:43 +0200173void WiMax::OnServiceStopped(const WiMaxServiceRefPtr &service) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200174 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200175 if (service == selected_service()) {
176 DropConnection();
177 }
Darin Petkovc1e52732012-05-25 15:23:45 +0200178 if (service == pending_service_) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200179 pending_service_ = NULL;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700180 }
181}
182
Darin Petkovb96a4512012-06-04 11:02:49 +0200183void WiMax::OnDeviceVanished() {
184 LOG(INFO) << "WiMAX device vanished: " << link_name();
185 proxy_.reset();
186 DropService(Service::kStateIdle);
187 // Disable the device. This will also clear any relevant properties such as
188 // the live network set.
189 SetEnabled(false);
190}
191
Darin Petkov9893d9c2012-05-17 15:27:31 -0700192void WiMax::OnScanNetworksComplete(const Error &/*error*/) {
193 SLOG(WiMax, 2) << __func__;
194 scanning_ = false;
195 // The networks are updated when the NetworksChanged signal is received.
Darin Petkov912f0de2012-05-16 14:12:14 +0200196}
197
198void WiMax::OnConnectComplete(const Error &error) {
199 SLOG(WiMax, 2) << __func__;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200200 if (error.IsSuccess()) {
201 // Nothing to do -- the connection process is resumed on the StatusChanged
202 // signal.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700203 return;
204 }
Darin Petkovb96a4512012-06-04 11:02:49 +0200205 DropService(Service::kStateFailure);
Darin Petkov912f0de2012-05-16 14:12:14 +0200206}
207
Darin Petkov9893d9c2012-05-17 15:27:31 -0700208void WiMax::OnDisconnectComplete(const Error &/*error*/) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200209 SLOG(WiMax, 2) << __func__;
Darin Petkov912f0de2012-05-16 14:12:14 +0200210}
211
212void WiMax::OnEnableComplete(const EnabledStateChangedCallback &callback,
213 const Error &error) {
214 SLOG(WiMax, 2) << __func__;
215 if (error.IsFailure()) {
216 proxy_.reset();
217 } else {
Darin Petkov59f2d692012-06-07 15:57:46 +0200218 LOG(INFO) << "WiMAX device " << link_name() << " enabled.";
219 // Updates the live networks based on the current WiMaxManager.Device
220 // networks. The RPC device will signal when the network set changes.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700221 Error e;
Darin Petkov59f2d692012-06-07 15:57:46 +0200222 OnNetworksChanged(proxy_->Networks(&e));
Darin Petkov912f0de2012-05-16 14:12:14 +0200223 }
224 callback.Run(error);
Darin Petkov912f0de2012-05-16 14:12:14 +0200225}
226
227void WiMax::OnDisableComplete(const EnabledStateChangedCallback &callback,
228 const Error &error) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200229 LOG(INFO) << "WiMAX device " << link_name() << " disabled.";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700230 proxy_.reset();
Darin Petkov912f0de2012-05-16 14:12:14 +0200231 callback.Run(error);
Ben Chan99c8a4d2012-05-01 08:11:53 -0700232}
233
Darin Petkov9893d9c2012-05-17 15:27:31 -0700234void WiMax::OnNetworksChanged(const RpcIdentifiers &networks) {
235 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200236 networks_.clear();
237 networks_.insert(networks.begin(), networks.end());
238 manager()->wimax_provider()->OnNetworksChanged();
Darin Petkovd1cd7972012-05-22 15:26:15 +0200239}
240
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200241void WiMax::OnStatusChanged(wimax_manager::DeviceStatus status) {
Darin Petkov3a4100c2012-06-14 11:36:59 +0200242 SLOG(WiMax, 2) << "WiMAX device " << link_name() << " status: " << status;
243 wimax_manager::DeviceStatus old_status = status_;
244 status_ = status;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200245 switch (status) {
246 case wimax_manager::kDeviceStatusConnected:
247 if (!pending_service_) {
248 LOG(WARNING) << "Unexpected status change; ignored.";
249 return;
250 }
Darin Petkov3a4100c2012-06-14 11:36:59 +0200251 // Stops the connect timeout -- the DHCP provider has a separate timeout.
252 StopConnectTimeout();
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200253 if (AcquireIPConfig()) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200254 LOG(INFO) << "WiMAX device " << link_name() << " connected to "
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200255 << pending_service_->GetStorageIdentifier();
256 SelectService(pending_service_);
Darin Petkovb96a4512012-06-04 11:02:49 +0200257 pending_service_ = NULL;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200258 SetServiceState(Service::kStateConfiguring);
259 } else {
Darin Petkovb96a4512012-06-04 11:02:49 +0200260 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200261 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200262 break;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200263 case wimax_manager::kDeviceStatusConnecting:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200264 LOG(INFO) << "WiMAX device " << link_name() << " connecting...";
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200265 // Nothing to do.
266 break;
267 default:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200268 // We may receive a queued up status update (e.g., to Scanning) before
269 // receiving the status update to Connecting, so be careful to fail the
270 // service only on the right status transition.
271 if (old_status == wimax_manager::kDeviceStatusConnecting ||
272 old_status == wimax_manager::kDeviceStatusConnected) {
273 LOG(INFO) << "WiMAX device " << link_name()
274 << " status: " << old_status << " -> " << status;
275 if (pending_service_) {
276 // For now, assume that failing to connect to a live network indicates
277 // bad user credentials. Reset the password to trigger the
278 // user/password dialog in the UI.
279 pending_service_->ClearPassphrase();
280 }
281 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200282 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200283 break;
284 }
285}
286
Darin Petkovb96a4512012-06-04 11:02:49 +0200287void WiMax::DropService(Service::ConnectState state) {
288 SLOG(WiMax, 2) << __func__
289 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov3a4100c2012-06-14 11:36:59 +0200290 StopConnectTimeout();
Darin Petkovb96a4512012-06-04 11:02:49 +0200291 if (pending_service_) {
292 LOG(WARNING) << "Unable to initiate connection to: "
293 << pending_service_->GetStorageIdentifier();
294 pending_service_->SetState(state);
295 pending_service_ = NULL;
296 }
297 if (selected_service()) {
298 LOG(WARNING) << "Service disconnected: "
299 << selected_service()->GetStorageIdentifier();
300 selected_service()->SetState(state);
301 DropConnection();
302 }
303}
304
Darin Petkov3a4100c2012-06-14 11:36:59 +0200305void WiMax::StartConnectTimeout() {
306 SLOG(WiMax, 2) << __func__;
307 if (IsConnectTimeoutStarted()) {
308 return;
309 }
310 connect_timeout_callback_.Reset(
311 Bind(&WiMax::OnConnectTimeout, weak_ptr_factory_.GetWeakPtr()));
312 dispatcher()->PostDelayedTask(
313 connect_timeout_callback_.callback(), connect_timeout_seconds_ * 1000);
314}
315
316void WiMax::StopConnectTimeout() {
317 SLOG(WiMax, 2) << __func__;
318 connect_timeout_callback_.Cancel();
319}
320
321bool WiMax::IsConnectTimeoutStarted() const {
322 return !connect_timeout_callback_.IsCancelled();
323}
324
325void WiMax::OnConnectTimeout() {
326 LOG(ERROR) << "WiMAX device " << link_name() << ": connect timeout.";
327 StopConnectTimeout();
328 DropService(Service::kStateFailure);
329}
330
Ben Chan99c8a4d2012-05-01 08:11:53 -0700331} // namespace shill