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; 40 static string ObjectID(WiMax* w) { return w->GetRpcIdentifier(); } 41 } 42 43 namespace { 44 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 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 89 WiMax::~WiMax() { 90 LOG(INFO) << "WiMAX device destroyed: " << link_name(); 91 } 92 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 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 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 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 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 210 bool WiMax::IsIdle() const { 211 return !pending_service_ && !selected_service(); 212 } 213 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 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 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 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 249 void WiMax::OnDisconnectComplete(const Error& /*error*/) { 250 SLOG(this, 2) << __func__; 251 } 252 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 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 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 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 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 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 356 void WiMax::StopConnectTimeout() { 357 SLOG(this, 2) << __func__; 358 connect_timeout_callback_.Cancel(); 359 } 360 361 bool WiMax::IsConnectTimeoutStarted() const { 362 return !connect_timeout_callback_.IsCancelled(); 363 } 364 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