1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chromeos/network/network_state.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/stl_util.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "chromeos/network/network_profile_handler.h"
12 #include "chromeos/network/network_type_pattern.h"
13 #include "chromeos/network/network_util.h"
14 #include "chromeos/network/onc/onc_utils.h"
15 #include "chromeos/network/shill_property_util.h"
16 #include "components/device_event_log/device_event_log.h"
17 #include "third_party/cros_system_api/dbus/service_constants.h"
21 const char kErrorUnknown
[] = "Unknown";
23 bool ConvertListValueToStringVector(const base::ListValue
& string_list
,
24 std::vector
<std::string
>* result
) {
25 for (size_t i
= 0; i
< string_list
.GetSize(); ++i
) {
27 if (!string_list
.GetString(i
, &str
))
29 result
->push_back(str
);
34 bool IsCaptivePortalState(const base::DictionaryValue
& properties
, bool log
) {
36 properties
.GetStringWithoutPathExpansion(shill::kStateProperty
, &state
);
37 if (state
!= shill::kStatePortal
)
39 std::string portal_detection_phase
, portal_detection_status
;
40 if (!properties
.GetStringWithoutPathExpansion(
41 shill::kPortalDetectionFailedPhaseProperty
,
42 &portal_detection_phase
) ||
43 !properties
.GetStringWithoutPathExpansion(
44 shill::kPortalDetectionFailedStatusProperty
,
45 &portal_detection_status
)) {
46 // If Shill (or a stub) has not set PortalDetectionFailedStatus
47 // or PortalDetectionFailedPhase, assume we are in captive portal state.
51 // Shill reports the phase in which it determined that the device is behind a
52 // captive portal. We only want to rely only on incorrect content being
53 // returned and ignore other reasons.
54 bool is_captive_portal
=
55 portal_detection_phase
== shill::kPortalDetectionPhaseContent
&&
56 portal_detection_status
== shill::kPortalDetectionStatusFailure
;
60 properties
.GetStringWithoutPathExpansion(shill::kNameProperty
, &name
);
62 properties
.GetStringWithoutPathExpansion(shill::kSSIDProperty
, &name
);
63 if (!is_captive_portal
) {
64 NET_LOG(EVENT
) << "State is 'portal' but not in captive portal state:"
65 << " name=" << name
<< " phase=" << portal_detection_phase
66 << " status=" << portal_detection_status
;
68 NET_LOG(EVENT
) << "Network is in captive portal state: " << name
;
72 return is_captive_portal
;
79 NetworkState::NetworkState(const std::string
& path
)
80 : ManagedState(MANAGED_TYPE_NETWORK
, path
),
84 is_captive_portal_(false),
86 cellular_out_of_credits_(false) {
89 NetworkState::~NetworkState() {
92 bool NetworkState::PropertyChanged(const std::string
& key
,
93 const base::Value
& value
) {
94 // Keep care that these properties are the same as in |GetProperties|.
95 if (ManagedStatePropertyChanged(key
, value
))
97 if (key
== shill::kSignalStrengthProperty
) {
98 return GetIntegerValue(key
, value
, &signal_strength_
);
99 } else if (key
== shill::kStateProperty
) {
100 return GetStringValue(key
, value
, &connection_state_
);
101 } else if (key
== shill::kVisibleProperty
) {
102 return GetBooleanValue(key
, value
, &visible_
);
103 } else if (key
== shill::kConnectableProperty
) {
104 return GetBooleanValue(key
, value
, &connectable_
);
105 } else if (key
== shill::kErrorProperty
) {
106 if (!GetStringValue(key
, value
, &error_
))
108 if (ErrorIsValid(error_
))
109 last_error_
= error_
;
113 } else if (key
== shill::kActivationTypeProperty
) {
114 return GetStringValue(key
, value
, &activation_type_
);
115 } else if (key
== shill::kActivationStateProperty
) {
116 return GetStringValue(key
, value
, &activation_state_
);
117 } else if (key
== shill::kRoamingStateProperty
) {
118 return GetStringValue(key
, value
, &roaming_
);
119 } else if (key
== shill::kPaymentPortalProperty
) {
120 const base::DictionaryValue
* olp
;
121 if (!value
.GetAsDictionary(&olp
))
123 return olp
->GetStringWithoutPathExpansion(shill::kPaymentPortalURL
,
125 } else if (key
== shill::kSecurityClassProperty
) {
126 return GetStringValue(key
, value
, &security_class_
);
127 } else if (key
== shill::kEapMethodProperty
) {
128 return GetStringValue(key
, value
, &eap_method_
);
129 } else if (key
== shill::kNetworkTechnologyProperty
) {
130 return GetStringValue(key
, value
, &network_technology_
);
131 } else if (key
== shill::kDeviceProperty
) {
132 return GetStringValue(key
, value
, &device_path_
);
133 } else if (key
== shill::kGuidProperty
) {
134 return GetStringValue(key
, value
, &guid_
);
135 } else if (key
== shill::kProfileProperty
) {
136 return GetStringValue(key
, value
, &profile_path_
);
137 } else if (key
== shill::kWifiHexSsid
) {
138 std::string ssid_hex
;
139 if (!GetStringValue(key
, value
, &ssid_hex
)) {
143 return base::HexStringToBytes(ssid_hex
, &raw_ssid_
);
144 } else if (key
== shill::kOutOfCreditsProperty
) {
145 return GetBooleanValue(key
, value
, &cellular_out_of_credits_
);
146 } else if (key
== shill::kProxyConfigProperty
) {
147 std::string proxy_config_str
;
148 if (!value
.GetAsString(&proxy_config_str
)) {
149 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
153 proxy_config_
.Clear();
154 if (proxy_config_str
.empty())
157 scoped_ptr
<base::DictionaryValue
> proxy_config_dict(
158 onc::ReadDictionaryFromJson(proxy_config_str
));
159 if (proxy_config_dict
) {
160 // Warning: The DictionaryValue returned from
161 // ReadDictionaryFromJson/JSONParser is an optimized derived class that
162 // doesn't allow releasing ownership of nested values. A Swap in the wrong
163 // order leads to memory access errors.
164 proxy_config_
.MergeDictionary(proxy_config_dict
.get());
166 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
169 } else if (key
== shill::kProviderProperty
) {
170 std::string vpn_provider_type
;
171 const base::DictionaryValue
* dict
;
172 if (!value
.GetAsDictionary(&dict
) ||
173 !dict
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
174 &vpn_provider_type
)) {
175 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
179 if (vpn_provider_type
== shill::kProviderThirdPartyVpn
) {
180 // If the network uses a third-party VPN provider, copy over the
181 // provider's extension ID, which is held in |shill::kHostProperty|.
182 if (!dict
->GetStringWithoutPathExpansion(
183 shill::kHostProperty
, &third_party_vpn_provider_extension_id_
)) {
184 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
188 third_party_vpn_provider_extension_id_
.clear();
191 vpn_provider_type_
= vpn_provider_type
;
197 bool NetworkState::InitialPropertiesReceived(
198 const base::DictionaryValue
& properties
) {
199 NET_LOG(EVENT
) << "InitialPropertiesReceived: " << path() << ": " << name()
200 << " State: " << connection_state_
<< " Visible: " << visible_
;
201 if (!properties
.HasKey(shill::kTypeProperty
)) {
202 NET_LOG(ERROR
) << "NetworkState has no type: "
203 << shill_property_util::GetNetworkIdFromProperties(
208 // By convention, all visible WiFi and WiMAX networks have a
209 // SignalStrength > 0.
210 if ((type() == shill::kTypeWifi
|| type() == shill::kTypeWimax
) &&
211 visible() && signal_strength_
<= 0) {
212 signal_strength_
= 1;
215 // Any change to connection state will trigger a complete property update,
216 // so we update is_captive_portal_ here.
217 is_captive_portal_
= IsCaptivePortalState(properties
, true /* log */);
219 // Ensure that the network has a valid name.
220 return UpdateName(properties
);
223 void NetworkState::GetStateProperties(base::DictionaryValue
* dictionary
) const {
224 ManagedState::GetStateProperties(dictionary
);
226 // Properties shared by all types.
227 dictionary
->SetStringWithoutPathExpansion(shill::kGuidProperty
, guid());
228 dictionary
->SetStringWithoutPathExpansion(shill::kSecurityClassProperty
,
230 dictionary
->SetStringWithoutPathExpansion(shill::kProfileProperty
,
234 if (!error().empty())
235 dictionary
->SetStringWithoutPathExpansion(shill::kErrorProperty
, error());
236 dictionary
->SetStringWithoutPathExpansion(shill::kStateProperty
,
241 if (NetworkTypePattern::VPN().MatchesType(type())) {
242 // Shill sends VPN provider properties in a nested dictionary. |dictionary|
243 // must replicate that nested structure.
244 scoped_ptr
<base::DictionaryValue
> provider_property(
245 new base::DictionaryValue
);
246 provider_property
->SetStringWithoutPathExpansion(shill::kTypeProperty
,
248 if (vpn_provider_type_
== shill::kProviderThirdPartyVpn
) {
249 provider_property
->SetStringWithoutPathExpansion(
250 shill::kHostProperty
, third_party_vpn_provider_extension_id_
);
252 dictionary
->SetWithoutPathExpansion(shill::kProviderProperty
,
253 provider_property
.release());
256 // Wireless properties
257 if (!NetworkTypePattern::Wireless().MatchesType(type()))
261 dictionary
->SetBooleanWithoutPathExpansion(shill::kConnectableProperty
,
263 dictionary
->SetIntegerWithoutPathExpansion(shill::kSignalStrengthProperty
,
268 if (NetworkTypePattern::WiFi().MatchesType(type())) {
269 dictionary
->SetStringWithoutPathExpansion(shill::kEapMethodProperty
,
274 if (NetworkTypePattern::Mobile().MatchesType(type())) {
275 dictionary
->SetStringWithoutPathExpansion(shill::kNetworkTechnologyProperty
,
276 network_technology());
277 dictionary
->SetStringWithoutPathExpansion(shill::kActivationStateProperty
,
279 dictionary
->SetStringWithoutPathExpansion(shill::kRoamingStateProperty
,
281 dictionary
->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty
,
282 cellular_out_of_credits());
286 void NetworkState::IPConfigPropertiesChanged(
287 const base::DictionaryValue
& properties
) {
288 for (base::DictionaryValue::Iterator
iter(properties
); !iter
.IsAtEnd();
290 std::string key
= iter
.key();
291 const base::Value
& value
= iter
.value();
293 if (key
== shill::kAddressProperty
) {
294 GetStringValue(key
, value
, &ip_address_
);
295 } else if (key
== shill::kGatewayProperty
) {
296 GetStringValue(key
, value
, &gateway_
);
297 } else if (key
== shill::kNameServersProperty
) {
298 const base::ListValue
* dns_servers
;
299 if (value
.GetAsList(&dns_servers
)) {
300 dns_servers_
.clear();
301 ConvertListValueToStringVector(*dns_servers
, &dns_servers_
);
303 } else if (key
== shill::kPrefixlenProperty
) {
304 GetIntegerValue(key
, value
, &prefix_length_
);
305 } else if (key
== shill::kWebProxyAutoDiscoveryUrlProperty
) {
306 std::string url_string
;
307 if (GetStringValue(key
, value
, &url_string
)) {
308 if (url_string
.empty()) {
309 web_proxy_auto_discovery_url_
= GURL();
311 GURL
gurl(url_string
);
312 if (gurl
.is_valid()) {
313 web_proxy_auto_discovery_url_
= gurl
;
315 NET_LOG(ERROR
) << "Invalid WebProxyAutoDiscoveryUrl: " << path()
316 << ": " << url_string
;
317 web_proxy_auto_discovery_url_
= GURL();
325 bool NetworkState::RequiresActivation() const {
326 return (type() == shill::kTypeCellular
&&
327 activation_state() != shill::kActivationStateActivated
&&
328 activation_state() != shill::kActivationStateUnknown
);
331 std::string
NetworkState::connection_state() const {
333 return shill::kStateDisconnect
;
334 return connection_state_
;
337 bool NetworkState::IsConnectedState() const {
338 return visible() && StateIsConnected(connection_state_
);
341 bool NetworkState::IsConnectingState() const {
342 return visible() && StateIsConnecting(connection_state_
);
345 bool NetworkState::IsInProfile() const {
346 // kTypeEthernetEap is always saved. We need this check because it does
347 // not show up in the visible list, but its properties may not be available
348 // when it first shows up in ServiceCompleteList. See crbug.com/355117.
349 return !profile_path_
.empty() || type() == shill::kTypeEthernetEap
;
352 bool NetworkState::IsPrivate() const {
353 return !profile_path_
.empty() &&
354 profile_path_
!= NetworkProfileHandler::GetSharedProfilePath();
357 std::string
NetworkState::GetHexSsid() const {
358 return base::HexEncode(vector_as_array(&raw_ssid()), raw_ssid().size());
361 std::string
NetworkState::GetDnsServersAsString() const {
363 for (size_t i
= 0; i
< dns_servers_
.size(); ++i
) {
366 result
+= dns_servers_
[i
];
371 std::string
NetworkState::GetNetmask() const {
372 return network_util::PrefixLengthToNetmask(prefix_length_
);
375 std::string
NetworkState::GetSpecifier() const {
376 if (!update_received()) {
377 NET_LOG(ERROR
) << "GetSpecifier called before update: " << path();
378 return std::string();
380 if (type() == shill::kTypeWifi
)
381 return name() + "_" + security_class_
;
384 return type(); // For unnamed networks such as ethernet.
387 void NetworkState::SetGuid(const std::string
& guid
) {
391 bool NetworkState::UpdateName(const base::DictionaryValue
& properties
) {
392 std::string updated_name
=
393 shill_property_util::GetNameFromProperties(path(), properties
);
394 if (updated_name
!= name()) {
395 set_name(updated_name
);
402 bool NetworkState::StateIsConnected(const std::string
& connection_state
) {
403 return (connection_state
== shill::kStateReady
||
404 connection_state
== shill::kStateOnline
||
405 connection_state
== shill::kStatePortal
);
409 bool NetworkState::StateIsConnecting(const std::string
& connection_state
) {
410 return (connection_state
== shill::kStateAssociation
||
411 connection_state
== shill::kStateConfiguration
||
412 connection_state
== shill::kStateCarrier
);
416 bool NetworkState::NetworkStateIsCaptivePortal(
417 const base::DictionaryValue
& shill_properties
) {
418 return IsCaptivePortalState(shill_properties
, false /* log */);
422 bool NetworkState::ErrorIsValid(const std::string
& error
) {
423 // Shill uses "Unknown" to indicate an unset or cleared error state.
424 return !error
.empty() && error
!= kErrorUnknown
;
427 } // namespace chromeos