// 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 "ash/system/chromeos/network/network_connect.h" #include "ash/session_state_delegate.h" #include "ash/shell.h" #include "ash/system/chromeos/network/network_state_notifier.h" #include "ash/system/system_notifier.h" #include "ash/system/tray/system_tray_delegate.h" #include "ash/system/tray/system_tray_notifier.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.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 "chromeos/network/shill_property_util.h" #include "grit/ash_resources.h" #include "grit/ash_strings.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/message_center/message_center.h" #include "ui/message_center/notification.h" using chromeos::DeviceState; using chromeos::NetworkConfigurationHandler; using chromeos::NetworkConnectionHandler; using chromeos::NetworkHandler; using chromeos::NetworkProfile; using chromeos::NetworkProfileHandler; using chromeos::NetworkState; using chromeos::NetworkStateHandler; using chromeos::NetworkTypePattern; namespace ash { namespace { // TODO(stevenjb): This should be in service_constants.h const char kErrorInProgress[] = "org.chromium.flimflam.Error.InProgress"; // 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; } void ShowErrorNotification(const std::string& error_name, const std::string& shill_error, const std::string& service_path) { Shell::GetInstance()->system_tray_notifier()->network_state_notifier()-> ShowNetworkConnectError(error_name, shill_error, service_path); } void HandleUnconfiguredNetwork(const std::string& service_path, gfx::NativeWindow parent_window) { const NetworkState* network = NetworkHandler::Get()->network_state_handler()-> GetNetworkState(service_path); if (!network) { NET_LOG_ERROR("Configuring unknown network", service_path); return; } if (network->type() == shill::kTypeWifi) { // Only show the config view for secure networks, otherwise do nothing. if (network->security() != shill::kSecurityNone) { ash::Shell::GetInstance()->system_tray_delegate()-> ShowNetworkConfigure(service_path, parent_window); } return; } if (network->type() == shill::kTypeWimax || network->type() == shill::kTypeVPN) { ash::Shell::GetInstance()->system_tray_delegate()-> ShowNetworkConfigure(service_path, parent_window); return; } if (network->type() == shill::kTypeCellular) { if (network->RequiresActivation()) { ash::network_connect::ActivateCellular(service_path); return; } if (network->cellular_out_of_credits()) { ash::network_connect::ShowMobileSetup(service_path); return; } // No special configure or setup for |network|, show the settings UI. if (chromeos::LoginState::Get()->IsUserLoggedIn()) { ash::Shell::GetInstance()->system_tray_delegate()-> ShowNetworkSettings(service_path); } return; } NOTREACHED(); } void OnConnectFailed(const std::string& service_path, gfx::NativeWindow parent_window, const std::string& error_name, scoped_ptr error_data) { NET_LOG_ERROR("Connect Failed: " + error_name, service_path); if (!ash::Shell::HasInstance()) return; // If a new connect attempt canceled this connect, no need to notify the user. if (error_name == NetworkConnectionHandler::kErrorConnectCanceled) return; if (error_name == shill::kErrorBadPassphrase || error_name == NetworkConnectionHandler::kErrorPassphraseRequired || error_name == NetworkConnectionHandler::kErrorConfigurationRequired || error_name == NetworkConnectionHandler::kErrorAuthenticationRequired) { HandleUnconfiguredNetwork(service_path, parent_window); return; } if (error_name == NetworkConnectionHandler::kErrorCertificateRequired) { if (!ash::Shell::GetInstance()->system_tray_delegate()->EnrollNetwork( service_path, parent_window)) { HandleUnconfiguredNetwork(service_path, parent_window); } return; } if (error_name == NetworkConnectionHandler::kErrorActivationRequired) { network_connect::ActivateCellular(service_path); return; } if (error_name == NetworkConnectionHandler::kErrorConnected || error_name == NetworkConnectionHandler::kErrorConnecting) { network_connect::ShowNetworkSettings(service_path); return; } // ConnectFailed or unknown error; show a notification. std::string shill_error; error_data.get()->GetString( chromeos::network_handler::kErrorDetail, &shill_error); ShowErrorNotification(error_name, shill_error, service_path); // Only show a configure dialog if there was a ConnectFailed error and the // screen is not locked. if (error_name != shill::kErrorConnectFailed || Shell::GetInstance()->session_state_delegate()->IsScreenLocked()) return; // If Shill reports an InProgress error, don't try to configure the network. std::string dbus_error_name; error_data.get()->GetString( chromeos::network_handler::kDbusErrorName, &dbus_error_name); if (dbus_error_name == kErrorInProgress) return; HandleUnconfiguredNetwork(service_path, parent_window); } void OnConnectSucceeded(const std::string& service_path) { NET_LOG_USER("Connect Succeeded", service_path); if (!ash::Shell::HasInstance()) return; message_center::MessageCenter::Get()->RemoveNotification( network_connect::kNetworkConnectNotificationId, false /* not by user */); } // 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). |parent_window| will be used to parent // any configuration UI on failure and may be NULL (in which case the default // window will be used). void CallConnectToNetwork(const std::string& service_path, bool check_error_state, gfx::NativeWindow parent_window) { if (!ash::Shell::HasInstance()) return; message_center::MessageCenter::Get()->RemoveNotification( network_connect::kNetworkConnectNotificationId, false /* not by user */); NetworkHandler::Get()->network_connection_handler()->ConnectToNetwork( service_path, base::Bind(&OnConnectSucceeded, service_path), base::Bind(&OnConnectFailed, service_path, parent_window), check_error_state); } void OnActivateFailed(const std::string& service_path, const std::string& error_name, scoped_ptr error_data) { NET_LOG_ERROR("Unable to activate network", service_path); ShowErrorNotification( network_connect::kErrorActivateFailed, "", service_path); } void OnActivateSucceeded(const std::string& service_path) { NET_LOG_USER("Activation Succeeded", service_path); } void OnConfigureFailed(const std::string& error_name, scoped_ptr error_data) { NET_LOG_ERROR("Unable to configure network", ""); ShowErrorNotification( NetworkConnectionHandler::kErrorConfigureFailed, "", ""); } void OnConfigureSucceeded(const std::string& service_path) { NET_LOG_USER("Configure Succeeded", service_path); // After configuring a network, ignore any (possibly stale) error state. const bool check_error_state = false; const gfx::NativeWindow parent_window = NULL; CallConnectToNetwork(service_path, check_error_state, parent_window); } void SetPropertiesFailed(const std::string& desc, const std::string& service_path, const std::string& config_error_name, scoped_ptr error_data) { NET_LOG_ERROR(desc + ": Failed: " + config_error_name, service_path); ShowErrorNotification( NetworkConnectionHandler::kErrorConfigureFailed, "", service_path); } void SetPropertiesToClear(base::DictionaryValue* properties_to_set, std::vector* 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::iterator iter = properties_to_clear->begin(); iter != properties_to_clear->end(); ++iter) { properties_to_set->RemoveWithoutPathExpansion(*iter, NULL); } } void ClearPropertiesAndConnect( const std::string& service_path, const std::vector& properties_to_clear) { NET_LOG_USER("ClearPropertiesAndConnect", service_path); // After configuring a network, ignore any (possibly stale) error state. const bool check_error_state = false; const gfx::NativeWindow parent_window = NULL; NetworkHandler::Get()->network_configuration_handler()->ClearProperties( service_path, properties_to_clear, base::Bind(&CallConnectToNetwork, service_path, check_error_state, parent_window), base::Bind(&SetPropertiesFailed, "ClearProperties", service_path)); } // Returns false if !shared and no valid profile is available, which will // trigger an error and abort. bool GetNetworkProfilePath(bool shared, std::string* profile_path) { if (shared) { *profile_path = NetworkProfileHandler::kSharedProfilePath; return true; } if (!chromeos::LoginState::Get()->IsUserAuthenticated()) { 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 ConfigureSetProfileSucceeded( const std::string& service_path, scoped_ptr properties_to_set) { std::vector properties_to_clear; SetPropertiesToClear(properties_to_set.get(), &properties_to_clear); NetworkHandler::Get()->network_configuration_handler()->SetProperties( service_path, *properties_to_set, base::Bind(&ClearPropertiesAndConnect, service_path, properties_to_clear), base::Bind(&SetPropertiesFailed, "SetProperties", service_path)); } const NetworkState* GetNetworkState(const std::string& service_path) { return NetworkHandler::Get()->network_state_handler()-> GetNetworkState(service_path); } } // namespace namespace network_connect { const char kNetworkConnectNotificationId[] = "chrome://settings/internet/connect"; const char kNetworkActivateNotificationId[] = "chrome://settings/internet/activate"; const char kErrorActivateFailed[] = "activate-failed"; void ConnectToNetwork(const std::string& service_path, gfx::NativeWindow parent_window) { NET_LOG_USER("ConnectToNetwork", service_path); const NetworkState* network = GetNetworkState(service_path); if (network && !network->error().empty()) { NET_LOG_USER("Configure: " + network->error(), service_path); // If the network is in an error state, show the configuration UI directly // to avoid a spurious notification. HandleUnconfiguredNetwork(service_path, parent_window); return; } const bool check_error_state = true; CallConnectToNetwork(service_path, check_error_state, parent_window); } void 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, chromeos::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. ash::Shell::GetInstance()->system_tray_delegate()-> ShowMobileSimDialog(); return; } } } handler->SetTechnologyEnabled( technology, true, chromeos::network_handler::ErrorCallback()); } void ActivateCellular(const std::string& service_path) { NET_LOG_USER("ActivateCellular", service_path); const NetworkState* cellular = GetNetworkState(service_path); if (!cellular || cellular->type() != shill::kTypeCellular) { NET_LOG_ERROR("ActivateCellular with no Service", service_path); return; } const DeviceState* cellular_device = NetworkHandler::Get()->network_state_handler()-> GetDeviceState(cellular->device_path()); if (!cellular_device) { NET_LOG_ERROR("ActivateCellular with no Device", service_path); return; } if (!IsDirectActivatedCarrier(cellular_device->carrier())) { // For non direct activation, show the mobile setup dialog which can be // used to activate the network. ShowMobileSetup(service_path); return; } if (cellular->activation_state() == shill::kActivationStateActivated) { NET_LOG_ERROR("ActivateCellular for activated service", service_path); return; } NetworkHandler::Get()->network_activation_handler()->Activate( service_path, "", // carrier base::Bind(&OnActivateSucceeded, service_path), base::Bind(&OnActivateFailed, service_path)); } void ShowMobileSetup(const std::string& service_path) { NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); const NetworkState* cellular = handler->GetNetworkState(service_path); if (!cellular || cellular->type() != shill::kTypeCellular) { NET_LOG_ERROR("ShowMobileSetup without Cellular network", service_path); return; } if (cellular->activation_state() != shill::kActivationStateActivated && cellular->activate_over_non_cellular_networks() && !handler->DefaultNetwork()) { message_center::MessageCenter::Get()->AddNotification( message_center::Notification::CreateSystemNotification( kNetworkActivateNotificationId, l10n_util::GetStringUTF16(IDS_NETWORK_ACTIVATION_ERROR_TITLE), l10n_util::GetStringFUTF16(IDS_NETWORK_ACTIVATION_NEEDS_CONNECTION, UTF8ToUTF16(cellular->name())), ui::ResourceBundle::GetSharedInstance().GetImageNamed( IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED), ash::system_notifier::kNotifierNetwork, base::Bind(&ash::network_connect::ShowNetworkSettings, service_path))); return; } ash::Shell::GetInstance()->system_tray_delegate()->ShowMobileSetupDialog( service_path); } void ConfigureNetworkAndConnect(const std::string& service_path, const base::DictionaryValue& properties, bool shared) { NET_LOG_USER("ConfigureNetworkAndConnect", service_path); scoped_ptr properties_to_set(properties.DeepCopy()); std::string profile_path; if (!GetNetworkProfilePath(shared, &profile_path)) { ShowErrorNotification( NetworkConnectionHandler::kErrorConfigureFailed, "", service_path); return; } NetworkHandler::Get()->network_configuration_handler()->SetNetworkProfile( service_path, profile_path, base::Bind(&ConfigureSetProfileSucceeded, service_path, base::Passed(&properties_to_set)), base::Bind(&SetPropertiesFailed, "SetProfile: " + profile_path, service_path)); } void CreateConfigurationAndConnect(base::DictionaryValue* properties, bool shared) { NET_LOG_USER("CreateConfigurationAndConnect", ""); std::string profile_path; if (!GetNetworkProfilePath(shared, &profile_path)) { ShowErrorNotification( NetworkConnectionHandler::kErrorConfigureFailed, "", ""); return; } properties->SetStringWithoutPathExpansion( shill::kProfileProperty, profile_path); NetworkHandler::Get()->network_configuration_handler()->CreateConfiguration( *properties, base::Bind(&OnConfigureSucceeded), base::Bind(&OnConfigureFailed)); } base::string16 ErrorString(const std::string& error, const std::string& service_path) { if (error.empty()) return base::string16(); if (error == shill::kErrorOutOfRange) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OUT_OF_RANGE); if (error == shill::kErrorPinMissing) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_PIN_MISSING); if (error == shill::kErrorDhcpFailed) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_DHCP_FAILED); if (error == shill::kErrorConnectFailed) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_CONNECT_FAILED); if (error == shill::kErrorBadPassphrase) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_PASSPHRASE); if (error == shill::kErrorBadWEPKey) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_BAD_WEPKEY); if (error == shill::kErrorActivationFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_ACTIVATION_FAILED); } if (error == shill::kErrorNeedEvdo) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_NEED_EVDO); if (error == shill::kErrorNeedHomeNetwork) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_NEED_HOME_NETWORK); } if (error == shill::kErrorOtaspFailed) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_OTASP_FAILED); if (error == shill::kErrorAaaFailed) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_AAA_FAILED); if (error == shill::kErrorInternal) return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_INTERNAL); if (error == shill::kErrorDNSLookupFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_DNS_LOOKUP_FAILED); } if (error == shill::kErrorHTTPGetFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_HTTP_GET_FAILED); } if (error == shill::kErrorIpsecPskAuthFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_IPSEC_PSK_AUTH_FAILED); } if (error == shill::kErrorIpsecCertAuthFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_CERT_AUTH_FAILED); } if (error == shill::kErrorEapAuthenticationFailed) { const NetworkState* network = GetNetworkState(service_path); // TLS always requires a client certificate, so show a cert auth // failed message for TLS. Other EAP methods do not generally require // a client certicate. if (network && network->eap_method() == shill::kEapMethodTLS) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_CERT_AUTH_FAILED); } else { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_EAP_AUTH_FAILED); } } if (error == shill::kErrorEapLocalTlsFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_EAP_LOCAL_TLS_FAILED); } if (error == shill::kErrorEapRemoteTlsFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_EAP_REMOTE_TLS_FAILED); } if (error == shill::kErrorPppAuthFailed) { return l10n_util::GetStringUTF16( IDS_CHROMEOS_NETWORK_ERROR_PPP_AUTH_FAILED); } if (StringToLowerASCII(error) == StringToLowerASCII(std::string(shill::kUnknownString))) { return l10n_util::GetStringUTF16(IDS_CHROMEOS_NETWORK_ERROR_UNKNOWN); } return l10n_util::GetStringFUTF16(IDS_NETWORK_UNRECOGNIZED_ERROR, UTF8ToUTF16(error)); } void ShowNetworkSettings(const std::string& service_path) { if (!ash::Shell::HasInstance()) return; ash::Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings( service_path); } } // network_connect } // ash