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(&parameters);
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