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