| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/wimax_service.h" |
| |
| #include <algorithm> |
| |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <chromeos/dbus/service_constants.h> |
| |
| #include "shill/dbus_adaptor.h" |
| #include "shill/eap_credentials.h" |
| #include "shill/key_value_store.h" |
| #include "shill/logging.h" |
| #include "shill/manager.h" |
| #include "shill/store_interface.h" |
| #include "shill/technology.h" |
| #include "shill/wimax.h" |
| |
| using std::replace_if; |
| using std::string; |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kWiMax; |
| static string ObjectID(WiMaxService *w) { return w->GetRpcIdentifier(); } |
| } |
| |
| const char WiMaxService::kStorageNetworkId[] = "NetworkId"; |
| const char WiMaxService::kNetworkIdProperty[] = "NetworkId"; |
| |
| WiMaxService::WiMaxService(ControlInterface *control, |
| EventDispatcher *dispatcher, |
| Metrics *metrics, |
| Manager *manager) |
| : Service(control, dispatcher, metrics, manager, Technology::kWiMax), |
| need_passphrase_(true), |
| is_default_(false) { |
| PropertyStore *store = this->mutable_store(); |
| // TODO(benchan): Support networks that require no user credentials or |
| // implicitly defined credentials. |
| store->RegisterBool(kPassphraseRequiredProperty, &need_passphrase_); |
| store->RegisterConstString(kNetworkIdProperty, &network_id_); |
| |
| SetEapCredentials(new EapCredentials()); |
| |
| IgnoreParameterForConfigure(kNetworkIdProperty); |
| |
| // Initialize a default storage identifier based on the service's unique |
| // name. The identifier most likely needs to be reinitialized by the caller |
| // when its components have been set. |
| InitStorageIdentifier(); |
| |
| // Now that |this| is a fully constructed WiMaxService, synchronize observers |
| // with our current state, and emit the appropriate change notifications. |
| // (Initial observer state may have been set in our base class.) |
| NotifyPropertyChanges(); |
| } |
| |
| WiMaxService::~WiMaxService() {} |
| |
| void WiMaxService::GetConnectParameters(KeyValueStore *parameters) const { |
| CHECK(parameters); |
| eap()->PopulateWiMaxProperties(parameters); |
| } |
| |
| RpcIdentifier WiMaxService::GetNetworkObjectPath() const { |
| CHECK(proxy_.get()); |
| return proxy_->path(); |
| } |
| |
| void WiMaxService::Stop() { |
| if (!IsStarted()) { |
| return; |
| } |
| LOG(INFO) << "Stopping WiMAX service: " << GetStorageIdentifier(); |
| proxy_.reset(); |
| SetStrength(0); |
| if (device_) { |
| device_->OnServiceStopped(this); |
| SetDevice(nullptr); |
| } |
| UpdateConnectable(); |
| NotifyPropertyChanges(); |
| } |
| |
| bool WiMaxService::Start(WiMaxNetworkProxyInterface *proxy) { |
| SLOG(this, 2) << __func__; |
| CHECK(proxy); |
| std::unique_ptr<WiMaxNetworkProxyInterface> local_proxy(proxy); |
| if (IsStarted()) { |
| return true; |
| } |
| if (friendly_name().empty()) { |
| LOG(ERROR) << "Empty service name."; |
| return false; |
| } |
| Error error; |
| network_name_ = proxy->Name(&error); |
| if (error.IsFailure()) { |
| return false; |
| } |
| uint32_t identifier = proxy->Identifier(&error); |
| if (error.IsFailure()) { |
| return false; |
| } |
| WiMaxNetworkId id = ConvertIdentifierToNetworkId(identifier); |
| if (id != network_id_) { |
| LOG(ERROR) << "Network identifiers don't match: " |
| << id << " != " << network_id_; |
| return false; |
| } |
| int signal_strength = proxy->SignalStrength(&error); |
| if (error.IsFailure()) { |
| return false; |
| } |
| SetStrength(signal_strength); |
| proxy->set_signal_strength_changed_callback( |
| Bind(&WiMaxService::OnSignalStrengthChanged, Unretained(this))); |
| proxy_.reset(local_proxy.release()); |
| UpdateConnectable(); |
| NotifyPropertyChanges(); |
| LOG(INFO) << "WiMAX service started: " << GetStorageIdentifier(); |
| return true; |
| } |
| |
| bool WiMaxService::IsStarted() const { |
| return proxy_.get(); |
| } |
| |
| void WiMaxService::Connect(Error *error, const char *reason) { |
| SLOG(this, 2) << __func__; |
| if (device_) { |
| // TODO(benchan): Populate error again after changing the way that |
| // Chrome handles Error::kAlreadyConnected situation. |
| LOG(WARNING) << "Service " << GetStorageIdentifier() |
| << " is already being connected or already connected."; |
| return; |
| } |
| if (!connectable()) { |
| LOG(ERROR) << "Can't connect. Service " << GetStorageIdentifier() |
| << " is not connectable."; |
| Error::PopulateAndLog(error, Error::kOperationFailed, |
| Error::GetDefaultMessage(Error::kOperationFailed)); |
| return; |
| } |
| WiMaxRefPtr carrier = manager()->wimax_provider()->SelectCarrier(this); |
| if (!carrier) { |
| Error::PopulateAndLog( |
| error, Error::kNoCarrier, "No suitable WiMAX device available."); |
| return; |
| } |
| Service::Connect(error, reason); |
| carrier->ConnectTo(this, error); |
| if (error->IsSuccess()) { |
| // Associate with the carrier device if the connection process has been |
| // initiated successfully. |
| SetDevice(carrier); |
| } |
| } |
| |
| void WiMaxService::Disconnect(Error *error, const char *reason) { |
| SLOG(this, 2) << __func__; |
| if (!device_) { |
| Error::PopulateAndLog( |
| error, Error::kNotConnected, "Not connected."); |
| return; |
| } |
| Service::Disconnect(error, reason); |
| device_->DisconnectFrom(this, error); |
| SetDevice(nullptr); |
| } |
| |
| string WiMaxService::GetStorageIdentifier() const { |
| return storage_id_; |
| } |
| |
| string WiMaxService::GetDeviceRpcId(Error *error) const { |
| if (!device_) { |
| error->Populate(Error::kNotFound, "Not associated with a device"); |
| return DBusAdaptor::kNullPath; |
| } |
| return device_->GetRpcIdentifier(); |
| } |
| |
| bool WiMaxService::IsAutoConnectable(const char **reason) const { |
| if (!Service::IsAutoConnectable(reason)) { |
| return false; |
| } |
| WiMaxRefPtr device = manager()->wimax_provider()->SelectCarrier(this); |
| DCHECK(device); |
| if (!device->IsIdle()) { |
| *reason = kAutoConnBusy; |
| return false; |
| } |
| return true; |
| } |
| |
| bool WiMaxService::Is8021x() const { |
| return true; |
| } |
| |
| bool WiMaxService::IsVisible() const { |
| // WiMAX services should be displayed only if they are in range (i.e. |
| // a corresponding network is exposed by WiMAX manager). |
| return IsStarted(); |
| } |
| |
| void WiMaxService::OnEapCredentialsChanged( |
| Service::UpdateCredentialsReason reason) { |
| need_passphrase_ = !eap()->IsConnectableUsingPassphrase(); |
| if (reason == Service::kReasonPropertyUpdate) |
| SetHasEverConnected(false); |
| UpdateConnectable(); |
| } |
| |
| void WiMaxService::UpdateConnectable() { |
| SLOG(this, 2) << __func__ << "(started: " << IsStarted() |
| << ", need passphrase: " << need_passphrase_ << ")"; |
| SetConnectableFull(IsStarted() && !need_passphrase_); |
| } |
| |
| void WiMaxService::OnSignalStrengthChanged(int strength) { |
| SLOG(this, 2) << __func__ << "(" << strength << ")"; |
| SetStrength(strength); |
| } |
| |
| void WiMaxService::SetDevice(WiMaxRefPtr new_device) { |
| if (device_ == new_device) |
| return; |
| if (new_device) { |
| adaptor()->EmitRpcIdentifierChanged(kDeviceProperty, |
| new_device->GetRpcIdentifier()); |
| } else { |
| adaptor()->EmitRpcIdentifierChanged(kDeviceProperty, |
| DBusAdaptor::kNullPath); |
| } |
| device_ = new_device; |
| } |
| |
| bool WiMaxService::Save(StoreInterface *storage) { |
| SLOG(this, 2) << __func__; |
| if (!Service::Save(storage)) { |
| return false; |
| } |
| const string id = GetStorageIdentifier(); |
| storage->SetString(id, kStorageNetworkId, network_id_); |
| |
| return true; |
| } |
| |
| bool WiMaxService::Unload() { |
| SLOG(this, 2) << __func__; |
| // The base method also disconnects the service. |
| Service::Unload(); |
| ClearPassphrase(); |
| // Notify the WiMAX provider that this service has been unloaded. If the |
| // provider releases ownership of this service, it needs to be deregistered. |
| return manager()->wimax_provider()->OnServiceUnloaded(this); |
| } |
| |
| void WiMaxService::SetState(ConnectState state) { |
| Service::SetState(state); |
| if (!IsConnecting() && !IsConnected()) { |
| // Disassociate from any carrier device if it's not connected anymore. |
| SetDevice(nullptr); |
| } |
| } |
| |
| // static |
| WiMaxNetworkId WiMaxService::ConvertIdentifierToNetworkId(uint32_t identifier) { |
| return base::StringPrintf("%08x", identifier); |
| } |
| |
| void WiMaxService::InitStorageIdentifier() { |
| storage_id_ = CreateStorageIdentifier(network_id_, friendly_name()); |
| } |
| |
| // static |
| string WiMaxService::CreateStorageIdentifier(const WiMaxNetworkId &id, |
| const string &name) { |
| string storage_id = |
| base::StringPrintf("%s_%s_%s", |
| kTypeWimax, name.c_str(), id.c_str()); |
| base::StringToLowerASCII(&storage_id); |
| replace_if(storage_id.begin(), storage_id.end(), &Service::IllegalChar, '_'); |
| return storage_id; |
| } |
| |
| void WiMaxService::ClearPassphrase() { |
| SLOG(this, 2) << __func__; |
| mutable_eap()->set_password(""); |
| OnEapCredentialsChanged(Service::kReasonPropertyUpdate); |
| } |
| |
| } // namespace shill |