Home | History | Annotate | Download | only in wimax
      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(&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 
    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