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 32using base::Bind; 33using std::set; 34using std::string; 35 36namespace shill { 37 38namespace Logging { 39static auto kModuleLogScope = ScopeLogger::kWiMax; 40static string ObjectID(WiMax* w) { return w->GetRpcIdentifier(); } 41} 42 43namespace { 44 45const 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 66const int WiMax::kDefaultConnectTimeoutSeconds = 60; 67const int WiMax::kDefaultRPCTimeoutSeconds = 30; 68 69WiMax::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 89WiMax::~WiMax() { 90 LOG(INFO) << "WiMAX device destroyed: " << link_name(); 91} 92 93void 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 106void 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 129void 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 146void 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 180void 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 210bool WiMax::IsIdle() const { 211 return !pending_service_ && !selected_service(); 212} 213 214void 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 224void 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 233void 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 239void 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 249void WiMax::OnDisconnectComplete(const Error& /*error*/) { 250 SLOG(this, 2) << __func__; 251} 252 253void 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 268void 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 275void 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 282void 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 327void 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 345void 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 356void WiMax::StopConnectTimeout() { 357 SLOG(this, 2) << __func__; 358 connect_timeout_callback_.Cancel(); 359} 360 361bool WiMax::IsConnectTimeoutStarted() const { 362 return !connect_timeout_callback_.IsCancelled(); 363} 364 365void WiMax::OnConnectTimeout() { 366 LOG(ERROR) << "WiMAX device " << link_name() << ": connect timeout."; 367 StopConnectTimeout(); 368 DropService(Service::kStateFailure); 369} 370 371} // namespace shill 372