OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // WiFi card drivers for Linux implement the Wireless Extensions interface. | 5 // Provides wifi scan API binding for suitable for typical linux distributions. |
6 // This interface is part of the Linux kernel. | 6 // Currently, only the NetworkManager API is used, accessed via D-Bus (in turn |
7 // | 7 // accessed via the GLib wrapper). |
8 // Various sets of tools are available to manipulate the Wireless Extensions, | 8 |
9 // of which Wireless Tools is the default implementation. Wireless Tools | 9 #include "chrome/browser/geolocation/wifi_data_provider_linux.h" |
10 // provides a C++ library (libiw) as well as a set of command line tools | 10 |
11 // (iwconfig, iwlist etc). See | 11 #include <dbus/dbus-glib.h> |
12 // http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html for details. | 12 #include <glib.h> |
13 // | 13 |
14 // Ideally, we would use libiw to obtain WiFi data. However, Wireless Tools is | 14 #include "base/scoped_ptr.h" |
15 // released under GPL, which is not compatible with Gears. Furthermore, little | 15 #include "base/utf_string_conversions.h" |
16 // documentation is available for Wireless Extensions, so replicating libiw | 16 |
17 // without copying it directly would be difficult. | 17 namespace { |
18 // | 18 // The time periods between successive polls of the wifi data. |
19 // We therefore simply invoke iwlist (one of the Wireless Tools command line | 19 const int kDefaultPollingIntervalMilliseconds = 10 * 1000; // 10s |
20 // tools) and parse the output. Sample output is shown below. | 20 const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000; // 2 mins |
21 // | 21 const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000; // 10 mins |
22 // lo Interface doesn't support scanning. | 22 |
23 // | 23 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; |
24 // ath0 Scan completed : | 24 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; |
25 // Cell 01 - Address: 00:24:86:11:4C:42 | 25 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; |
26 // ESSID:"Test SSID" | 26 |
27 // Mode:Master | 27 // From http://projects.gnome.org/NetworkManager/developers/spec.html |
28 // Frequency:2.427 GHz (Channel 4) | 28 enum { NM_DEVICE_TYPE_WIFI = 2 }; |
29 // Quality=5/94 Signal level=-90 dBm Noise level=-95 dBm | 29 |
30 // Encryption key:off | 30 // Utility wrappers to make various GLib & DBus structs into scoped objects. |
31 // Bit Rates:1 Mb/s; 2 Mb/s; 5 Mb/s; 6 Mb/s; 9 Mb/s | 31 class ScopedGPtrArrayFree { |
32 // 11 Mb/s; 12 Mb/s; 18 Mb/s | 32 public: |
33 // Extra:bcn_int=100 | 33 void operator()(GPtrArray* x) const { |
34 // Cell 02 - Address: 00:24:86:11:6F:E2 | 34 if (x) |
35 // ESSID:"Test SSID" | 35 g_ptr_array_free(x, TRUE); |
36 // Mode:Master | 36 } |
37 // Frequency:2.447 GHz (Channel 8) | 37 }; |
38 // Quality=4/94 Signal level=-91 dBm Noise level=-95 dBm | 38 // Use ScopedGPtrArrayPtr as if it were scoped_ptr<GPtrArray> |
39 // Encryption key:off | 39 typedef scoped_ptr_malloc<GPtrArray, ScopedGPtrArrayFree> ScopedGPtrArrayPtr; |
40 // Bit Rates:1 Mb/s; 2 Mb/s; 5 Mb/s; 6 Mb/s; 9 Mb/s | 40 |
41 // 11 Mb/s; 12 Mb/s; 18 Mb/s | 41 class ScopedGObjectFree { |
42 // Extra:bcn_int=100 | 42 public: |
43 // | 43 void operator()(void* x) const { |
44 // TODO(steveblock): Investigate the possibility of the author of Wireless Tools | 44 if (x) |
45 // releasing libiw under a Gears-compatible license. | 45 g_object_unref(x); |
46 | 46 } |
47 // TODO(joth): port to chromium | 47 }; |
48 #if 0 | 48 // Use ScopedDBusGProxyPtr as if it were scoped_ptr<DBusGProxy> |
49 | 49 typedef scoped_ptr_malloc<DBusGProxy, ScopedGObjectFree> ScopedDBusGProxyPtr; |
50 #include "gears/geolocation/wifi_data_provider_linux.h" | 50 |
51 | 51 // Use ScopedGValue::v as an instance of GValue with automatic cleanup. |
52 #include <ctype.h> // For isxdigit() | 52 class ScopedGValue { |
53 #include <stdio.h> | 53 public: |
54 #include "gears/base/common/string_utils.h" | 54 ScopedGValue() |
55 #include "gears/geolocation/wifi_data_provider_common.h" | 55 : v(empty_gvalue()) { |
56 | 56 } |
57 // The time periods, in milliseconds, between successive polls of the wifi data. | 57 ~ScopedGValue() { |
58 extern const int kDefaultPollingInterval = 10000; // 10s | 58 g_value_unset(&v); |
59 extern const int kNoChangePollingInterval = 120000; // 2 mins | 59 } |
60 extern const int kTwoNoChangePollingInterval = 600000; // 10 mins | 60 static GValue empty_gvalue() { |
61 | 61 GValue value = {0}; |
62 // Local function | 62 return value; |
63 static bool GetAccessPointData(WifiData::AccessPointDataSet *access_points); | 63 } |
| 64 |
| 65 GValue v; |
| 66 }; |
| 67 |
| 68 // Wifi API binding to NetworkManager, to allow reuse of the polling behavior |
| 69 // defined in WifiDataProviderCommon. |
| 70 // TODO(joth): NetworkManager also allows for notification based handling, |
| 71 // however this will require reworking of the threading code to run a GLib |
| 72 // event loop (GMainLoop). |
| 73 class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface { |
| 74 public: |
| 75 NetworkManagerWlanApi(); |
| 76 ~NetworkManagerWlanApi(); |
| 77 |
| 78 // Must be called before any other interface method. Will return false if the |
| 79 // NetworkManager session cannot be created (e.g. not present on this distro), |
| 80 // in which case no other method may be called. |
| 81 bool Init(); |
| 82 |
| 83 // WifiDataProviderCommon::WlanApiInterface |
| 84 bool GetAccessPointData(WifiData::AccessPointDataSet* data); |
| 85 |
| 86 private: |
| 87 // Checks if the last dbus call returned an error. If it did, logs the error |
| 88 // message, frees it and returns true. |
| 89 // This must be called after every dbus call that accepts |&error_| |
| 90 bool CheckError(); |
| 91 |
| 92 // Enumerates the list of available network adapter devices known to |
| 93 // NetworkManager. Ownership of the array (and contained objects) is returned |
| 94 // to the caller. |
| 95 GPtrArray* GetAdapterDeviceList(); |
| 96 |
| 97 // Given the NetworkManager path to a wireless adapater, dumps the wifi scan |
| 98 // results and appends them to |data|. Returns false if a fatal error is |
| 99 // encountered such that the data set could not be populated. |
| 100 bool GetAccessPointsForAdapter(const gchar* adapter_path, |
| 101 WifiData::AccessPointDataSet* data); |
| 102 |
| 103 // Internal method used by |GetAccessPointsForAdapter|, given a wifi access |
| 104 // point proxy retrieves the named property into |value_out|. Returns false if |
| 105 // the property could not be read, or is not of type |expected_gvalue_type|. |
| 106 bool GetAccessPointProperty(DBusGProxy* proxy, const char* property_name, |
| 107 int expected_gvalue_type, GValue* value_out); |
| 108 |
| 109 // Error from the last dbus call. NULL when there's no error. Freed and |
| 110 // cleared by CheckError(). |
| 111 GError* error_; |
| 112 // Connection to the dbus system bus. |
| 113 DBusGConnection* connection_; |
| 114 // Proxy to the network maanger dbus service. |
| 115 ScopedDBusGProxyPtr proxy_; |
| 116 |
| 117 DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi); |
| 118 }; |
| 119 |
| 120 // Convert a wifi frequency to the corresponding channel. Adapted from |
| 121 // geolocaiton/wifilib.cc in googleclient (internal to google). |
| 122 int frquency_in_khz_to_channel(int frequency_khz) { |
| 123 if (frequency_khz >= 2412000 && frequency_khz <= 2472000) // Channels 1-13. |
| 124 return (frequency_khz - 2407000) / 5000; |
| 125 if (frequency_khz == 2484000) |
| 126 return 14; |
| 127 if (frequency_khz > 5000000 && frequency_khz < 6000000) // .11a bands. |
| 128 return (frequency_khz - 5000000) / 5000; |
| 129 // Ignore everything else. |
| 130 return AccessPointData().channel; // invalid channel |
| 131 } |
| 132 |
| 133 NetworkManagerWlanApi::NetworkManagerWlanApi() |
| 134 : error_(NULL), connection_(NULL) { |
| 135 } |
| 136 |
| 137 NetworkManagerWlanApi::~NetworkManagerWlanApi() { |
| 138 proxy_.reset(); |
| 139 if (connection_) { |
| 140 dbus_g_connection_unref(connection_); |
| 141 } |
| 142 DCHECK(!error_) << "Missing a call to CheckError() to clear |error_|"; |
| 143 } |
| 144 |
| 145 bool NetworkManagerWlanApi::Init() { |
| 146 // Chrome DLL init code handles initializing the thread system, so rather than |
| 147 // get caught up with that nonsense here, lets just assert our requirement. |
| 148 CHECK(g_thread_supported()); |
| 149 |
| 150 // We should likely do this higher up too, the docs say it must only be done |
| 151 // once but there's no way to know if it already was or not. |
| 152 dbus_g_thread_init(); |
| 153 |
| 154 // Get a connection to the session bus. |
| 155 connection_ = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error_); |
| 156 if (CheckError()) |
| 157 return false; |
| 158 DCHECK(connection_); |
| 159 |
| 160 proxy_.reset(dbus_g_proxy_new_for_name(connection_, |
| 161 kNetworkManagerServiceName, |
| 162 kNetworkManagerPath, |
| 163 kNetworkManagerInterface)); |
| 164 DCHECK(proxy_.get()); |
| 165 |
| 166 // Validate the proxy object by checking we can enumerate devices. |
| 167 ScopedGPtrArrayPtr device_list(GetAdapterDeviceList()); |
| 168 return !!device_list.get(); |
| 169 } |
| 170 |
| 171 bool NetworkManagerWlanApi::GetAccessPointData( |
| 172 WifiData::AccessPointDataSet* data) { |
| 173 ScopedGPtrArrayPtr device_list(GetAdapterDeviceList()); |
| 174 if (device_list == NULL) { |
| 175 DLOG(WARNING) << "Could not enumerate access points"; |
| 176 return false; |
| 177 } |
| 178 int success_count = 0; |
| 179 int fail_count = 0; |
| 180 |
| 181 // Iterate the devices, getting APs for each wireless adapter found |
| 182 for (guint i = 0; i < device_list->len; i++) { |
| 183 const gchar* device_path = |
| 184 reinterpret_cast<const gchar*>(g_ptr_array_index(device_list, i)); |
| 185 |
| 186 ScopedDBusGProxyPtr device_properties_proxy(dbus_g_proxy_new_from_proxy( |
| 187 proxy_.get(), DBUS_INTERFACE_PROPERTIES, device_path)); |
| 188 ScopedGValue device_type_g_value; |
| 189 dbus_g_proxy_call(device_properties_proxy.get(), "Get", &error_, |
| 190 G_TYPE_STRING, "org.freedesktop.NetworkManager.Device", |
| 191 G_TYPE_STRING, "DeviceType", |
| 192 G_TYPE_INVALID, |
| 193 G_TYPE_VALUE, &device_type_g_value.v, |
| 194 G_TYPE_INVALID); |
| 195 if (CheckError()) |
| 196 continue; |
| 197 |
| 198 const guint device_type = g_value_get_uint(&device_type_g_value.v); |
| 199 |
| 200 if (device_type == NM_DEVICE_TYPE_WIFI) { // Found a wlan adapter |
| 201 if (GetAccessPointsForAdapter(device_path, data)) |
| 202 ++success_count; |
| 203 else |
| 204 ++fail_count; |
| 205 } |
| 206 } |
| 207 // At least one successfull scan overrides any other adapter reporting error. |
| 208 return success_count || fail_count == 0; |
| 209 } |
| 210 |
| 211 bool NetworkManagerWlanApi::CheckError() { |
| 212 if (error_) { |
| 213 LOG(ERROR) << "Failed to complete NetworkManager call: " << error_->message; |
| 214 g_error_free(error_); |
| 215 error_ = NULL; |
| 216 return true; |
| 217 } |
| 218 return false; |
| 219 } |
| 220 |
| 221 GPtrArray* NetworkManagerWlanApi::GetAdapterDeviceList() { |
| 222 GPtrArray* device_list = NULL; |
| 223 dbus_g_proxy_call(proxy_.get(), "GetDevices", &error_, |
| 224 G_TYPE_INVALID, |
| 225 dbus_g_type_get_collection("GPtrArray", |
| 226 DBUS_TYPE_G_OBJECT_PATH), |
| 227 &device_list, |
| 228 G_TYPE_INVALID); |
| 229 if (CheckError()) |
| 230 return NULL; |
| 231 return device_list; |
| 232 } |
| 233 |
| 234 bool NetworkManagerWlanApi::GetAccessPointsForAdapter( |
| 235 const gchar* adapter_path, WifiData::AccessPointDataSet* data) { |
| 236 DCHECK(proxy_.get()); |
| 237 |
| 238 // Create a proxy object for this wifi adapter, and ask it to do a scan |
| 239 // (or at least, dump its scan results). |
| 240 ScopedDBusGProxyPtr wifi_adapter_proxy(dbus_g_proxy_new_from_proxy( |
| 241 proxy_.get(), "org.freedesktop.NetworkManager.Device.Wireless", |
| 242 adapter_path)); |
| 243 |
| 244 GPtrArray* ap_list_raw = NULL; |
| 245 // Enumerate the access points for this adapter. |
| 246 dbus_g_proxy_call(wifi_adapter_proxy.get(), "GetAccessPoints", &error_, |
| 247 G_TYPE_INVALID, |
| 248 dbus_g_type_get_collection("GPtrArray", |
| 249 DBUS_TYPE_G_OBJECT_PATH), |
| 250 &ap_list_raw, |
| 251 G_TYPE_INVALID); |
| 252 ScopedGPtrArrayPtr ap_list(ap_list_raw); // Takes ownership. |
| 253 ap_list_raw = NULL; |
| 254 |
| 255 if (CheckError()) |
| 256 return false; |
| 257 |
| 258 DLOG(INFO) << "Wireless adapter " << adapter_path << " found " |
| 259 << ap_list->len << " access points."; |
| 260 |
| 261 for (guint i = 0; i < ap_list->len; i++) { |
| 262 const gchar* ap_path = |
| 263 reinterpret_cast<const gchar*>(g_ptr_array_index(ap_list, i)); |
| 264 ScopedDBusGProxyPtr access_point_proxy(dbus_g_proxy_new_from_proxy( |
| 265 proxy_.get(), DBUS_INTERFACE_PROPERTIES, ap_path)); |
| 266 |
| 267 AccessPointData access_point_data; |
| 268 { // Read SSID. |
| 269 ScopedGValue ssid_g_value; |
| 270 if (!GetAccessPointProperty(access_point_proxy.get(), "Ssid", |
| 271 G_TYPE_BOXED, &ssid_g_value.v)) |
| 272 continue; |
| 273 const GArray* ssid = |
| 274 reinterpret_cast<const GArray*>(g_value_get_boxed(&ssid_g_value.v)); |
| 275 UTF8ToUTF16(ssid->data, ssid->len, &access_point_data.ssid); |
| 276 } |
| 277 |
| 278 { // Read the mac address |
| 279 ScopedGValue mac_g_value; |
| 280 if (!GetAccessPointProperty(access_point_proxy.get(), "HwAddress", |
| 281 G_TYPE_STRING, &mac_g_value.v)) |
| 282 continue; |
| 283 std::string mac = g_value_get_string(&mac_g_value.v); |
| 284 ReplaceSubstringsAfterOffset(&mac, 0U, ":", ""); |
| 285 std::vector<uint8> mac_bytes; |
| 286 if (!HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) { |
| 287 DLOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size() |
| 288 << " bytes) so using raw string: " << mac; |
| 289 access_point_data.mac_address = UTF8ToUTF16(mac); |
| 290 } else { |
| 291 access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]); |
| 292 } |
| 293 } |
| 294 |
| 295 { // Read signal strength. |
| 296 ScopedGValue signal_g_value; |
| 297 if (!GetAccessPointProperty(access_point_proxy.get(), "Strength", |
| 298 G_TYPE_UCHAR, &signal_g_value.v)) |
| 299 continue; |
| 300 // Convert strength as a percentage into dBs. |
| 301 access_point_data.radio_signal_strength = |
| 302 -100 + g_value_get_uchar(&signal_g_value.v) / 2; |
| 303 } |
| 304 |
| 305 { // Read the channel |
| 306 ScopedGValue freq_g_value; |
| 307 if (!GetAccessPointProperty(access_point_proxy.get(), "Frequency", |
| 308 G_TYPE_UINT, &freq_g_value.v)) |
| 309 continue; |
| 310 // NetworkManager returns frequency in MHz. |
| 311 access_point_data.channel = |
| 312 frquency_in_khz_to_channel(g_value_get_uint(&freq_g_value.v) * 1000); |
| 313 } |
| 314 data->insert(access_point_data); |
| 315 } |
| 316 return true; |
| 317 } |
| 318 |
| 319 bool NetworkManagerWlanApi::GetAccessPointProperty(DBusGProxy* proxy, |
| 320 const char* property_name, |
| 321 int expected_gvalue_type, |
| 322 GValue* value_out) { |
| 323 dbus_g_proxy_call(proxy, "Get", &error_, |
| 324 G_TYPE_STRING, "org.freedesktop.NetworkManager.AccessPoint", |
| 325 G_TYPE_STRING, property_name, |
| 326 G_TYPE_INVALID, |
| 327 G_TYPE_VALUE, value_out, |
| 328 G_TYPE_INVALID); |
| 329 if (CheckError()) |
| 330 return false; |
| 331 if (!G_VALUE_HOLDS(value_out, expected_gvalue_type)) { |
| 332 DLOG(WARNING) << "Property " << property_name << " unexptected type " |
| 333 << G_VALUE_TYPE(value_out); |
| 334 return false; |
| 335 } |
| 336 return true; |
| 337 } |
| 338 |
| 339 } // namespace |
64 | 340 |
65 // static | 341 // static |
66 template<> | 342 template<> |
67 WifiDataProviderImplBase *WifiDataProvider::DefaultFactoryFunction() { | 343 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { |
68 return new LinuxWifiDataProvider(); | 344 return new WifiDataProviderLinux(); |
69 } | 345 } |
70 | 346 |
71 | 347 WifiDataProviderLinux::WifiDataProviderLinux() { |
72 LinuxWifiDataProvider::LinuxWifiDataProvider() | 348 } |
73 : is_first_scan_complete_(false) { | 349 |
74 Start(); | 350 WifiDataProviderLinux::~WifiDataProviderLinux() { |
75 } | 351 } |
76 | 352 |
77 LinuxWifiDataProvider::~LinuxWifiDataProvider() { | 353 WifiDataProviderCommon::WlanApiInterface* |
78 stop_event_.Signal(); | 354 WifiDataProviderLinux::NewWlanApi() { |
79 Join(); | 355 scoped_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi); |
80 } | 356 if (wlan_api->Init()) |
81 | 357 return wlan_api.release(); |
82 bool LinuxWifiDataProvider::GetData(WifiData *data) { | 358 return NULL; |
83 DCHECK(data); | 359 } |
84 MutexLock lock(&data_mutex_); | 360 |
85 *data = wifi_data_; | 361 PollingPolicyInterface* WifiDataProviderLinux::NewPollingPolicy() { |
86 // If we've successfully completed a scan, indicate that we have all of the | 362 return new GenericPollingPolicy<kDefaultPollingIntervalMilliseconds, |
87 // data we can get. | 363 kNoChangePollingIntervalMilliseconds, |
88 return is_first_scan_complete_; | 364 kTwoNoChangePollingIntervalMilliseconds>; |
89 } | 365 } |
90 | 366 |
91 // Thread implementation | |
92 void LinuxWifiDataProvider::Run() { | |
93 // Regularly get the access point data. | |
94 int polling_interval = kDefaultPollingInterval; | |
95 do { | |
96 WifiData new_data; | |
97 if (GetAccessPointData(&new_data.access_point_data)) { | |
98 bool update_available; | |
99 data_mutex_.Lock(); | |
100 update_available = wifi_data_.DiffersSignificantly(new_data); | |
101 wifi_data_ = new_data; | |
102 data_mutex_.Unlock(); | |
103 polling_interval = | |
104 UpdatePollingInterval(polling_interval, update_available); | |
105 if (update_available) { | |
106 is_first_scan_complete_ = true; | |
107 NotifyListeners(); | |
108 } | |
109 } | |
110 } while (!stop_event_.WaitWithTimeout(polling_interval)); | |
111 } | |
112 | |
113 // Local functions | |
114 | |
115 static bool IsValidMacAddress(const char *mac_address) { | |
116 return isxdigit(mac_address[0]) && | |
117 isxdigit(mac_address[1]) && | |
118 mac_address[2] == ':' && | |
119 isxdigit(mac_address[3]) && | |
120 isxdigit(mac_address[4]) && | |
121 mac_address[5] == ':' && | |
122 isxdigit(mac_address[6]) && | |
123 isxdigit(mac_address[7]) && | |
124 mac_address[8] == ':' && | |
125 isxdigit(mac_address[9]) && | |
126 isxdigit(mac_address[10]) && | |
127 mac_address[11] == ':' && | |
128 isxdigit(mac_address[12]) && | |
129 isxdigit(mac_address[13]) && | |
130 mac_address[14] == ':' && | |
131 isxdigit(mac_address[15]) && | |
132 isxdigit(mac_address[16]); | |
133 } | |
134 | |
135 static void ParseLine(const std::string &line, | |
136 const std::string &mac_address_string, | |
137 const std::string &ssid_string, | |
138 const std::string &signal_strength_string, | |
139 AccessPointData *access_point_data) { | |
140 // Currently we get only MAC address, SSID and signal strength. | |
141 // TODO(steveblock): Work out how to get age, channel and signal-to-noise. | |
142 std::string::size_type index; | |
143 if ((index = line.find(mac_address_string)) != std::string::npos) { | |
144 // MAC address | |
145 if (IsValidMacAddress(&line.at(index + mac_address_string.size()))) { | |
146 UTF8ToString16(&line.at(index + mac_address_string.size()), | |
147 17, // XX:XX:XX:XX:XX:XX | |
148 &access_point_data->mac_address); | |
149 } | |
150 } else if ((index = line.find(ssid_string)) != std::string::npos) { | |
151 // SSID | |
152 // The string should be quoted. | |
153 std::string::size_type start = index + ssid_string.size() + 1; | |
154 std::string::size_type end = line.find('\"', start); | |
155 // If we can't find a trailing quote, something has gone wrong. | |
156 if (end != std::string::npos) { | |
157 UTF8ToString16(&line.at(start), end - start, &access_point_data->ssid); | |
158 } | |
159 } else if ((index = line.find(signal_strength_string)) != std::string::npos) { | |
160 // Signal strength | |
161 // iwlist will convert to dBm if it can. If it has failed to do so, we can't | |
162 // make use of the data. | |
163 if (line.find("dBm") != std::string::npos) { | |
164 // atoi will ignore trailing non-numeric characters | |
165 access_point_data->radio_signal_strength = | |
166 atoi(&line.at(index + signal_strength_string.size())); | |
167 } | |
168 } | |
169 } | |
170 | |
171 static void ParseAccessPoint(const std::string &text, | |
172 const std::string &mac_address_string, | |
173 const std::string &ssid_string, | |
174 const std::string &signal_strength_string, | |
175 AccessPointData *access_point_data) { | |
176 // Split response into lines to aid parsing. | |
177 std::string::size_type start = 0; | |
178 std::string::size_type end; | |
179 do { | |
180 end = text.find('\n', start); | |
181 std::string::size_type length = (end == std::string::npos) ? | |
182 std::string::npos : end - start; | |
183 ParseLine(text.substr(start, length), | |
184 mac_address_string, | |
185 ssid_string, | |
186 signal_strength_string, | |
187 access_point_data); | |
188 start = end + 1; | |
189 } while (end != std::string::npos); | |
190 } | |
191 | |
192 // Issues the specified command, and parses the response. Data for each access | |
193 // point is separated by the given delimiter. Within each block of data, the | |
194 // repsonse is split into lines and data is extracted by searching for the MAC | |
195 // address, SSID and signal strength strings. | |
196 bool IssueCommandAndParseResult(const char *command, | |
197 const char *delimiter, | |
198 const std::string &mac_address_string, | |
199 const std::string &ssid_string, | |
200 const std::string &signal_strength_string, | |
201 WifiData::AccessPointDataSet *access_points) { | |
202 // Open pipe in read mode. | |
203 FILE *result_pipe = popen(command, "r"); | |
204 if (result_pipe == NULL) { | |
205 LOG(("IssueCommand(): Failed to open pipe.\n")); | |
206 return false; | |
207 } | |
208 | |
209 // Read results of command. | |
210 static const int kBufferSize = 1024; | |
211 char buffer[kBufferSize]; | |
212 size_t bytes_read; | |
213 std::string result; | |
214 do { | |
215 bytes_read = fread(buffer, 1, kBufferSize, result_pipe); | |
216 result.append(buffer, bytes_read); | |
217 } while (static_cast<int>(bytes_read) == kBufferSize); | |
218 pclose(result_pipe); | |
219 | |
220 | |
221 // Parse results. | |
222 DCHECK(access_points); | |
223 access_points->clear(); | |
224 std::string::size_type start = result.find(delimiter); | |
225 while (start != std::string::npos) { | |
226 std::string::size_type end = result.find(delimiter, start + 1); | |
227 std::string::size_type length = (end == std::string::npos) ? | |
228 std::string::npos : end - start; | |
229 AccessPointData access_point_data; | |
230 ParseAccessPoint(result.substr(start, length), | |
231 mac_address_string, | |
232 ssid_string, | |
233 signal_strength_string, | |
234 &access_point_data); | |
235 access_points->insert(access_point_data); | |
236 start = end; | |
237 } | |
238 | |
239 return !access_points->empty(); | |
240 } | |
241 | |
242 static bool GetAccessPointData(WifiData::AccessPointDataSet *access_points) { | |
243 return IssueCommandAndParseResult("iwlist scan 2> /dev/null", | |
244 "Cell ", | |
245 "Address: ", | |
246 "ESSID:", | |
247 "Signal level=", | |
248 access_points) || | |
249 IssueCommandAndParseResult("iwconfig 2> /dev/null", | |
250 "ESSID:\"", | |
251 "Access Point: ", | |
252 "ESSID:", | |
253 "Signal level=", | |
254 access_points); | |
255 } | |
256 | |
257 #endif // 0 | |
OLD | NEW |