blob: b4b2b5ba3e8b0c8fe2994246b52859f7928249be [file] [log] [blame]
// Copyright (c) 2013 The Chromium 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 "chromeos/network/network_connect.h"
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/values.h"
#include "chromeos/login/login_state.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_activation_handler.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_handler_callbacks.h"
#include "chromeos/network/network_profile.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
void IgnoreDisconnectError(const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {}
// Returns true for carriers that can be activated through Shill instead of
// through a WebUI dialog.
bool IsDirectActivatedCarrier(const std::string& carrier) {
if (carrier == shill::kCarrierSprint)
return true;
return false;
}
const NetworkState* GetNetworkStateFromId(const std::string& network_id) {
// Note: network_id === NetworkState::guid.
return NetworkHandler::Get()
->network_state_handler()
->GetNetworkStateFromGuid(network_id);
}
class NetworkConnectImpl : public NetworkConnect {
public:
explicit NetworkConnectImpl(Delegate* delegate);
~NetworkConnectImpl() override;
// NetworkConnect
void ConnectToNetworkId(const std::string& network_id) override;
void DisconnectFromNetworkId(const std::string& network_id) override;
bool MaybeShowConfigureUI(const std::string& network_id,
const std::string& connect_error) override;
void SetTechnologyEnabled(const NetworkTypePattern& technology,
bool enabled_state) override;
void ShowMobileSetup(const std::string& network_id) override;
void ConfigureNetworkIdAndConnect(
const std::string& network_id,
const base::DictionaryValue& shill_properties,
bool shared) override;
void CreateConfigurationAndConnect(base::DictionaryValue* shill_properties,
bool shared) override;
void CreateConfiguration(base::DictionaryValue* shill_properties,
bool shared) override;
private:
void ActivateCellular(const std::string& network_id);
void HandleUnconfiguredNetwork(const std::string& network_id);
void OnConnectFailed(const std::string& network_id,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data);
bool MaybeShowConfigureUIImpl(const std::string& network_id,
const std::string& connect_error);
bool GetNetworkProfilePath(bool shared, std::string* profile_path);
void OnConnectSucceeded(const std::string& network_id);
void CallConnectToNetwork(const std::string& network_id,
bool check_error_state);
void OnActivateFailed(const std::string& network_id,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data);
void OnActivateSucceeded(const std::string& network_id);
void OnConfigureFailed(const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data);
void OnConfigureSucceeded(bool connect_on_configure,
const std::string& service_path,
const std::string& network_id);
void CallCreateConfiguration(base::DictionaryValue* properties,
bool shared,
bool connect_on_configure);
void SetPropertiesFailed(const std::string& desc,
const std::string& network_id,
const std::string& config_error_name,
std::unique_ptr<base::DictionaryValue> error_data);
void SetPropertiesToClear(base::DictionaryValue* properties_to_set,
std::vector<std::string>* properties_to_clear);
void ClearPropertiesAndConnect(
const std::string& network_id,
const std::vector<std::string>& properties_to_clear);
void ConfigureSetProfileSucceeded(
const std::string& network_id,
std::unique_ptr<base::DictionaryValue> properties_to_set);
Delegate* delegate_;
base::WeakPtrFactory<NetworkConnectImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(NetworkConnectImpl);
};
NetworkConnectImpl::NetworkConnectImpl(Delegate* delegate)
: delegate_(delegate), weak_factory_(this) {}
NetworkConnectImpl::~NetworkConnectImpl() {}
void NetworkConnectImpl::HandleUnconfiguredNetwork(
const std::string& network_id) {
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network) {
NET_LOG_ERROR("Configuring unknown network", network_id);
return;
}
if (network->type() == shill::kTypeWifi) {
// Only show the config view for secure networks, otherwise do nothing.
if (network->security_class() != shill::kSecurityNone) {
delegate_->ShowNetworkConfigure(network_id);
}
return;
}
if (network->type() == shill::kTypeWimax) {
delegate_->ShowNetworkConfigure(network_id);
return;
}
if (network->type() == shill::kTypeVPN) {
// Third-party VPNs handle configuration UI themselves.
if (network->vpn_provider_type() != shill::kProviderThirdPartyVpn)
delegate_->ShowNetworkConfigure(network_id);
return;
}
if (network->type() == shill::kTypeCellular) {
if (network->RequiresActivation()) {
ActivateCellular(network_id);
return;
}
if (network->cellular_out_of_credits()) {
ShowMobileSetup(network_id);
return;
}
// No special configure or setup for |network|, show the settings UI.
if (LoginState::Get()->IsUserLoggedIn())
delegate_->ShowNetworkSettings(network_id);
return;
}
NOTREACHED();
}
// If |shared| is true, sets |profile_path| to the shared profile path.
// Otherwise sets |profile_path| to the user profile path if authenticated and
// available. Returns 'false' if unable to set |profile_path|.
bool NetworkConnectImpl::GetNetworkProfilePath(bool shared,
std::string* profile_path) {
if (shared) {
*profile_path = NetworkProfileHandler::GetSharedProfilePath();
return true;
}
if (!LoginState::Get()->UserHasNetworkProfile()) {
NET_LOG_ERROR("User profile specified before login", "");
return false;
}
const NetworkProfile* profile =
NetworkHandler::Get()->network_profile_handler()->GetDefaultUserProfile();
if (!profile) {
NET_LOG_ERROR("No user profile for unshared network configuration", "");
return false;
}
*profile_path = profile->path;
return true;
}
void NetworkConnectImpl::OnConnectFailed(
const std::string& network_id,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
MaybeShowConfigureUIImpl(network_id, error_name);
}
// This handles connect failures that are a direct result of a user initiated
// connect request and result in a new UI being shown. Note: notifications are
// handled by NetworkStateNotifier.
bool NetworkConnectImpl::MaybeShowConfigureUIImpl(
const std::string& network_id,
const std::string& connect_error) {
NET_LOG_ERROR("Connect Failed: " + connect_error, network_id);
if (connect_error == NetworkConnectionHandler::kErrorBadPassphrase ||
connect_error == NetworkConnectionHandler::kErrorPassphraseRequired ||
connect_error == NetworkConnectionHandler::kErrorConfigurationRequired ||
connect_error == NetworkConnectionHandler::kErrorAuthenticationRequired) {
HandleUnconfiguredNetwork(network_id);
return true;
}
if (connect_error == NetworkConnectionHandler::kErrorCertificateRequired) {
if (!delegate_->ShowEnrollNetwork(network_id))
HandleUnconfiguredNetwork(network_id);
return true;
}
// Only show a configure dialog if there was a ConnectFailed error. The dialog
// allows the user to request a new connect attempt or cancel. Note: a
// notification may also be displayed by NetworkStateNotifier in this case.
if (connect_error == NetworkConnectionHandler::kErrorConnectFailed) {
HandleUnconfiguredNetwork(network_id);
return true;
}
// Notifications for other connect failures are handled by
// NetworkStateNotifier, so no need to do anything else here.
return false;
}
void NetworkConnectImpl::OnConnectSucceeded(const std::string& network_id) {
NET_LOG_USER("Connect Succeeded", network_id);
}
// If |check_error_state| is true, error state for the network is checked,
// otherwise any current error state is ignored (e.g. for recently configured
// networks or repeat connect attempts).
void NetworkConnectImpl::CallConnectToNetwork(const std::string& network_id,
bool check_error_state) {
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network) {
OnConnectFailed(network_id, NetworkConnectionHandler::kErrorNotFound,
nullptr);
return;
}
NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork(
network->path(), base::Bind(&NetworkConnectImpl::OnConnectSucceeded,
weak_factory_.GetWeakPtr(), network_id),
base::Bind(&NetworkConnectImpl::OnConnectFailed,
weak_factory_.GetWeakPtr(), network_id),
check_error_state);
}
void NetworkConnectImpl::OnActivateFailed(
const std::string& network_id,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
NET_LOG_ERROR("Unable to activate network", network_id);
delegate_->ShowNetworkConnectError(
NetworkConnectionHandler::kErrorActivateFailed, network_id);
}
void NetworkConnectImpl::OnActivateSucceeded(const std::string& network_id) {
NET_LOG_USER("Activation Succeeded", network_id);
}
void NetworkConnectImpl::OnConfigureFailed(
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
NET_LOG_ERROR("Unable to configure network", "");
delegate_->ShowNetworkConnectError(
NetworkConnectionHandler::kErrorConfigureFailed, "");
}
void NetworkConnectImpl::OnConfigureSucceeded(bool connect_on_configure,
const std::string& service_path,
const std::string& network_id) {
NET_LOG_USER("Configure Succeeded", network_id);
if (!connect_on_configure)
return;
// After configuring a network, ignore any (possibly stale) error state.
const bool check_error_state = false;
CallConnectToNetwork(network_id, check_error_state);
}
void NetworkConnectImpl::CallCreateConfiguration(
base::DictionaryValue* shill_properties,
bool shared,
bool connect_on_configure) {
std::string profile_path;
if (!GetNetworkProfilePath(shared, &profile_path)) {
delegate_->ShowNetworkConnectError(
NetworkConnectionHandler::kErrorConfigureFailed, "");
return;
}
shill_properties->SetStringWithoutPathExpansion(shill::kProfileProperty,
profile_path);
NetworkHandler::Get()
->network_configuration_handler()
->CreateShillConfiguration(
*shill_properties, NetworkConfigurationObserver::SOURCE_USER_ACTION,
base::Bind(&NetworkConnectImpl::OnConfigureSucceeded,
weak_factory_.GetWeakPtr(), connect_on_configure),
base::Bind(&NetworkConnectImpl::OnConfigureFailed,
weak_factory_.GetWeakPtr()));
}
void NetworkConnectImpl::SetPropertiesFailed(
const std::string& desc,
const std::string& network_id,
const std::string& config_error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
NET_LOG_ERROR(desc + ": Failed: " + config_error_name, network_id);
delegate_->ShowNetworkConnectError(
NetworkConnectionHandler::kErrorConfigureFailed, network_id);
}
void NetworkConnectImpl::SetPropertiesToClear(
base::DictionaryValue* properties_to_set,
std::vector<std::string>* properties_to_clear) {
// Move empty string properties to properties_to_clear.
for (base::DictionaryValue::Iterator iter(*properties_to_set);
!iter.IsAtEnd(); iter.Advance()) {
std::string value_str;
if (iter.value().GetAsString(&value_str) && value_str.empty())
properties_to_clear->push_back(iter.key());
}
// Remove cleared properties from properties_to_set.
for (std::vector<std::string>::iterator iter = properties_to_clear->begin();
iter != properties_to_clear->end(); ++iter) {
properties_to_set->RemoveWithoutPathExpansion(*iter, NULL);
}
}
void NetworkConnectImpl::ClearPropertiesAndConnect(
const std::string& network_id,
const std::vector<std::string>& properties_to_clear) {
NET_LOG_USER("ClearPropertiesAndConnect", network_id);
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network) {
SetPropertiesFailed("ClearProperties", network_id,
NetworkConnectionHandler::kErrorNotFound, nullptr);
return;
}
// After configuring a network, ignore any (possibly stale) error state.
const bool check_error_state = false;
NetworkHandler::Get()->network_configuration_handler()->ClearShillProperties(
network->path(), properties_to_clear,
base::Bind(&NetworkConnectImpl::CallConnectToNetwork,
weak_factory_.GetWeakPtr(), network_id, check_error_state),
base::Bind(&NetworkConnectImpl::SetPropertiesFailed,
weak_factory_.GetWeakPtr(), "ClearProperties", network_id));
}
void NetworkConnectImpl::ConfigureSetProfileSucceeded(
const std::string& network_id,
std::unique_ptr<base::DictionaryValue> properties_to_set) {
std::vector<std::string> properties_to_clear;
SetPropertiesToClear(properties_to_set.get(), &properties_to_clear);
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network) {
SetPropertiesFailed("SetProperties", network_id,
NetworkConnectionHandler::kErrorNotFound, nullptr);
return;
}
NetworkHandler::Get()->network_configuration_handler()->SetShillProperties(
network->path(), *properties_to_set,
NetworkConfigurationObserver::SOURCE_USER_ACTION,
base::Bind(&NetworkConnectImpl::ClearPropertiesAndConnect,
weak_factory_.GetWeakPtr(), network_id, properties_to_clear),
base::Bind(&NetworkConnectImpl::SetPropertiesFailed,
weak_factory_.GetWeakPtr(), "SetProperties", network_id));
}
// Public methods
void NetworkConnectImpl::ConnectToNetworkId(const std::string& network_id) {
NET_LOG_USER("ConnectToNetwork", network_id);
const NetworkState* network = GetNetworkStateFromId(network_id);
if (network) {
if (!network->error().empty() && !network->security_class().empty()) {
NET_LOG_USER("Configure: " + network->error(), network_id);
// If the network is in an error state, show the configuration UI
// directly to avoid a spurious notification.
HandleUnconfiguredNetwork(network_id);
return;
} else if (network->RequiresActivation()) {
ActivateCellular(network_id);
return;
}
}
const bool check_error_state = true;
CallConnectToNetwork(network_id, check_error_state);
}
void NetworkConnectImpl::DisconnectFromNetworkId(
const std::string& network_id) {
NET_LOG_USER("DisconnectFromNetwork", network_id);
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network)
return;
NetworkHandler::Get()->network_connection_handler()->DisconnectNetwork(
network->path(), base::Bind(&base::DoNothing),
base::Bind(&IgnoreDisconnectError));
}
bool NetworkConnectImpl::MaybeShowConfigureUI(
const std::string& network_id,
const std::string& connect_error) {
return MaybeShowConfigureUIImpl(network_id, connect_error);
}
void NetworkConnectImpl::SetTechnologyEnabled(
const NetworkTypePattern& technology,
bool enabled_state) {
std::string log_string = base::StringPrintf(
"technology %s, target state: %s", technology.ToDebugString().c_str(),
(enabled_state ? "ENABLED" : "DISABLED"));
NET_LOG_USER("SetTechnologyEnabled", log_string);
NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
bool enabled = handler->IsTechnologyEnabled(technology);
if (enabled_state == enabled) {
NET_LOG_USER("Technology already in target state.", log_string);
return;
}
if (enabled) {
// User requested to disable the technology.
handler->SetTechnologyEnabled(technology, false,
network_handler::ErrorCallback());
return;
}
// If we're dealing with a mobile network, then handle SIM lock here.
// SIM locking only applies to cellular, so the code below won't execute
// if |technology| has been explicitly set to WiMAX.
if (technology.MatchesPattern(NetworkTypePattern::Mobile())) {
const DeviceState* mobile = handler->GetDeviceStateByType(technology);
if (!mobile) {
NET_LOG_ERROR("SetTechnologyEnabled with no device", log_string);
return;
}
// The following only applies to cellular.
if (mobile->type() == shill::kTypeCellular) {
if (mobile->IsSimAbsent()) {
// If this is true, then we have a cellular device with no SIM
// inserted. TODO(armansito): Chrome should display a notification here,
// prompting the user to insert a SIM card and restart the device to
// enable cellular. See crbug.com/125171.
NET_LOG_USER("Cannot enable cellular device without SIM.", log_string);
return;
}
if (!mobile->sim_lock_type().empty()) {
// A SIM has been inserted, but it is locked. Let the user unlock it
// via the dialog.
delegate_->ShowMobileSimDialog();
return;
}
}
}
handler->SetTechnologyEnabled(technology, true,
network_handler::ErrorCallback());
}
void NetworkConnectImpl::ActivateCellular(const std::string& network_id) {
NET_LOG_USER("ActivateCellular", network_id);
const NetworkState* cellular = GetNetworkStateFromId(network_id);
if (!cellular || cellular->type() != shill::kTypeCellular) {
NET_LOG_ERROR("ActivateCellular with no Service", network_id);
return;
}
const DeviceState* cellular_device =
NetworkHandler::Get()->network_state_handler()->GetDeviceState(
cellular->device_path());
if (!cellular_device) {
NET_LOG_ERROR("ActivateCellular with no Device", network_id);
return;
}
if (!IsDirectActivatedCarrier(cellular_device->carrier())) {
// For non direct activation, show the mobile setup dialog which can be
// used to activate the network.
ShowMobileSetup(network_id);
return;
}
if (cellular->activation_state() == shill::kActivationStateActivated) {
NET_LOG_ERROR("ActivateCellular for activated service", network_id);
return;
}
NetworkHandler::Get()->network_activation_handler()->Activate(
cellular->path(),
"", // carrier
base::Bind(&NetworkConnectImpl::OnActivateSucceeded,
weak_factory_.GetWeakPtr(), network_id),
base::Bind(&NetworkConnectImpl::OnActivateFailed,
weak_factory_.GetWeakPtr(), network_id));
}
void NetworkConnectImpl::ShowMobileSetup(const std::string& network_id) {
const NetworkState* cellular = GetNetworkStateFromId(network_id);
if (!cellular || cellular->type() != shill::kTypeCellular) {
NET_LOG_ERROR("ShowMobileSetup without Cellular network", network_id);
return;
}
if (cellular->activation_state() != shill::kActivationStateActivated &&
cellular->activation_type() == shill::kActivationTypeNonCellular &&
!NetworkHandler::Get()->network_state_handler()->DefaultNetwork()) {
delegate_->ShowMobileActivationError(network_id);
return;
}
delegate_->ShowMobileSetupDialog(network_id);
}
void NetworkConnectImpl::ConfigureNetworkIdAndConnect(
const std::string& network_id,
const base::DictionaryValue& properties,
bool shared) {
NET_LOG_USER("ConfigureNetworkIdAndConnect", network_id);
std::unique_ptr<base::DictionaryValue> properties_to_set(
properties.DeepCopy());
std::string profile_path;
if (!GetNetworkProfilePath(shared, &profile_path)) {
delegate_->ShowNetworkConnectError(
NetworkConnectionHandler::kErrorConfigureFailed, network_id);
return;
}
const NetworkState* network = GetNetworkStateFromId(network_id);
if (!network) {
delegate_->ShowNetworkConnectError(NetworkConnectionHandler::kErrorNotFound,
network_id);
return;
}
NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile(
network->path(), profile_path,
NetworkConfigurationObserver::SOURCE_USER_ACTION,
base::Bind(&NetworkConnectImpl::ConfigureSetProfileSucceeded,
weak_factory_.GetWeakPtr(), network_id,
base::Passed(&properties_to_set)),
base::Bind(&NetworkConnectImpl::SetPropertiesFailed,
weak_factory_.GetWeakPtr(), "SetProfile: " + profile_path,
network_id));
}
void NetworkConnectImpl::CreateConfigurationAndConnect(
base::DictionaryValue* properties,
bool shared) {
NET_LOG_USER("CreateConfigurationAndConnect", "");
CallCreateConfiguration(properties, shared, true /* connect_on_configure */);
}
void NetworkConnectImpl::CreateConfiguration(base::DictionaryValue* properties,
bool shared) {
NET_LOG_USER("CreateConfiguration", "");
CallCreateConfiguration(properties, shared, false /* connect_on_configure */);
}
} // namespace
static NetworkConnect* g_network_connect = NULL;
// static
void NetworkConnect::Initialize(Delegate* delegate) {
CHECK(g_network_connect == NULL);
g_network_connect = new NetworkConnectImpl(delegate);
}
// static
void NetworkConnect::Shutdown() {
CHECK(g_network_connect);
delete g_network_connect;
g_network_connect = NULL;
}
// static
NetworkConnect* NetworkConnect::Get() {
CHECK(g_network_connect);
return g_network_connect;
}
NetworkConnect::NetworkConnect() {}
NetworkConnect::~NetworkConnect() {}
} // namespace chromeos