// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/network/network_state.h"
#include <stddef.h>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include "ash/constants/ash_features.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/ash/components/network/cellular_utils.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/network_config.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_profile_handler.h"
#include "chromeos/ash/components/network/network_type_pattern.h"
#include "chromeos/ash/components/network/network_ui_data.h"
#include "chromeos/ash/components/network/network_util.h"
#include "chromeos/ash/components/network/shill_property_util.h"
#include "chromeos/ash/components/network/tether_constants.h"
#include "chromeos/components/onc/onc_utils.h"
#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
#include "net/http/http_status_code.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace ash {
namespace {
namespace network_config {
namespace mojom = ::chromeos::network_config::mojom;
}
// TODO(tbarzic): Add payment portal method values to shill/dbus-constants.
constexpr char kPaymentPortalMethodPost[] = "POST";
// |dict| may be an empty value, in which case return an empty string.
std::string GetStringFromDictionary(
const std::optional<base::Value::Dict>& dict,
const char* key) {
const std::string* stringp =
dict.has_value() ? dict->FindString(key) : nullptr;
return stringp ? *stringp : std::string();
}
bool IsValidConnectionState(const std::string& connection_state) {
return connection_state == shill::kStateIdle ||
connection_state == shill::kStateAssociation ||
connection_state == shill::kStateConfiguration ||
connection_state == shill::kStateReady ||
connection_state == shill::kStateNoConnectivity ||
connection_state == shill::kStateRedirectFound ||
connection_state == shill::kStatePortalSuspected ||
connection_state == shill::kStateOnline ||
connection_state == shill::kStateFailure ||
connection_state == shill::kStateDisconnecting;
}
} // namespace
NetworkState::NetworkState(const std::string& path)
: ManagedState(MANAGED_TYPE_NETWORK, path) {}
NetworkState::~NetworkState() = default;
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) {
int signal_strength = signal_strength_;
if (!GetIntegerValue(key, value, &signal_strength)) {
return false;
}
if (signal_strength_ > 0 && abs(signal_strength - signal_strength_) <
kSignalStrengthChangeThreshold) {
return false;
}
signal_strength_ = signal_strength;
return true;
} else if (key == shill::kWifiSignalStrengthRssiProperty) {
int rssi = rssi_;
if (!GetIntegerValue(key, value, &rssi)) {
return false;
}
if (rssi == rssi_) {
return false;
}
rssi_ = rssi;
return true;
} else if (key == shill::kStateProperty) {
std::string connection_state;
if (!GetStringValue(key, value, &connection_state)) {
return false;
}
SetConnectionState(connection_state);
return true;
} 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) {
std::string error;
if (!GetStringValue(key, value, &error)) {
return false;
}
if (ErrorIsValid(error)) {
last_error_ = error;
}
return true;
} else if (key == shill::kWifiFrequency) {
return GetIntegerValue(key, value, &frequency_);
} 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::kCellularAllowRoamingProperty) {
return GetBooleanValue(key, value, &allow_roaming_);
} else if (key == shill::kPaymentPortalProperty) {
const base::Value::Dict* value_dict = value.GetIfDict();
if (!value_dict) {
return false;
}
const std::string* portal_url_value =
value_dict->FindString(shill::kPaymentPortalURL);
if (!portal_url_value) {
return false;
}
payment_url_ = *portal_url_value;
// If payment portal uses post method, set up post data.
const std::string* portal_method_value =
value_dict->FindString(shill::kPaymentPortalMethod);
payment_method_ =
portal_method_value ? *portal_method_value : std::string();
const std::string* portal_post_data_value =
value_dict->FindString(shill::kPaymentPortalPostData);
if (payment_method_ == kPaymentPortalMethodPost && portal_post_data_value) {
payment_post_data_ = *portal_post_data_value;
}
return true;
} 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::kWifiBSsid) {
return GetStringValue(key, value, &bssid_);
} else if (key == shill::kPriorityProperty) {
return GetIntegerValue(key, value, &priority_);
} else if (key == shill::kWifiHiddenSsid) {
return GetBooleanValue(key, value, &hidden_ssid_);
} else if (key == shill::kPasspointIDProperty) {
return GetStringValue(key, value, &passpoint_id_);
} else if (key == shill::kMeteredProperty) {
return GetBooleanValue(key, value, &metered_);
} else if (key == shill::kOutOfCreditsProperty) {
return GetBooleanValue(key, value, &cellular_out_of_credits_);
} else if (key == shill::kIccidProperty) {
return GetStringValue(key, value, &iccid_);
} else if (key == shill::kEidProperty) {
return GetStringValue(key, value, &eid_);
} else if (key == shill::kProxyConfigProperty) {
const std::string* proxy_config_str = value.GetIfString();
if (!proxy_config_str) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
if ((*proxy_config_str).empty()) {
proxy_config_.reset();
return true;
}
std::optional<base::Value::Dict> proxy_config =
chromeos::onc::ReadDictionaryFromJson(*proxy_config_str);
if (!proxy_config.has_value()) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
proxy_config_.reset();
} else {
proxy_config_ = std::move(proxy_config.value());
}
return true;
} else if (key == shill::kProviderProperty) {
const std::string* vpn_provider_type =
value.is_dict() ? value.GetDict().FindString(shill::kTypeProperty)
: nullptr;
if (!vpn_provider_type) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
std::string vpn_provider_id;
if (*vpn_provider_type == shill::kProviderThirdPartyVpn ||
*vpn_provider_type == shill::kProviderArcVpn) {
// If the network uses a third-party or Arc VPN provider,
// |shill::kHostProperty| contains the extension ID or Arc package name.
const std::string* host =
value.GetDict().FindString(shill::kHostProperty);
if (!host) {
NET_LOG(ERROR) << "Failed to parse " << path() << "." << key;
return false;
}
vpn_provider_id = *host;
}
SetVpnProvider(vpn_provider_id, *vpn_provider_type);
return true;
} else if (key == shill::kUIDataProperty) {
std::unique_ptr<NetworkUIData> ui_data =
shill_property_util::GetUIDataFromValue(value);
if (!ui_data) {
return false;
}
onc_source_ = ui_data->onc_source();
return true;
} else if (key == shill::kProbeUrlProperty) {
std::string probe_url_string;
if (!GetStringValue(key, value, &probe_url_string)) {
return false;
}
probe_url_ = GURL(probe_url_string);
return true;
} else if (key == shill::kUplinkSpeedPropertyKbps) {
uint32_t max_uplink_speed_kbps;
if (!GetUInt32Value(key, value, &max_uplink_speed_kbps)) {
return false;
}
if (max_uplink_speed_kbps_.has_value() &&
max_uplink_speed_kbps == max_uplink_speed_kbps_.value()) {
return false;
}
max_uplink_speed_kbps_ = max_uplink_speed_kbps;
return true;
} else if (key == shill::kDownlinkSpeedPropertyKbps) {
uint32_t max_downlink_speed_kbps;
if (!GetUInt32Value(key, value, &max_downlink_speed_kbps)) {
return false;
}
if (max_downlink_speed_kbps_.has_value() &&
max_downlink_speed_kbps == max_downlink_speed_kbps_.value()) {
return false;
}
max_downlink_speed_kbps_ = max_downlink_speed_kbps;
return true;
} else if (key == shill::kNetworkConfigProperty) {
network_config_ = NetworkConfig::ParseFromServicePropertyValue(value);
return true;
}
return false;
}
bool NetworkState::InitialPropertiesReceived(
const base::Value::Dict& properties) {
NET_LOG(EVENT) << "InitialPropertiesReceived: " << NetworkId(this)
<< " State: " << connection_state_ << " Visible: " << visible_;
if (!properties.contains(shill::kTypeProperty)) {
NET_LOG(ERROR) << "NetworkState has no type: " << NetworkId(this);
return false;
}
// By convention, all visible WiFi networks have a SignalStrength > 0.
if (type() == shill::kTypeWifi && visible() && signal_strength_ <= 0) {
signal_strength_ = 1;
}
// Any change to connection state or portal properties will trigger a complete
// property update, so we update captive portal state here.
UpdateCaptivePortalState(properties);
// Ensure that the network has a valid name.
return UpdateName(properties);
}
void NetworkState::GetStateProperties(base::Value::Dict* dictionary) const {
ManagedState::GetStateProperties(dictionary);
// Properties shared by all types.
dictionary->Set(shill::kGuidProperty, guid());
dictionary->Set(shill::kSecurityClassProperty, security_class());
dictionary->Set(shill::kProfileProperty, profile_path());
dictionary->Set(shill::kPriorityProperty, priority_);
if (visible()) {
dictionary->Set(shill::kStateProperty, connection_state());
}
if (!device_path().empty()) {
dictionary->Set(shill::kDeviceProperty, device_path());
}
// VPN properties.
if (NetworkTypePattern::VPN().MatchesType(type()) && vpn_provider()) {
// Shill sends VPN provider properties in a nested dictionary. |dictionary|
// must replicate that nested structure.
std::string provider_type = vpn_provider()->type;
auto provider_property =
base::Value::Dict().Set(shill::kTypeProperty, provider_type);
if (provider_type == shill::kProviderThirdPartyVpn ||
provider_type == shill::kProviderArcVpn) {
provider_property.Set(shill::kHostProperty, vpn_provider()->id);
}
dictionary->Set(shill::kProviderProperty, std::move(provider_property));
}
// Tether properties
if (NetworkTypePattern::Tether().MatchesType(type())) {
dictionary->Set(kTetherBatteryPercentage, battery_percentage());
dictionary->Set(kTetherCarrier, tether_carrier());
dictionary->Set(kTetherHasConnectedToHost, tether_has_connected_to_host());
dictionary->Set(kTetherSignalStrength, signal_strength());
// All Tether networks are connectable.
dictionary->Set(shill::kConnectableProperty, connectable());
// Tether networks do not share some of the wireless/mobile properties added
// below; exit early to avoid having these properties applied.
return;
}
// Wireless properties
if (!NetworkTypePattern::Wireless().MatchesType(type())) {
return;
}
if (visible()) {
dictionary->Set(shill::kConnectableProperty, connectable());
dictionary->Set(shill::kSignalStrengthProperty, signal_strength());
}
// Wifi properties
if (NetworkTypePattern::WiFi().MatchesType(type())) {
dictionary->Set(shill::kWifiBSsid, bssid_);
dictionary->Set(shill::kEapMethodProperty, eap_method());
dictionary->Set(shill::kWifiFrequency, frequency_);
dictionary->Set(shill::kWifiHexSsid, GetHexSsid());
}
// Mobile properties
if (NetworkTypePattern::Mobile().MatchesType(type())) {
dictionary->Set(shill::kNetworkTechnologyProperty, network_technology());
dictionary->Set(shill::kActivationStateProperty, activation_state());
dictionary->Set(shill::kRoamingStateProperty, roaming_);
dictionary->Set(shill::kOutOfCreditsProperty, cellular_out_of_credits());
}
// Cellular properties
if (NetworkTypePattern::Cellular().MatchesType(type())) {
dictionary->Set(shill::kIccidProperty, iccid());
dictionary->Set(shill::kEidProperty, eid());
}
}
bool NetworkState::IsActive() const {
return IsConnectingOrConnected() ||
activation_state() == shill::kActivationStateActivating;
}
void NetworkState::IPConfigPropertiesChanged(
const base::Value::Dict& properties) {
if (properties.empty()) {
ipv4_config_.reset();
return;
}
ipv4_config_ = properties.Clone();
}
std::string NetworkState::GetIpAddress() const {
return GetStringFromDictionary(ipv4_config_, shill::kAddressProperty);
}
std::string NetworkState::GetGateway() const {
return GetStringFromDictionary(ipv4_config_, shill::kGatewayProperty);
}
GURL NetworkState::GetWebProxyAutoDiscoveryUrl() const {
std::string url = GetStringFromDictionary(
ipv4_config_, shill::kWebProxyAutoDiscoveryUrlProperty);
if (url.empty()) {
return GURL();
}
GURL gurl(url);
if (!gurl.is_valid()) {
NET_LOG(ERROR) << "Invalid WebProxyAutoDiscoveryUrl: " << NetworkId(this)
<< ": " << url;
return GURL();
}
return gurl;
}
std::string NetworkState::GetVpnProviderType() const {
return vpn_provider_ ? vpn_provider_->type : std::string();
}
bool NetworkState::RequiresActivation() const {
return type() == shill::kTypeCellular &&
(activation_state() == shill::kActivationStateNotActivated ||
activation_state() == shill::kActivationStatePartiallyActivated);
}
bool NetworkState::SecurityRequiresPassphraseOnly() const {
return type() == shill::kTypeWifi &&
(security_class_ == shill::kSecurityClassPsk ||
security_class_ == shill::kSecurityClassWep);
}
const std::string& NetworkState::GetError() const {
return last_error_;
}
void NetworkState::ClearError() {
last_error_.clear();
}
std::string NetworkState::connection_state() const {
if (!visible()) {
return shill::kStateIdle;
}
return connection_state_;
}
void NetworkState::SetConnectionState(const std::string& connection_state) {
DCHECK(IsValidConnectionState(connection_state)) << connection_state;
if (connection_state == connection_state_) {
return;
}
const std::string prev_connection_state = connection_state_;
connection_state_ = connection_state;
if (StateIsConnected(connection_state_) ||
StateIsConnecting(prev_connection_state)) {
// If connected or previously connecting, clear |connect_requested_|.
connect_requested_ = false;
} else if (StateIsConnected(prev_connection_state) &&
StateIsConnecting(connection_state_)) {
// If transitioning from a connected state to a connecting state, set
// |connect_requested_| so that the UI knows the connecting state is
// important (i.e. not a normal auto connect).
connect_requested_ = true;
}
if (shill_portal_state_ == PortalState::kUnknown &&
connection_state_ == shill::kStateOnline) {
shill_portal_state_ = PortalState::kOnline;
}
chrome_portal_state_ = PortalState::kUnknown;
}
bool NetworkState::IsManagedByPolicy() const {
return onc_source_ == ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY ||
onc_source_ == ::onc::ONCSource::ONC_SOURCE_USER_POLICY;
}
bool NetworkState::IndicateRoaming() const {
return type() == shill::kTypeCellular &&
roaming_ == shill::kRoamingStateRoaming && !provider_requires_roaming_;
}
bool NetworkState::IsDynamicWep() const {
return security_class_ == shill::kSecurityClassWep &&
eap_key_mgmt_ == shill::kKeyManagementIEEE8021X;
}
bool NetworkState::IsConnectedState() const {
return visible() && StateIsConnected(connection_state_);
}
bool NetworkState::IsConnectingState() const {
return visible() &&
(connect_requested_ || StateIsConnecting(connection_state_));
}
bool NetworkState::IsConnectingOrConnected() const {
return visible() &&
(connect_requested_ || StateIsConnecting(connection_state_) ||
StateIsConnected(connection_state_));
}
bool NetworkState::IsOnline() const {
return connection_state() == shill::kStateOnline;
}
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::IsNonProfileType() const {
return type() == kTypeTether || IsNonShillCellularNetwork();
}
bool NetworkState::IsPrivate() const {
return !profile_path_.empty() &&
profile_path_ != NetworkProfileHandler::GetSharedProfilePath();
}
bool NetworkState::IsNonShillCellularNetwork() const {
return type() == shill::kTypeCellular &&
cellular_utils::IsStubCellularServicePath(path());
}
NetworkState::PortalState NetworkState::GetPortalState() const {
return chrome_portal_state_ != PortalState::kUnknown ? chrome_portal_state_
: shill_portal_state_;
}
void NetworkState::SetChromePortalState(PortalState portal_state) {
CHECK(!features::IsRemoveDetectPortalFromChromeEnabled());
chrome_portal_state_ = portal_state;
}
bool NetworkState::IsSecure() const {
return !security_class_.empty() &&
security_class_ != shill::kSecurityClassNone;
}
std::string NetworkState::GetHexSsid() const {
return base::HexEncode(raw_ssid());
}
std::string NetworkState::GetDnsServersAsString() const {
const base::Value::List* list =
ipv4_config_.has_value()
? ipv4_config_->FindList(shill::kNameServersProperty)
: nullptr;
if (!list) {
return std::string();
}
std::string result;
for (const auto& v : *list) {
if (!result.empty()) {
result += ",";
}
result += v.GetString();
}
return result;
}
std::string NetworkState::GetNetmask() const {
int prefixlen = ipv4_config_->FindInt(shill::kPrefixlenProperty).value_or(-1);
return network_util::PrefixLengthToNetmask(prefixlen);
}
std::string NetworkState::GetSpecifier() const {
if (!update_received()) {
NET_LOG(ERROR) << "GetSpecifier called before update: " << NetworkId(this);
return std::string();
}
if (type() == shill::kTypeWifi) {
return name() + "_" + security_class_;
}
if (type() == shill::kTypeCellular && !iccid().empty()) {
return iccid();
}
if (!name().empty()) {
return type() + "_" + name();
}
return type(); // For unnamed networks, i.e. Ethernet.
}
void NetworkState::SetGuid(const std::string& guid) {
guid_ = guid;
}
network_config::mojom::ActivationStateType
NetworkState::GetMojoActivationState() const {
using network_config::mojom::ActivationStateType;
if (activation_state_.empty()) {
return ActivationStateType::kUnknown;
}
if (activation_state_ == shill::kActivationStateActivated) {
return ActivationStateType::kActivated;
}
if (activation_state_ == shill::kActivationStateActivating) {
return ActivationStateType::kActivating;
}
if (activation_state_ == shill::kActivationStateNotActivated) {
return ActivationStateType::kNotActivated;
}
if (activation_state_ == shill::kActivationStatePartiallyActivated) {
return ActivationStateType::kPartiallyActivated;
}
NET_LOG(ERROR) << "Unexpected shill activation state: " << activation_state_;
return ActivationStateType::kUnknown;
}
network_config::mojom::SecurityType NetworkState::GetMojoSecurity() const {
using network_config::mojom::SecurityType;
if (!IsSecure()) {
return SecurityType::kNone;
}
if (IsDynamicWep()) {
return SecurityType::kWep8021x;
}
if (security_class_ == shill::kSecurityClassWep) {
return SecurityType::kWepPsk;
}
if (security_class_ == shill::kSecurityClassPsk) {
return SecurityType::kWpaPsk;
}
if (security_class_ == shill::kSecurityClass8021x) {
return SecurityType::kWpaEap;
}
NET_LOG(ERROR) << "Unsupported shill security class: " << security_class_;
return SecurityType::kNone;
}
NetworkState::NetworkTechnologyType NetworkState::GetNetworkTechnologyType()
const {
const std::string& network_type = type();
if (network_type == shill::kTypeCellular) {
return NetworkTechnologyType::kCellular;
}
if (network_type == shill::kTypeEthernet) {
return NetworkTechnologyType::kEthernet;
}
if (network_type == shill::kTypeEthernetEap) {
return NetworkTechnologyType::kEthernet;
}
if (network_type == kTypeTether) {
return NetworkTechnologyType::kTether;
}
if (network_type == shill::kTypeVPN) {
return NetworkTechnologyType::kVPN;
}
if (network_type == shill::kTypeWifi) {
return NetworkTechnologyType::kWiFi;
}
NOTREACHED_IN_MIGRATION() << "Unknown network type: " << network_type;
return NetworkTechnologyType::kUnknown;
}
// static
bool NetworkState::StateIsConnected(const std::string& connection_state) {
return (connection_state == shill::kStateReady ||
connection_state == shill::kStateOnline ||
StateIsPortalled(connection_state));
}
// static
bool NetworkState::StateIsConnecting(const std::string& connection_state) {
return (connection_state == shill::kStateAssociation ||
connection_state == shill::kStateConfiguration);
}
// static
bool NetworkState::StateIsPortalled(const std::string& connection_state) {
return (connection_state == shill::kStateNoConnectivity ||
connection_state == shill::kStateRedirectFound ||
connection_state == shill::kStatePortalSuspected);
}
// static
bool NetworkState::ErrorIsValid(const std::string& error) {
return !error.empty() && error != shill::kErrorNoFailure;
}
// static
std::unique_ptr<NetworkState> NetworkState::CreateNonShillCellularNetwork(
const std::string& iccid,
const std::string& eid,
const std::string& guid,
bool is_managed,
const std::string& cellular_device_path) {
std::string path = cellular_utils::GenerateStubCellularServicePath(iccid);
auto new_state = std::make_unique<NetworkState>(path);
new_state->set_type(shill::kTypeCellular);
new_state->set_update_received();
new_state->set_visible(true);
new_state->device_path_ = cellular_device_path;
new_state->iccid_ = iccid;
new_state->eid_ = eid;
new_state->guid_ = guid;
if (is_managed) {
new_state->onc_source_ = ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY;
}
new_state->activation_state_ = shill::kActivationStateActivated;
return new_state;
}
// Private methods.
bool NetworkState::UpdateName(const base::Value::Dict& properties) {
std::string updated_name =
shill_property_util::GetNameFromProperties(path(), properties);
if (updated_name != name()) {
set_name(updated_name);
return true;
}
return false;
}
void NetworkState::UpdateCaptivePortalState(
const base::Value::Dict& properties) {
if (!IsConnectedState()) {
// Unconnected networks are in an unknown portal state and should not
// update histograms.
shill_portal_state_ = PortalState::kUnknown;
return;
}
if (connection_state_ == shill::kStateNoConnectivity) {
shill_portal_state_ = PortalState::kNoInternet;
} else if (connection_state_ == shill::kStateRedirectFound) {
shill_portal_state_ = PortalState::kPortal;
} else if (connection_state_ == shill::kStatePortalSuspected) {
shill_portal_state_ = PortalState::kPortalSuspected;
} else if (connection_state_ == shill::kStateOnline) {
shill_portal_state_ = PortalState::kOnline;
} else {
shill_portal_state_ = PortalState::kUnknown;
}
base::UmaHistogramEnumeration("Network.CaptivePortalResult",
shill_portal_state_);
if (shill_portal_state_ != PortalState::kOnline) {
NET_LOG(EVENT) << "Shill captive portal state for: " << NetworkId(this)
<< " = " << shill_portal_state_;
}
}
void NetworkState::SetVpnProvider(const std::string& id,
const std::string& type) {
// |type| is required but |id| is only set for ThirdParty and Arc VPNs.
if (type.empty()) {
vpn_provider_ = nullptr;
return;
}
if (!vpn_provider_) {
vpn_provider_ = std::make_unique<VpnProviderInfo>();
}
vpn_provider_->id = id;
vpn_provider_->type = type;
}
std::ostream& operator<<(std::ostream& out,
const NetworkState::PortalState state) {
using State = NetworkState::PortalState;
switch (state) {
#define PRINT(s) \
case State::k##s: \
return out << #s;
PRINT(Unknown)
PRINT(Online)
PRINT(PortalSuspected)
PRINT(Portal)
PRINT(NoInternet)
#undef PRINT
}
return out << "PortalState("
<< static_cast<std::underlying_type_t<State>>(state) << ")";
}
std::ostream& operator<<(std::ostream& out,
const NetworkState::NetworkTechnologyType type) {
using Type = NetworkState::NetworkTechnologyType;
switch (type) {
#define PRINT(s) \
case Type::k##s: \
return out << #s;
PRINT(Cellular)
PRINT(Ethernet)
PRINT(EthernetEap)
PRINT(WiFi)
PRINT(Tether)
PRINT(VPN)
PRINT(Unknown)
#undef PRINT
}
return out << "NetworkTechnologyType("
<< static_cast<std::underlying_type_t<Type>>(type) << ")";
}
} // namespace ash