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
),
85 is_captive_portal_(false),
87 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::kPriorityProperty
) {
145 return GetIntegerValue(key
, value
, &priority_
);
146 } else if (key
== shill::kOutOfCreditsProperty
) {
147 return GetBooleanValue(key
, value
, &cellular_out_of_credits_
);
148 } else if (key
== shill::kProxyConfigProperty
) {
149 std::string proxy_config_str
;
150 if (!value
.GetAsString(&proxy_config_str
)) {
151 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
155 proxy_config_
.Clear();
156 if (proxy_config_str
.empty())
159 scoped_ptr
<base::DictionaryValue
> proxy_config_dict(
160 onc::ReadDictionaryFromJson(proxy_config_str
));
161 if (proxy_config_dict
) {
162 // Warning: The DictionaryValue returned from
163 // ReadDictionaryFromJson/JSONParser is an optimized derived class that
164 // doesn't allow releasing ownership of nested values. A Swap in the wrong
165 // order leads to memory access errors.
166 proxy_config_
.MergeDictionary(proxy_config_dict
.get());
168 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
171 } else if (key
== shill::kProviderProperty
) {
172 std::string vpn_provider_type
;
173 const base::DictionaryValue
* dict
;
174 if (!value
.GetAsDictionary(&dict
) ||
175 !dict
->GetStringWithoutPathExpansion(shill::kTypeProperty
,
176 &vpn_provider_type
)) {
177 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
181 if (vpn_provider_type
== shill::kProviderThirdPartyVpn
) {
182 // If the network uses a third-party VPN provider, copy over the
183 // provider's extension ID, which is held in |shill::kHostProperty|.
184 if (!dict
->GetStringWithoutPathExpansion(
185 shill::kHostProperty
, &third_party_vpn_provider_extension_id_
)) {
186 NET_LOG(ERROR
) << "Failed to parse " << path() << "." << key
;
190 third_party_vpn_provider_extension_id_
.clear();
193 vpn_provider_type_
= vpn_provider_type
;
199 bool NetworkState::InitialPropertiesReceived(
200 const base::DictionaryValue
& properties
) {
201 NET_LOG(EVENT
) << "InitialPropertiesReceived: " << path() << ": " << name()
202 << " State: " << connection_state_
<< " Visible: " << visible_
;
203 if (!properties
.HasKey(shill::kTypeProperty
)) {
204 NET_LOG(ERROR
) << "NetworkState has no type: "
205 << shill_property_util::GetNetworkIdFromProperties(
210 // By convention, all visible WiFi and WiMAX networks have a
211 // SignalStrength > 0.
212 if ((type() == shill::kTypeWifi
|| type() == shill::kTypeWimax
) &&
213 visible() && signal_strength_
<= 0) {
214 signal_strength_
= 1;
217 // Any change to connection state will trigger a complete property update,
218 // so we update is_captive_portal_ here.
219 is_captive_portal_
= IsCaptivePortalState(properties
, true /* log */);
221 // Ensure that the network has a valid name.
222 return UpdateName(properties
);
225 void NetworkState::GetStateProperties(base::DictionaryValue
* dictionary
) const {
226 ManagedState::GetStateProperties(dictionary
);
228 // Properties shared by all types.
229 dictionary
->SetStringWithoutPathExpansion(shill::kGuidProperty
, guid());
230 dictionary
->SetStringWithoutPathExpansion(shill::kSecurityClassProperty
,
232 dictionary
->SetStringWithoutPathExpansion(shill::kProfileProperty
,
234 dictionary
->SetIntegerWithoutPathExpansion(shill::kPriorityProperty
,
238 dictionary
->SetStringWithoutPathExpansion(shill::kStateProperty
,
243 if (NetworkTypePattern::VPN().MatchesType(type())) {
244 // Shill sends VPN provider properties in a nested dictionary. |dictionary|
245 // must replicate that nested structure.
246 scoped_ptr
<base::DictionaryValue
> provider_property(
247 new base::DictionaryValue
);
248 provider_property
->SetStringWithoutPathExpansion(shill::kTypeProperty
,
250 if (vpn_provider_type_
== shill::kProviderThirdPartyVpn
) {
251 provider_property
->SetStringWithoutPathExpansion(
252 shill::kHostProperty
, third_party_vpn_provider_extension_id_
);
254 dictionary
->SetWithoutPathExpansion(shill::kProviderProperty
,
255 provider_property
.release());
258 // Wireless properties
259 if (!NetworkTypePattern::Wireless().MatchesType(type()))
263 dictionary
->SetBooleanWithoutPathExpansion(shill::kConnectableProperty
,
265 dictionary
->SetIntegerWithoutPathExpansion(shill::kSignalStrengthProperty
,
270 if (NetworkTypePattern::WiFi().MatchesType(type())) {
271 dictionary
->SetStringWithoutPathExpansion(shill::kEapMethodProperty
,
276 if (NetworkTypePattern::Mobile().MatchesType(type())) {
277 dictionary
->SetStringWithoutPathExpansion(shill::kNetworkTechnologyProperty
,
278 network_technology());
279 dictionary
->SetStringWithoutPathExpansion(shill::kActivationStateProperty
,
281 dictionary
->SetStringWithoutPathExpansion(shill::kRoamingStateProperty
,
283 dictionary
->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty
,
284 cellular_out_of_credits());
288 void NetworkState::IPConfigPropertiesChanged(
289 const base::DictionaryValue
& properties
) {
290 for (base::DictionaryValue::Iterator
iter(properties
); !iter
.IsAtEnd();
292 std::string key
= iter
.key();
293 const base::Value
& value
= iter
.value();
295 if (key
== shill::kAddressProperty
) {
296 GetStringValue(key
, value
, &ip_address_
);
297 } else if (key
== shill::kGatewayProperty
) {
298 GetStringValue(key
, value
, &gateway_
);
299 } else if (key
== shill::kNameServersProperty
) {
300 const base::ListValue
* dns_servers
;
301 if (value
.GetAsList(&dns_servers
)) {
302 dns_servers_
.clear();
303 ConvertListValueToStringVector(*dns_servers
, &dns_servers_
);
305 } else if (key
== shill::kPrefixlenProperty
) {
306 GetIntegerValue(key
, value
, &prefix_length_
);
307 } else if (key
== shill::kWebProxyAutoDiscoveryUrlProperty
) {
308 std::string url_string
;
309 if (GetStringValue(key
, value
, &url_string
)) {
310 if (url_string
.empty()) {
311 web_proxy_auto_discovery_url_
= GURL();
313 GURL
gurl(url_string
);
314 if (gurl
.is_valid()) {
315 web_proxy_auto_discovery_url_
= gurl
;
317 NET_LOG(ERROR
) << "Invalid WebProxyAutoDiscoveryUrl: " << path()
318 << ": " << url_string
;
319 web_proxy_auto_discovery_url_
= GURL();
327 bool NetworkState::RequiresActivation() const {
328 return (type() == shill::kTypeCellular
&&
329 activation_state() != shill::kActivationStateActivated
&&
330 activation_state() != shill::kActivationStateUnknown
);
333 std::string
NetworkState::connection_state() const {
335 return shill::kStateDisconnect
;
336 return connection_state_
;
339 bool NetworkState::IsConnectedState() const {
340 return visible() && StateIsConnected(connection_state_
);
343 bool NetworkState::IsConnectingState() const {
344 return visible() && StateIsConnecting(connection_state_
);
347 bool NetworkState::IsInProfile() const {
348 // kTypeEthernetEap is always saved. We need this check because it does
349 // not show up in the visible list, but its properties may not be available
350 // when it first shows up in ServiceCompleteList. See crbug.com/355117.
351 return !profile_path_
.empty() || type() == shill::kTypeEthernetEap
;
354 bool NetworkState::IsPrivate() const {
355 return !profile_path_
.empty() &&
356 profile_path_
!= NetworkProfileHandler::GetSharedProfilePath();
359 std::string
NetworkState::GetHexSsid() const {
360 return base::HexEncode(vector_as_array(&raw_ssid()), raw_ssid().size());
363 std::string
NetworkState::GetDnsServersAsString() const {
365 for (size_t i
= 0; i
< dns_servers_
.size(); ++i
) {
368 result
+= dns_servers_
[i
];
373 std::string
NetworkState::GetNetmask() const {
374 return network_util::PrefixLengthToNetmask(prefix_length_
);
377 std::string
NetworkState::GetSpecifier() const {
378 if (!update_received()) {
379 NET_LOG(ERROR
) << "GetSpecifier called before update: " << path();
380 return std::string();
382 if (type() == shill::kTypeWifi
)
383 return name() + "_" + security_class_
;
386 return type(); // For unnamed networks such as ethernet.
389 void NetworkState::SetGuid(const std::string
& guid
) {
393 bool NetworkState::UpdateName(const base::DictionaryValue
& properties
) {
394 std::string updated_name
=
395 shill_property_util::GetNameFromProperties(path(), properties
);
396 if (updated_name
!= name()) {
397 set_name(updated_name
);
403 std::string
NetworkState::GetErrorState() const {
404 if (ErrorIsValid(error()))
410 bool NetworkState::StateIsConnected(const std::string
& connection_state
) {
411 return (connection_state
== shill::kStateReady
||
412 connection_state
== shill::kStateOnline
||
413 connection_state
== shill::kStatePortal
);
417 bool NetworkState::StateIsConnecting(const std::string
& connection_state
) {
418 return (connection_state
== shill::kStateAssociation
||
419 connection_state
== shill::kStateConfiguration
||
420 connection_state
== shill::kStateCarrier
);
424 bool NetworkState::NetworkStateIsCaptivePortal(
425 const base::DictionaryValue
& shill_properties
) {
426 return IsCaptivePortalState(shill_properties
, false /* log */);
430 bool NetworkState::ErrorIsValid(const std::string
& error
) {
431 // Shill uses "Unknown" to indicate an unset or cleared error state.
432 return !error
.empty() && error
!= kErrorUnknown
;
435 } // namespace chromeos