blob: b9f10d8038e1d237011db77d0864e86f34993d2f [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 Guthrie68d41092013-04-02 12:56:02 -070088void WiMax::Scan(ScanType scan_type, Error *error) {
Darin Petkov912f0de2012-05-16 14:12:14 +020089 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -070090 if (scanning_) {
91 Error::PopulateAndLog(
92 error, Error::kInProgress, "Scan already in progress.");
93 return;
94 }
95 scanning_ = true;
96 proxy_->ScanNetworks(
Darin Petkov3a4100c2012-06-14 11:36:59 +020097 error, Bind(&WiMax::OnScanNetworksComplete, this),
98 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -070099 if (error->IsFailure()) {
100 OnScanNetworksComplete(*error);
101 }
Ben Chan99c8a4d2012-05-01 08:11:53 -0700102}
103
Darin Petkov9893d9c2012-05-17 15:27:31 -0700104void WiMax::ConnectTo(const WiMaxServiceRefPtr &service, Error *error) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200105 SLOG(WiMax, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700106 if (pending_service_) {
107 Error::PopulateAndLog(
108 error, Error::kInProgress,
109 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100110 "Pending connect to service %s, ignoring connect request to %s.",
111 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200112 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700113 return;
114 }
115 service->SetState(Service::kStateAssociating);
116 pending_service_ = service;
Ben Chan4e5c1312012-05-18 18:45:38 -0700117
Darin Petkov3a4100c2012-06-14 11:36:59 +0200118 // We use the RPC device status to determine the outcome of the connect
119 // operation by listening for status updates in OnStatusChanged. A transition
120 // to Connected means success. A transition to Connecting and then to a status
121 // different than Connected means failure. Also, schedule a connect timeout to
122 // guard against the RPC device never transitioning to a Connecting or a
123 // Connected state.
124 status_ = wimax_manager::kDeviceStatusUninitialized;
125 StartConnectTimeout();
126
Darin Petkov25665aa2012-05-21 14:08:12 +0200127 KeyValueStore parameters;
Ben Chan4e5c1312012-05-18 18:45:38 -0700128 service->GetConnectParameters(&parameters);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700129 proxy_->Connect(
Ben Chan4e5c1312012-05-18 18:45:38 -0700130 service->GetNetworkObjectPath(), parameters,
Darin Petkov3a4100c2012-06-14 11:36:59 +0200131 error, Bind(&WiMax::OnConnectComplete, this),
132 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700133 if (error->IsFailure()) {
134 OnConnectComplete(*error);
135 }
136}
137
Darin Petkovc63dcf02012-05-24 11:51:43 +0200138void WiMax::DisconnectFrom(const ServiceRefPtr &service, Error *error) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200139 SLOG(WiMax, 2) << __func__;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700140 if (pending_service_) {
141 Error::PopulateAndLog(
142 error, Error::kInProgress,
143 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100144 "Pending connect to service %s, "
145 "ignoring disconnect request from %s.",
146 pending_service_->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200147 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700148 return;
149 }
150 if (selected_service() && service != selected_service()) {
151 Error::PopulateAndLog(
152 error, Error::kNotConnected,
153 base::StringPrintf(
Darin Petkov457728b2013-01-09 09:49:08 +0100154 "Current service is %s, ignoring disconnect request from %s.",
155 selected_service()->unique_name().c_str(),
Darin Petkovc63dcf02012-05-24 11:51:43 +0200156 service->GetStorageIdentifier().c_str()));
Darin Petkov9893d9c2012-05-17 15:27:31 -0700157 return;
158 }
Darin Petkovc63dcf02012-05-24 11:51:43 +0200159 DropConnection();
Darin Petkov912f0de2012-05-16 14:12:14 +0200160 proxy_->Disconnect(
Darin Petkov3a4100c2012-06-14 11:36:59 +0200161 error, Bind(&WiMax::OnDisconnectComplete, this),
162 kDefaultRPCTimeoutSeconds * 1000);
Darin Petkov9893d9c2012-05-17 15:27:31 -0700163 if (error->IsFailure()) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200164 OnDisconnectComplete(*error);
165 }
166}
167
Darin Petkov6b9b2e12012-07-10 15:51:42 +0200168bool WiMax::IsIdle() const {
169 return !pending_service_ && !selected_service();
170}
171
Darin Petkovc63dcf02012-05-24 11:51:43 +0200172void WiMax::OnServiceStopped(const WiMaxServiceRefPtr &service) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200173 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200174 if (service == selected_service()) {
175 DropConnection();
176 }
Darin Petkovc1e52732012-05-25 15:23:45 +0200177 if (service == pending_service_) {
Darin Petkovc63dcf02012-05-24 11:51:43 +0200178 pending_service_ = NULL;
Darin Petkov9893d9c2012-05-17 15:27:31 -0700179 }
180}
181
Darin Petkovb96a4512012-06-04 11:02:49 +0200182void WiMax::OnDeviceVanished() {
183 LOG(INFO) << "WiMAX device vanished: " << link_name();
184 proxy_.reset();
185 DropService(Service::kStateIdle);
186 // Disable the device. This will also clear any relevant properties such as
187 // the live network set.
188 SetEnabled(false);
189}
190
Darin Petkov9893d9c2012-05-17 15:27:31 -0700191void WiMax::OnScanNetworksComplete(const Error &/*error*/) {
192 SLOG(WiMax, 2) << __func__;
193 scanning_ = false;
194 // The networks are updated when the NetworksChanged signal is received.
Darin Petkov912f0de2012-05-16 14:12:14 +0200195}
196
197void WiMax::OnConnectComplete(const Error &error) {
198 SLOG(WiMax, 2) << __func__;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200199 if (error.IsSuccess()) {
200 // Nothing to do -- the connection process is resumed on the StatusChanged
201 // signal.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700202 return;
203 }
Darin Petkovb96a4512012-06-04 11:02:49 +0200204 DropService(Service::kStateFailure);
Darin Petkov912f0de2012-05-16 14:12:14 +0200205}
206
Darin Petkov9893d9c2012-05-17 15:27:31 -0700207void WiMax::OnDisconnectComplete(const Error &/*error*/) {
Darin Petkov912f0de2012-05-16 14:12:14 +0200208 SLOG(WiMax, 2) << __func__;
Darin Petkov912f0de2012-05-16 14:12:14 +0200209}
210
211void WiMax::OnEnableComplete(const EnabledStateChangedCallback &callback,
212 const Error &error) {
213 SLOG(WiMax, 2) << __func__;
214 if (error.IsFailure()) {
215 proxy_.reset();
216 } else {
Darin Petkov59f2d692012-06-07 15:57:46 +0200217 LOG(INFO) << "WiMAX device " << link_name() << " enabled.";
218 // Updates the live networks based on the current WiMaxManager.Device
219 // networks. The RPC device will signal when the network set changes.
Darin Petkov9893d9c2012-05-17 15:27:31 -0700220 Error e;
Darin Petkov59f2d692012-06-07 15:57:46 +0200221 OnNetworksChanged(proxy_->Networks(&e));
Darin Petkov912f0de2012-05-16 14:12:14 +0200222 }
223 callback.Run(error);
Darin Petkov912f0de2012-05-16 14:12:14 +0200224}
225
226void WiMax::OnDisableComplete(const EnabledStateChangedCallback &callback,
227 const Error &error) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200228 LOG(INFO) << "WiMAX device " << link_name() << " disabled.";
Darin Petkov9893d9c2012-05-17 15:27:31 -0700229 proxy_.reset();
Darin Petkov912f0de2012-05-16 14:12:14 +0200230 callback.Run(error);
Ben Chan99c8a4d2012-05-01 08:11:53 -0700231}
232
Darin Petkov9893d9c2012-05-17 15:27:31 -0700233void WiMax::OnNetworksChanged(const RpcIdentifiers &networks) {
234 SLOG(WiMax, 2) << __func__;
Darin Petkovc63dcf02012-05-24 11:51:43 +0200235 networks_.clear();
236 networks_.insert(networks.begin(), networks.end());
237 manager()->wimax_provider()->OnNetworksChanged();
Darin Petkovd1cd7972012-05-22 15:26:15 +0200238}
239
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200240void WiMax::OnStatusChanged(wimax_manager::DeviceStatus status) {
Darin Petkov3a4100c2012-06-14 11:36:59 +0200241 SLOG(WiMax, 2) << "WiMAX device " << link_name() << " status: " << status;
242 wimax_manager::DeviceStatus old_status = status_;
243 status_ = status;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200244 switch (status) {
245 case wimax_manager::kDeviceStatusConnected:
246 if (!pending_service_) {
247 LOG(WARNING) << "Unexpected status change; ignored.";
248 return;
249 }
Darin Petkov3a4100c2012-06-14 11:36:59 +0200250 // Stops the connect timeout -- the DHCP provider has a separate timeout.
251 StopConnectTimeout();
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200252 if (AcquireIPConfig()) {
Darin Petkovb96a4512012-06-04 11:02:49 +0200253 LOG(INFO) << "WiMAX device " << link_name() << " connected to "
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200254 << pending_service_->GetStorageIdentifier();
255 SelectService(pending_service_);
Darin Petkovb96a4512012-06-04 11:02:49 +0200256 pending_service_ = NULL;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200257 SetServiceState(Service::kStateConfiguring);
258 } else {
Darin Petkovb96a4512012-06-04 11:02:49 +0200259 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200260 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200261 break;
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200262 case wimax_manager::kDeviceStatusConnecting:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200263 LOG(INFO) << "WiMAX device " << link_name() << " connecting...";
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200264 // Nothing to do.
265 break;
266 default:
Darin Petkov3a4100c2012-06-14 11:36:59 +0200267 // We may receive a queued up status update (e.g., to Scanning) before
268 // receiving the status update to Connecting, so be careful to fail the
269 // service only on the right status transition.
270 if (old_status == wimax_manager::kDeviceStatusConnecting ||
271 old_status == wimax_manager::kDeviceStatusConnected) {
272 LOG(INFO) << "WiMAX device " << link_name()
273 << " status: " << old_status << " -> " << status;
274 if (pending_service_) {
275 // For now, assume that failing to connect to a live network indicates
276 // bad user credentials. Reset the password to trigger the
277 // user/password dialog in the UI.
278 pending_service_->ClearPassphrase();
279 }
280 DropService(Service::kStateFailure);
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200281 }
Darin Petkov8ea0eaf2012-05-29 11:21:33 +0200282 break;
283 }
284}
285
Darin Petkovb96a4512012-06-04 11:02:49 +0200286void WiMax::DropService(Service::ConnectState state) {
287 SLOG(WiMax, 2) << __func__
288 << "(" << Service::ConnectStateToString(state) << ")";
Darin Petkov3a4100c2012-06-14 11:36:59 +0200289 StopConnectTimeout();
Darin Petkovb96a4512012-06-04 11:02:49 +0200290 if (pending_service_) {
291 LOG(WARNING) << "Unable to initiate connection to: "
292 << pending_service_->GetStorageIdentifier();
293 pending_service_->SetState(state);
294 pending_service_ = NULL;
295 }
296 if (selected_service()) {
297 LOG(WARNING) << "Service disconnected: "
298 << selected_service()->GetStorageIdentifier();
299 selected_service()->SetState(state);
300 DropConnection();
301 }
302}
303
Darin Petkov3a4100c2012-06-14 11:36:59 +0200304void WiMax::StartConnectTimeout() {
305 SLOG(WiMax, 2) << __func__;
306 if (IsConnectTimeoutStarted()) {
307 return;
308 }
309 connect_timeout_callback_.Reset(
310 Bind(&WiMax::OnConnectTimeout, weak_ptr_factory_.GetWeakPtr()));
311 dispatcher()->PostDelayedTask(
312 connect_timeout_callback_.callback(), connect_timeout_seconds_ * 1000);
313}
314
315void WiMax::StopConnectTimeout() {
316 SLOG(WiMax, 2) << __func__;
317 connect_timeout_callback_.Cancel();
318}
319
320bool WiMax::IsConnectTimeoutStarted() const {
321 return !connect_timeout_callback_.IsCancelled();
322}
323
324void WiMax::OnConnectTimeout() {
325 LOG(ERROR) << "WiMAX device " << link_name() << ": connect timeout.";
326 StopConnectTimeout();
327 DropService(Service::kStateFailure);
328}
329
Ben Chan99c8a4d2012-05-01 08:11:53 -0700330} // namespace shill