network_state.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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. 4 5#include "chromeos/network/network_state.h" 6 7#include "base/i18n/icu_encoding_detection.h" 8#include "base/i18n/icu_string_conversions.h" 9#include "base/string_util.h" 10#include "base/stringprintf.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/utf_string_conversion_utils.h" 13#include "base/values.h" 14#include "chromeos/network/network_event_log.h" 15#include "third_party/cros_system_api/dbus/service_constants.h" 16 17namespace { 18 19const char kLogModule[] = "NetworkState"; 20 21bool ConvertListValueToStringVector(const base::ListValue& string_list, 22 std::vector<std::string>* result) { 23 for (size_t i = 0; i < string_list.GetSize(); ++i) { 24 std::string str; 25 if (!string_list.GetString(i, &str)) 26 return false; 27 result->push_back(str); 28 } 29 return true; 30} 31 32// Replace non UTF8 characters in |str| with a replacement character. 33std::string ValidateUTF8(const std::string& str) { 34 std::string result; 35 for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) { 36 uint32 code_point_out; 37 bool is_unicode_char = base::ReadUnicodeCharacter(str.c_str(), str.size(), 38 &index, &code_point_out); 39 const uint32 kFirstNonControlChar = 0x20; 40 if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) { 41 base::WriteUnicodeCharacter(code_point_out, &result); 42 } else { 43 const uint32 kReplacementChar = 0xFFFD; 44 // Puts kReplacementChar if character is a control character [0,0x20) 45 // or is not readable UTF8. 46 base::WriteUnicodeCharacter(kReplacementChar, &result); 47 } 48 } 49 return result; 50} 51 52} // namespace 53 54namespace chromeos { 55 56NetworkState::NetworkState(const std::string& path) 57 : ManagedState(MANAGED_TYPE_NETWORK, path), 58 auto_connect_(false), 59 favorite_(false), 60 priority_(0), 61 signal_strength_(0), 62 activate_over_non_cellular_networks_(false), 63 cellular_out_of_credits_(false) { 64} 65 66NetworkState::~NetworkState() { 67} 68 69bool NetworkState::PropertyChanged(const std::string& key, 70 const base::Value& value) { 71 // Keep care that these properties are the same as in |GetProperties|. 72 if (ManagedStatePropertyChanged(key, value)) 73 return true; 74 if (key == flimflam::kSignalStrengthProperty) { 75 return GetIntegerValue(key, value, &signal_strength_); 76 } else if (key == flimflam::kStateProperty) { 77 return GetStringValue(key, value, &connection_state_); 78 } else if (key == flimflam::kErrorProperty) { 79 return GetStringValue(key, value, &error_); 80 } else if (key == IPConfigProperty(flimflam::kAddressProperty)) { 81 return GetStringValue(key, value, &ip_address_); 82 } else if (key == IPConfigProperty(flimflam::kNameServersProperty)) { 83 dns_servers_.clear(); 84 const base::ListValue* dns_servers; 85 if (value.GetAsList(&dns_servers) && 86 ConvertListValueToStringVector(*dns_servers, &dns_servers_)) 87 return true; 88 } else if (key == flimflam::kActivationStateProperty) { 89 return GetStringValue(key, value, &activation_state_); 90 } else if (key == flimflam::kRoamingStateProperty) { 91 return GetStringValue(key, value, &roaming_); 92 } else if (key == flimflam::kSecurityProperty) { 93 return GetStringValue(key, value, &security_); 94 } else if (key == flimflam::kAutoConnectProperty) { 95 return GetBooleanValue(key, value, &auto_connect_); 96 } else if (key == flimflam::kFavoriteProperty) { 97 return GetBooleanValue(key, value, &favorite_); 98 } else if (key == flimflam::kPriorityProperty) { 99 return GetIntegerValue(key, value, &priority_); 100 } else if (key == flimflam::kNetworkTechnologyProperty) { 101 return GetStringValue(key, value, &technology_); 102 } else if (key == flimflam::kDeviceProperty) { 103 return GetStringValue(key, value, &device_path_); 104 } else if (key == flimflam::kGuidProperty) { 105 return GetStringValue(key, value, &guid_); 106 } else if (key == flimflam::kProfileProperty) { 107 return GetStringValue(key, value, &profile_path_); 108 } else if (key == shill::kActivateOverNonCellularNetworkProperty) { 109 return GetBooleanValue(key, value, &activate_over_non_cellular_networks_); 110 } else if (key == shill::kOutOfCreditsProperty) { 111 return GetBooleanValue(key, value, &cellular_out_of_credits_); 112 } else if (key == flimflam::kWifiHexSsid) { 113 return GetStringValue(key, value, &hex_ssid_); 114 } else if (key == flimflam::kCountryProperty) { 115 // TODO(stevenjb): This is currently experimental. If we find a case where 116 // base::DetectEncoding() fails in UpdateName(), where country_code_ is 117 // set, figure out whether we can use country_code_ with ConvertToUtf8(). 118 // crbug.com/233267. 119 return GetStringValue(key, value, &country_code_); 120 } 121 return false; 122} 123 124void NetworkState::InitialPropertiesReceived() { 125 UpdateName(); 126} 127 128void NetworkState::GetProperties(base::DictionaryValue* dictionary) const { 129 // Keep care that these properties are the same as in |PropertyChanged|. 130 dictionary->SetStringWithoutPathExpansion(flimflam::kNameProperty, name()); 131 dictionary->SetStringWithoutPathExpansion(flimflam::kTypeProperty, type()); 132 dictionary->SetIntegerWithoutPathExpansion(flimflam::kSignalStrengthProperty, 133 signal_strength()); 134 dictionary->SetStringWithoutPathExpansion(flimflam::kStateProperty, 135 connection_state()); 136 dictionary->SetStringWithoutPathExpansion(flimflam::kErrorProperty, 137 error()); 138 base::DictionaryValue* ipconfig_properties = new DictionaryValue; 139 ipconfig_properties->SetStringWithoutPathExpansion(flimflam::kAddressProperty, 140 ip_address()); 141 base::ListValue* name_servers = new ListValue; 142 name_servers->AppendStrings(dns_servers()); 143 ipconfig_properties->SetWithoutPathExpansion(flimflam::kNameServersProperty, 144 name_servers); 145 dictionary->SetWithoutPathExpansion(shill::kIPConfigProperty, 146 ipconfig_properties); 147 148 dictionary->SetStringWithoutPathExpansion(flimflam::kActivationStateProperty, 149 activation_state()); 150 dictionary->SetStringWithoutPathExpansion(flimflam::kRoamingStateProperty, 151 roaming()); 152 dictionary->SetStringWithoutPathExpansion(flimflam::kSecurityProperty, 153 security()); 154 dictionary->SetBooleanWithoutPathExpansion(flimflam::kAutoConnectProperty, 155 auto_connect()); 156 dictionary->SetBooleanWithoutPathExpansion(flimflam::kFavoriteProperty, 157 favorite()); 158 dictionary->SetIntegerWithoutPathExpansion(flimflam::kPriorityProperty, 159 priority()); 160 dictionary->SetStringWithoutPathExpansion( 161 flimflam::kNetworkTechnologyProperty, 162 technology()); 163 dictionary->SetStringWithoutPathExpansion(flimflam::kDeviceProperty, 164 device_path()); 165 dictionary->SetStringWithoutPathExpansion(flimflam::kGuidProperty, guid()); 166 dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, 167 profile_path()); 168 dictionary->SetBooleanWithoutPathExpansion( 169 shill::kActivateOverNonCellularNetworkProperty, 170 activate_over_non_cellular_networks()); 171 dictionary->SetBooleanWithoutPathExpansion(shill::kOutOfCreditsProperty, 172 cellular_out_of_credits()); 173} 174 175bool NetworkState::IsConnectedState() const { 176 return StateIsConnected(connection_state_); 177} 178 179bool NetworkState::IsConnectingState() const { 180 return StateIsConnecting(connection_state_); 181} 182 183void NetworkState::UpdateName() { 184 if (hex_ssid_.empty()) { 185 // Validate name for UTF8. 186 std::string valid_ssid = ValidateUTF8(name()); 187 if (valid_ssid != name()) { 188 set_name(valid_ssid); 189 network_event_log::AddEntry( 190 kLogModule, "UpdateName", 191 base::StringPrintf("%s: UTF8: %s", path().c_str(), name().c_str())); 192 } 193 return; 194 } 195 196 std::string ssid; 197 std::vector<uint8> raw_ssid_bytes; 198 if (base::HexStringToBytes(hex_ssid_, &raw_ssid_bytes)) { 199 ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end()); 200 } else { 201 std::string desc = base::StringPrintf("%s: Error processing: %s", 202 path().c_str(), hex_ssid_.c_str()); 203 network_event_log::AddEntry(kLogModule, "UpdateName", desc); 204 LOG(ERROR) << desc; 205 ssid = name(); 206 } 207 208 if (IsStringUTF8(ssid)) { 209 if (ssid != name()) { 210 set_name(ssid); 211 network_event_log::AddEntry( 212 kLogModule, "UpdateName", 213 base::StringPrintf("%s: UTF8: %s", path().c_str(), name().c_str())); 214 } 215 return; 216 } 217 218 // Detect encoding and convert to UTF-8. 219 std::string encoding; 220 if (!base::DetectEncoding(ssid, &encoding)) { 221 // TODO(stevenjb): Test this. See comment in PropertyChanged() under 222 // flimflam::kCountryProperty. 223 encoding = country_code_; 224 } 225 if (!encoding.empty()) { 226 std::string utf8_ssid; 227 if (base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { 228 set_name(utf8_ssid); 229 network_event_log::AddEntry( 230 kLogModule, "UpdateName", 231 base::StringPrintf("%s: Encoding=%s: %s", path().c_str(), 232 encoding.c_str(), name().c_str())); 233 return; 234 } 235 } 236 237 // Unrecognized encoding. Only use raw bytes if name_ is empty. 238 if (name().empty()) 239 set_name(ssid); 240 network_event_log::AddEntry( 241 kLogModule, "UpdateName", 242 base::StringPrintf("%s: Unrecognized Encoding=%s: %s", path().c_str(), 243 encoding.c_str(), name().c_str())); 244} 245 246// static 247bool NetworkState::StateIsConnected(const std::string& connection_state) { 248 return (connection_state == flimflam::kStateReady || 249 connection_state == flimflam::kStateOnline || 250 connection_state == flimflam::kStatePortal); 251} 252 253// static 254bool NetworkState::StateIsConnecting(const std::string& connection_state) { 255 return (connection_state == flimflam::kStateAssociation || 256 connection_state == flimflam::kStateConfiguration || 257 connection_state == flimflam::kStateCarrier); 258} 259 260// static 261std::string NetworkState::IPConfigProperty(const char* key) { 262 return base::StringPrintf("%s.%s", shill::kIPConfigProperty, key); 263} 264 265} // namespace chromeos 266