blob: 23381bcc2dd5fce11e34b6adcce2a89913c2789e [file] [log] [blame]
// Copyright (c) 2012 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_state.h"
#include <stddef.h>
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_type_pattern.h"
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_utils.h"
#include "chromeos/network/shill_property_util.h"
#include "components/device_event_log/device_event_log.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace {
const char kErrorUnknown[] = "Unknown";
bool ConvertListValueToStringVector(const base::ListValue& string_list,
std::vector<std::string>* result) {
for (size_t i = 0; i < string_list.GetSize(); ++i) {
std::string str;
if (!string_list.GetString(i, &str))
return false;
result->push_back(str);
}
return true;
}
bool IsCaptivePortalState(const base::DictionaryValue& properties, bool log) {
std::string state;
properties.GetStringWithoutPathExpansion(shill::kStateProperty, &state);
if (state != shill::kStatePortal)
return false;
std::string portal_detection_phase, portal_detection_status;
if (!properties.GetStringWithoutPathExpansion(
shill::kPortalDetectionFailedPhaseProperty,
&portal_detection_phase) ||
!properties.GetStringWithoutPathExpansion(
shill::kPortalDetectionFailedStatusProperty,
&portal_detection_status)) {
// If Shill (or a stub) has not set PortalDetectionFailedStatus
// or PortalDetectionFailedPhase, assume we are in captive portal state.
return true;
}
// Shill reports the phase in which it determined that the device is behind a
// captive portal. We only want to rely only on incorrect content being
// returned and ignore other reasons.
bool is_captive_portal =
portal_detection_phase == shill::kPortalDetectionPhaseContent &&
portal_detection_status == shill::kPortalDetectionStatusFailure;
if (log) {
std::string name;
properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
if (name.empty())
properties.GetStringWithoutPathExpansion(shill::kSSIDProperty, &name);
if (!is_captive_portal) {
NET_LOG(EVENT) << "State is 'portal' but not in captive portal state:"
<< " name=" << name << " phase=" << portal_detection_phase
<< " status=" << portal_detection_status;
} else {
NET_LOG(EVENT) << "Network is in captive portal state: " << name;
}
}
return is_captive_portal;
}
} // namespace
namespace chromeos {
NetworkState::NetworkState(const std::string& path)
: ManagedState(MANAGED_TYPE_NETWORK, path),
visible_(false),
priority_(0),
prefix_length_(0),
connectable_(false),
is_captive_portal_(false),
signal_strength_(0),
cellular_out_of_credits_(false) {}
NetworkState::~NetworkState() {
}
bool NetworkState::PropertyChanged(const std::string& key,
const base::Value& value) {
// Keep care that these properties are the same as in |GetProperties|.
if (ManagedStatePropertyChanged(key, value))
return true;
if (key == shill::kSignalStrengthProperty) {
return GetIntegerValue(key, value, &signal_strength_);
} else if (key == shill::kStateProperty) {
return GetStringValue(key, value, &connection_state_);
} else if (key == shill::kVisibleProperty) {
return GetBooleanValue(key, value, &visible_);
} else if (key == shill::kConnectableProperty) {
return GetBooleanValue(key, value, &connectable_);
} else if (key == shill::kErrorProperty) {
if (!GetStringValue(key, value, &error_))
return false;
if (ErrorIsValid(error_))
last_error_ = error_;
else
error_.clear();
return true;
} else if (key == shill::kActivationTypeProperty) {
return GetStringValue(key, value, &activation_type_);
} else if (key == shill::kActivationStateProperty) {
return GetStringValue(key, value, &activation_state_);
} else if (key == shill::kRoamingStateProperty) {
return GetStringValue(key, value, &roaming_);
} else if (key == shill::kPaymentPortalProperty) {
const base::DictionaryValue* olp;
if (!value.GetAsDictionary(&olp))
return false;
return olp->GetStringWithoutPathExpansion(shill::kPaymentPortalURL,
&payment_url_);
} else if (key == shill::kSecurityClassProperty) {
return GetStringValue(key, value, &security_class_);
} else if (key == shill::kEapMethodProperty) {
return GetStringValue(key, value, &eap_method_);
} else if (key == shill::kEapKeyMgmtProperty) {
return GetStringValue(key, value, &eap_key_mgmt_);
} else if (key == shill::kNetworkTechnologyProperty) {
return GetStringValue(key, value, &network_technology_);
} else if (key == shill::kDeviceProperty) {
return GetStringValue(key, value, &device_path_);
} else if (key == shill::kGuidProperty) {
return GetStringValue(key, value, &guid_);
} else if (key == shill::kProfileProperty) {
return GetStringValue(key, value, &profile_path_);
} else if (key == shill::kWifiHexSsid) {
std::string ssid_hex;
if (!GetStringValue(key, value, &ssid_hex)) {
return false;
}
raw_ssid_.clear();
return base::HexStringToBytes(ssid_hex, &raw_ssid_);
} else if (key == shill::kPriorityProperty) {
return GetIntegerValue(key, value, &priority_);
} else if (key == shill::kOutOfCreditsProperty) {
return GetBooleanValue(key, value, &cellular_out_of_credits_);
} else if (key == shill::kProxyConfigProperty) {
std::string proxy_config_str;
if (!value.GetAsString(&proxy_config_str)) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
proxy_config_.Clear();
if (proxy_config_str.empty())
return true;
scoped_ptr<base::DictionaryValue> proxy_config_dict(
onc::ReadDictionaryFromJson(proxy_config_str));
if (proxy_config_dict) {
// Warning: The DictionaryValue returned from
// ReadDictionaryFromJson/JSONParser is an optimized derived class that
// doesn't allow releasing ownership of nested values. A Swap in the wrong
// order leads to memory access errors.
proxy_config_.MergeDictionary(proxy_config_dict.get());
} else {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
}
return true;
} else if (key == shill::kProviderProperty) {
std::string vpn_provider_type;
const base::DictionaryValue* dict;
if (!value.GetAsDictionary(&dict) ||
!dict->GetStringWithoutPathExpansion(shill::kTypeProperty,
&vpn_provider_type)) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
if (vpn_provider_type == shill::kProviderThirdPartyVpn) {
// If the network uses a third-party VPN provider, copy over the
// provider's extension ID, which is held in |shill::kHostProperty|.
if (!dict->GetStringWithoutPathExpansion(
shill::kHostProperty, &third_party_vpn_provider_extension_id_)) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
} else {
third_party_vpn_provider_extension_id_.clear();
}
vpn_provider_type_ = vpn_provider_type;
return true;
}
return false;
}
bool NetworkState::InitialPropertiesReceived(
const base::DictionaryValue& properties) {
NET_LOG(EVENT) << "InitialPropertiesReceived: " << path() << ": " << name()
<< " State: " << connection_state_ << " Visible: " << visible_;
if (!properties.HasKey(shill::kTypeProperty)) {
NET_LOG(ERROR) << "NetworkState has no type: "
<< shill_property_util::GetNetworkIdFromProperties(
properties);
return false;
}
// By convention, all visible WiFi and WiMAX networks have a
// SignalStrength > 0.
if ((type() == shill::kTypeWifi || type() == shill::kTypeWimax) &&
visible() && signal_strength_ <= 0) {
signal_strength_ = 1;
}
// Any change to connection state will trigger a complete property update,
// so we update is_captive_portal_ here.
is_captive_portal_ = IsCaptivePortalState(properties, true /* log */);
// Ensure that the network has a valid name.
return UpdateName(properties);
}
void NetworkState::GetStateProperties(base::DictionaryValue* dictionary) const {
ManagedState::GetStateProperties(dictionary);
// Properties shared by all types.
dictionary->SetStringWithoutPathExpansion(shill::kGuidProperty, guid());
dictionary->SetStringWithoutPathExpansion(shill::kSecurityClassProperty,
security_class());
dictionary->SetStringWithoutPathExpansion(shill::kProfileProperty,
profile_path());
dictionary->SetIntegerWithoutPathExpansion(shill::kPriorityProperty,
priority_);
if (visible()) {
dictionary->SetStringWithoutPathExpansion(shill::kStateProperty,
connection_state());
}
// VPN properties.
if (NetworkTypePattern::VPN().MatchesType(type())) {
// Shill sends VPN provider properties in a nested dictionary. |dictionary|
// must replicate that nested structure.
scoped_ptr<base::DictionaryValue> provider_property(
new base::DictionaryValue);
provider_property->SetStringWithoutPathExpansion(shill::kTypeProperty,
vpn_provider_type_);
if (vpn_provider_type_ == shill::kProviderThirdPartyVpn) {
provider_property->SetStringWithoutPathExpansion(
shill::kHostProperty, third_party_vpn_provider_extension_id_);
}
dictionary->SetWithoutPathExpansion(shill::kProviderProperty,
provider_property.release());
}
// Wireless properties
if (!NetworkTypePattern::Wireless().MatchesType(type()))
return;
if (visible()) {
dictionary->SetBooleanWithoutPathExpansion(shill::kConnectableProperty,
connectable());
dictionary->SetIntegerWithoutPathExpansion(shill::kSignalStrengthProperty,
signal_strength());
}
// Wifi properties
if (NetworkTypePattern::WiFi().MatchesType(type())) {
dictionary->SetStringWithoutPathExpansion(shill::kEapMethodProperty,
eap_method());
}
// Mobile properties
if (NetworkTypePattern::Mobile().MatchesType(type())) {
dictionary->SetStringWithoutPathExpansion(shill::kNetworkTechnologyProperty,
network_technology());
dictionary->SetStringWithoutPathExpansion(shill::kActivationStateProperty,
activation_state());
dictionary->SetStringWithoutPathExpansion(shill::kRoamingStateProperty,
roaming());
dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty,
cellular_out_of_credits());
}
}
void NetworkState::IPConfigPropertiesChanged(
const base::DictionaryValue& properties) {
for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
iter.Advance()) {
std::string key = iter.key();
const base::Value& value = iter.value();
if (key == shill::kAddressProperty) {
GetStringValue(key, value, &ip_address_);
} else if (key == shill::kGatewayProperty) {
GetStringValue(key, value, &gateway_);
} else if (key == shill::kNameServersProperty) {
const base::ListValue* dns_servers;
if (value.GetAsList(&dns_servers)) {
dns_servers_.clear();
ConvertListValueToStringVector(*dns_servers, &dns_servers_);
}
} else if (key == shill::kPrefixlenProperty) {
GetIntegerValue(key, value, &prefix_length_);
} else if (key == shill::kWebProxyAutoDiscoveryUrlProperty) {
std::string url_string;
if (GetStringValue(key, value, &url_string)) {
if (url_string.empty()) {
web_proxy_auto_discovery_url_ = GURL();
} else {
GURL gurl(url_string);
if (gurl.is_valid()) {
web_proxy_auto_discovery_url_ = gurl;
} else {
NET_LOG(ERROR) << "Invalid WebProxyAutoDiscoveryUrl: " << path()
<< ": " << url_string;
web_proxy_auto_discovery_url_ = GURL();
}
}
}
}
}
}
bool NetworkState::RequiresActivation() const {
return (type() == shill::kTypeCellular &&
activation_state() != shill::kActivationStateActivated &&
activation_state() != shill::kActivationStateUnknown);
}
std::string NetworkState::connection_state() const {
if (!visible())
return shill::kStateDisconnect;
return connection_state_;
}
bool NetworkState::IsDynamicWep() const {
return security_class_ == shill::kSecurityWep &&
eap_key_mgmt_ == shill::kKeyManagementIEEE8021X;
}
bool NetworkState::IsConnectedState() const {
return visible() && StateIsConnected(connection_state_);
}
bool NetworkState::IsConnectingState() const {
return visible() && StateIsConnecting(connection_state_);
}
bool NetworkState::IsInProfile() const {
// kTypeEthernetEap is always saved. We need this check because it does
// not show up in the visible list, but its properties may not be available
// when it first shows up in ServiceCompleteList. See crbug.com/355117.
return !profile_path_.empty() || type() == shill::kTypeEthernetEap;
}
bool NetworkState::IsPrivate() const {
return !profile_path_.empty() &&
profile_path_ != NetworkProfileHandler::GetSharedProfilePath();
}
std::string NetworkState::GetHexSsid() const {
return base::HexEncode(raw_ssid().data(), raw_ssid().size());
}
std::string NetworkState::GetDnsServersAsString() const {
std::string result;
for (size_t i = 0; i < dns_servers_.size(); ++i) {
if (i != 0)
result += ",";
result += dns_servers_[i];
}
return result;
}
std::string NetworkState::GetNetmask() const {
return network_util::PrefixLengthToNetmask(prefix_length_);
}
std::string NetworkState::GetSpecifier() const {
if (!update_received()) {
NET_LOG(ERROR) << "GetSpecifier called before update: " << path();
return std::string();
}
if (type() == shill::kTypeWifi)
return name() + "_" + security_class_;
if (!name().empty())
return name();
return type(); // For unnamed networks such as ethernet.
}
void NetworkState::SetGuid(const std::string& guid) {
guid_ = guid;
}
bool NetworkState::UpdateName(const base::DictionaryValue& properties) {
std::string updated_name =
shill_property_util::GetNameFromProperties(path(), properties);
if (updated_name != name()) {
set_name(updated_name);
return true;
}
return false;
}
std::string NetworkState::GetErrorState() const {
if (ErrorIsValid(error()))
return error();
return last_error();
}
// static
bool NetworkState::StateIsConnected(const std::string& connection_state) {
return (connection_state == shill::kStateReady ||
connection_state == shill::kStateOnline ||
connection_state == shill::kStatePortal);
}
// static
bool NetworkState::StateIsConnecting(const std::string& connection_state) {
return (connection_state == shill::kStateAssociation ||
connection_state == shill::kStateConfiguration ||
connection_state == shill::kStateCarrier);
}
// static
bool NetworkState::NetworkStateIsCaptivePortal(
const base::DictionaryValue& shill_properties) {
return IsCaptivePortalState(shill_properties, false /* log */);
}
// static
bool NetworkState::ErrorIsValid(const std::string& error) {
// Shill uses "Unknown" to indicate an unset or cleared error state.
return !error.empty() && error != kErrorUnknown;
}
} // namespace chromeos