1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "shill/wimax/wimax.h"
18
19 #include <string>
20
21 #include <base/bind.h>
22 #include <base/strings/string_util.h>
23 #include <base/strings/stringprintf.h>
24
25 #include "shill/control_interface.h"
26 #include "shill/key_value_store.h"
27 #include "shill/logging.h"
28 #include "shill/manager.h"
29 #include "shill/wimax/wimax_device_proxy_interface.h"
30 #include "shill/wimax/wimax_service.h"
31
32 using base::Bind;
33 using std::set;
34 using std::string;
35
36 namespace shill {
37
38 namespace Logging {
39 static auto kModuleLogScope = ScopeLogger::kWiMax;
ObjectID(WiMax * w)40 static string ObjectID(WiMax* w) { return w->GetRpcIdentifier(); }
41 }
42
43 namespace {
44
DeviceStatusToString(wimax_manager::DeviceStatus status)45 const char* DeviceStatusToString(wimax_manager::DeviceStatus status) {
46 switch (status) {
47 case wimax_manager::kDeviceStatusUninitialized:
48 return "Uninitialized";
49 case wimax_manager::kDeviceStatusDisabled:
50 return "Disabled";
51 case wimax_manager::kDeviceStatusReady:
52 return "Ready";
53 case wimax_manager::kDeviceStatusScanning:
54 return "Scanning";
55 case wimax_manager::kDeviceStatusConnecting:
56 return "Connecting";
57 case wimax_manager::kDeviceStatusConnected:
58 return "Connected";
59 default:
60 return "Unknown";
61 }
62 }
63
64 } // namespace
65
66 const int WiMax::kDefaultConnectTimeoutSeconds = 60;
67 const int WiMax::kDefaultRPCTimeoutSeconds = 30;
68
WiMax(ControlInterface * control,EventDispatcher * dispatcher,Metrics * metrics,Manager * manager,const string & link_name,const string & address,int interface_index,const RpcIdentifier & path)69 WiMax::WiMax(ControlInterface* control,
70 EventDispatcher* dispatcher,
71 Metrics* metrics,
72 Manager* manager,
73 const string& link_name,
74 const string& address,
75 int interface_index,
76 const RpcIdentifier& path)
77 : Device(control, dispatcher, metrics, manager, link_name, address,
78 interface_index, Technology::kWiMax),
79 path_(path),
80 weak_ptr_factory_(this),
81 scanning_(false),
82 status_(wimax_manager::kDeviceStatusUninitialized),
83 connect_timeout_seconds_(kDefaultConnectTimeoutSeconds) {
84 LOG(INFO) << "WiMAX device created: " << link_name << " @ " << path;
85 PropertyStore* store = mutable_store();
86 store->RegisterConstBool(kScanningProperty, &scanning_);
87 }
88
~WiMax()89 WiMax::~WiMax() {
90 LOG(INFO) << "WiMAX device destroyed: " << link_name();
91 }
92
Start(Error * error,const EnabledStateChangedCallback & callback)93 void WiMax::Start(Error* error, const EnabledStateChangedCallback& callback) {
94 SLOG(this, 2) << __func__;
95 scanning_ = false;
96 proxy_.reset(control_interface()->CreateWiMaxDeviceProxy(path_));
97 proxy_->set_networks_changed_callback(
98 Bind(&WiMax::OnNetworksChanged, Unretained(this)));
99 proxy_->set_status_changed_callback(
100 Bind(&WiMax::OnStatusChanged, Unretained(this)));
101 proxy_->Enable(
102 error, Bind(&WiMax::OnEnableComplete, this, callback),
103 kDefaultRPCTimeoutSeconds * 1000);
104 }
105
Stop(Error * error,const EnabledStateChangedCallback & callback)106 void WiMax::Stop(Error* error, const EnabledStateChangedCallback& callback) {
107 SLOG(this, 2) << __func__;
108 StopConnectTimeout();
109 if (pending_service_) {
110 pending_service_->SetState(Service::kStateIdle);
111 pending_service_ = nullptr;
112 }
113 if (selected_service()) {
114 Error error;
115 DisconnectFrom(selected_service(), &error);
116 }
117 scanning_ = false;
118 networks_.clear();
119 manager()->wimax_provider()->OnNetworksChanged();
120 if (proxy_.get()) {
121 proxy_->Disable(
122 error, Bind(&WiMax::OnDisableComplete, this, callback),
123 kDefaultRPCTimeoutSeconds * 1000);
124 } else {
125 OnDisableComplete(callback, Error());
126 }
127 }
128
Scan(ScanType,Error * error,const string &)129 void WiMax::Scan(ScanType /*scan_type*/, Error* error,
130 const string& /*reason*/) {
131 SLOG(this, 2) << __func__;
132 if (scanning_) {
133 Error::PopulateAndLog(
134 FROM_HERE, error, Error::kInProgress, "Scan already in progress.");
135 return;
136 }
137 scanning_ = true;
138 proxy_->ScanNetworks(
139 error, Bind(&WiMax::OnScanNetworksComplete, this),
140 kDefaultRPCTimeoutSeconds * 1000);
141 if (error->IsFailure()) {
142 OnScanNetworksComplete(*error);
143 }
144 }
145
ConnectTo(const WiMaxServiceRefPtr & service,Error * error)146 void WiMax::ConnectTo(const WiMaxServiceRefPtr& service, Error* error) {
147 SLOG(this, 2) << __func__ << "(" << service->GetStorageIdentifier() << ")";
148 if (pending_service_) {
149 Error::PopulateAndLog(
150 FROM_HERE, error, Error::kInProgress,
151 base::StringPrintf(
152 "Pending connect to service %s, ignoring connect request to %s.",
153 pending_service_->unique_name().c_str(),
154 service->GetStorageIdentifier().c_str()));
155 return;
156 }
157 service->SetState(Service::kStateAssociating);
158 pending_service_ = service;
159
160 // We use the RPC device status to determine the outcome of the connect
161 // operation by listening for status updates in OnStatusChanged. A transition
162 // to Connected means success. A transition to Connecting and then to a status
163 // different than Connected means failure. Also, schedule a connect timeout to
164 // guard against the RPC device never transitioning to a Connecting or a
165 // Connected state.
166 status_ = wimax_manager::kDeviceStatusUninitialized;
167 StartConnectTimeout();
168
169 KeyValueStore parameters;
170 service->GetConnectParameters(¶meters);
171 proxy_->Connect(
172 service->GetNetworkObjectPath(), parameters,
173 error, Bind(&WiMax::OnConnectComplete, this),
174 kDefaultRPCTimeoutSeconds * 1000);
175 if (error->IsFailure()) {
176 OnConnectComplete(*error);
177 }
178 }
179
DisconnectFrom(const ServiceRefPtr & service,Error * error)180 void WiMax::DisconnectFrom(const ServiceRefPtr& service, Error* error) {
181 SLOG(this, 2) << __func__;
182 if (pending_service_) {
183 Error::PopulateAndLog(
184 FROM_HERE, error, Error::kInProgress,
185 base::StringPrintf(
186 "Pending connect to service %s, "
187 "ignoring disconnect request from %s.",
188 pending_service_->unique_name().c_str(),
189 service->GetStorageIdentifier().c_str()));
190 return;
191 }
192 if (selected_service() && service != selected_service()) {
193 Error::PopulateAndLog(
194 FROM_HERE, error, Error::kNotConnected,
195 base::StringPrintf(
196 "Current service is %s, ignoring disconnect request from %s.",
197 selected_service()->unique_name().c_str(),
198 service->GetStorageIdentifier().c_str()));
199 return;
200 }
201 DropConnection();
202 proxy_->Disconnect(
203 error, Bind(&WiMax::OnDisconnectComplete, this),
204 kDefaultRPCTimeoutSeconds * 1000);
205 if (error->IsFailure()) {
206 OnDisconnectComplete(*error);
207 }
208 }
209
IsIdle() const210 bool WiMax::IsIdle() const {
211 return !pending_service_ && !selected_service();
212 }
213
OnServiceStopped(const WiMaxServiceRefPtr & service)214 void WiMax::OnServiceStopped(const WiMaxServiceRefPtr& service) {
215 SLOG(this, 2) << __func__;
216 if (service == selected_service()) {
217 DropConnection();
218 }
219 if (service == pending_service_) {
220 pending_service_ = nullptr;
221 }
222 }
223
OnDeviceVanished()224 void WiMax::OnDeviceVanished() {
225 LOG(INFO) << "WiMAX device vanished: " << link_name();
226 proxy_.reset();
227 DropService(Service::kStateIdle);
228 // Disable the device. This will also clear any relevant properties such as
229 // the live network set.
230 SetEnabled(false);
231 }
232
OnScanNetworksComplete(const Error &)233 void WiMax::OnScanNetworksComplete(const Error& /*error*/) {
234 SLOG(this, 2) << __func__;
235 scanning_ = false;
236 // The networks are updated when the NetworksChanged signal is received.
237 }
238
OnConnectComplete(const Error & error)239 void WiMax::OnConnectComplete(const Error& error) {
240 SLOG(this, 2) << __func__;
241 if (error.IsSuccess()) {
242 // Nothing to do -- the connection process is resumed on the StatusChanged
243 // signal.
244 return;
245 }
246 DropService(Service::kStateFailure);
247 }
248
OnDisconnectComplete(const Error &)249 void WiMax::OnDisconnectComplete(const Error& /*error*/) {
250 SLOG(this, 2) << __func__;
251 }
252
OnEnableComplete(const EnabledStateChangedCallback & callback,const Error & error)253 void WiMax::OnEnableComplete(const EnabledStateChangedCallback& callback,
254 const Error& error) {
255 SLOG(this, 2) << __func__;
256 if (error.IsFailure()) {
257 proxy_.reset();
258 } else {
259 LOG(INFO) << "WiMAX device " << link_name() << " enabled.";
260 // Updates the live networks based on the current WiMaxManager.Device
261 // networks. The RPC device will signal when the network set changes.
262 Error e;
263 OnNetworksChanged(proxy_->Networks(&e));
264 }
265 callback.Run(error);
266 }
267
OnDisableComplete(const EnabledStateChangedCallback & callback,const Error & error)268 void WiMax::OnDisableComplete(const EnabledStateChangedCallback& callback,
269 const Error& error) {
270 LOG(INFO) << "WiMAX device " << link_name() << " disabled.";
271 proxy_.reset();
272 callback.Run(error);
273 }
274
OnNetworksChanged(const RpcIdentifiers & networks)275 void WiMax::OnNetworksChanged(const RpcIdentifiers& networks) {
276 SLOG(this, 2) << __func__;
277 networks_.clear();
278 networks_.insert(networks.begin(), networks.end());
279 manager()->wimax_provider()->OnNetworksChanged();
280 }
281
OnStatusChanged(wimax_manager::DeviceStatus status)282 void WiMax::OnStatusChanged(wimax_manager::DeviceStatus status) {
283 SLOG(this, 2) << "WiMAX device " << link_name()
284 << " status: " << DeviceStatusToString(status);
285 wimax_manager::DeviceStatus old_status = status_;
286 status_ = status;
287 switch (status) {
288 case wimax_manager::kDeviceStatusConnected:
289 if (!pending_service_) {
290 LOG(WARNING) << "Unexpected status change; ignored.";
291 return;
292 }
293 // Stops the connect timeout -- the DHCP provider has a separate timeout.
294 StopConnectTimeout();
295 if (AcquireIPConfig()) {
296 LOG(INFO) << "WiMAX device " << link_name() << " connected to "
297 << pending_service_->GetStorageIdentifier();
298 SelectService(pending_service_);
299 pending_service_ = nullptr;
300 SetServiceState(Service::kStateConfiguring);
301 } else {
302 DropService(Service::kStateFailure);
303 }
304 break;
305 case wimax_manager::kDeviceStatusConnecting:
306 LOG(INFO) << "WiMAX device " << link_name() << " connecting...";
307 // Nothing to do.
308 break;
309 default:
310 // We may receive a queued up status update (e.g., to Scanning) before
311 // receiving the status update to Connecting, so be careful to fail the
312 // service only on the right status transition.
313 if (old_status == wimax_manager::kDeviceStatusConnecting ||
314 old_status == wimax_manager::kDeviceStatusConnected) {
315 LOG(INFO) << "WiMAX device " << link_name()
316 << " status: " << DeviceStatusToString(old_status)
317 << " -> " << DeviceStatusToString(status);
318 // TODO(benchan): Investigate a method to determine if the connection
319 // failure is due to incorrect EAP credentials and indicate that via
320 // Service::kFailureBadPassphrase (crosbug.com/p/16324).
321 DropService(Service::kStateFailure);
322 }
323 break;
324 }
325 }
326
DropService(Service::ConnectState state)327 void WiMax::DropService(Service::ConnectState state) {
328 SLOG(this, 2) << __func__
329 << "(" << Service::ConnectStateToString(state) << ")";
330 StopConnectTimeout();
331 if (pending_service_) {
332 LOG(WARNING) << "Unable to initiate connection to: "
333 << pending_service_->GetStorageIdentifier();
334 pending_service_->SetState(state);
335 pending_service_ = nullptr;
336 }
337 if (selected_service()) {
338 LOG(WARNING) << "Service disconnected: "
339 << selected_service()->GetStorageIdentifier();
340 selected_service()->SetState(state);
341 DropConnection();
342 }
343 }
344
StartConnectTimeout()345 void WiMax::StartConnectTimeout() {
346 SLOG(this, 2) << __func__;
347 if (IsConnectTimeoutStarted()) {
348 return;
349 }
350 connect_timeout_callback_.Reset(
351 Bind(&WiMax::OnConnectTimeout, weak_ptr_factory_.GetWeakPtr()));
352 dispatcher()->PostDelayedTask(
353 connect_timeout_callback_.callback(), connect_timeout_seconds_ * 1000);
354 }
355
StopConnectTimeout()356 void WiMax::StopConnectTimeout() {
357 SLOG(this, 2) << __func__;
358 connect_timeout_callback_.Cancel();
359 }
360
IsConnectTimeoutStarted() const361 bool WiMax::IsConnectTimeoutStarted() const {
362 return !connect_timeout_callback_.IsCancelled();
363 }
364
OnConnectTimeout()365 void WiMax::OnConnectTimeout() {
366 LOG(ERROR) << "WiMAX device " << link_name() << ": connect timeout.";
367 StopConnectTimeout();
368 DropService(Service::kStateFailure);
369 }
370
371 } // namespace shill
372