| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2008, Google Inc. |
| 2 // |
| 3 // Redistribution and use in source and binary forms, with or without |
| 4 // modification, are permitted provided that the following conditions are met: |
| 5 // |
| 6 // 1. Redistributions of source code must retain the above copyright notice, |
| 7 // this list of conditions and the following disclaimer. |
| 8 // 2. Redistributions in binary form must reproduce the above copyright notice, |
| 9 // this list of conditions and the following disclaimer in the documentation |
| 10 // and/or other materials provided with the distribution. |
| 11 // 3. Neither the name of Google Inc. nor the names of its contributors may be |
| 12 // used to endorse or promote products derived from this software without |
| 13 // specific prior written permission. |
| 14 // |
| 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
| 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 // |
| 26 // WiFi card drivers for Linux implement the Wireless Extensions interface. |
| 27 // This interface is part of the Linux kernel. |
| 28 // |
| 29 // Various sets of tools are available to manipulate the Wireless Extensions, |
| 30 // of which Wireless Tools is the default implementation. Wireless Tools |
| 31 // provides a C++ library (libiw) as well as a set of command line tools |
| 32 // (iwconfig, iwlist etc). See |
| 33 // http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html for details. |
| 34 // |
| 35 // Ideally, we would use libiw to obtain WiFi data. However, Wireless Tools is |
| 36 // released under GPL, which is not compatible with Gears. Furthermore, little |
| 37 // documentation is available for Wireless Extensions, so replicating libiw |
| 38 // without copying it directly would be difficult. |
| 39 // |
| 40 // We therefore simply invoke iwlist (one of the Wireless Tools command line |
| 41 // tools) and parse the output. Sample output is shown below. |
| 42 // |
| 43 // lo Interface doesn't support scanning. |
| 44 // |
| 45 // ath0 Scan completed : |
| 46 // Cell 01 - Address: 00:24:86:11:4C:42 |
| 47 // ESSID:"Test SSID" |
| 48 // Mode:Master |
| 49 // Frequency:2.427 GHz (Channel 4) |
| 50 // Quality=5/94 Signal level=-90 dBm Noise level=-95 dBm |
| 51 // Encryption key:off |
| 52 // Bit Rates:1 Mb/s; 2 Mb/s; 5 Mb/s; 6 Mb/s; 9 Mb/s |
| 53 // 11 Mb/s; 12 Mb/s; 18 Mb/s |
| 54 // Extra:bcn_int=100 |
| 55 // Cell 02 - Address: 00:24:86:11:6F:E2 |
| 56 // ESSID:"Test SSID" |
| 57 // Mode:Master |
| 58 // Frequency:2.447 GHz (Channel 8) |
| 59 // Quality=4/94 Signal level=-91 dBm Noise level=-95 dBm |
| 60 // Encryption key:off |
| 61 // Bit Rates:1 Mb/s; 2 Mb/s; 5 Mb/s; 6 Mb/s; 9 Mb/s |
| 62 // 11 Mb/s; 12 Mb/s; 18 Mb/s |
| 63 // Extra:bcn_int=100 |
| 64 // |
| 65 // TODO(steveblock): Investigate the possibility of the author of Wireless Tools |
| 66 // releasing libiw under a Gears-compatible license. |
| 67 |
| 68 // TODO(cprince): remove platform-specific #ifdef guards when OS-specific |
| 69 // sources (e.g. WIN32_CPPSRCS) are implemented |
| 70 #if defined(LINUX) && !defined(OS_MACOSX) |
| 71 |
| 72 #include "gears/geolocation/wifi_data_provider_linux.h" |
| 73 |
| 74 #include <ctype.h> // For isxdigit() |
| 75 #include <stdio.h> |
| 76 #include "gears/base/common/string_utils.h" |
| 77 #include "gears/geolocation/wifi_data_provider_common.h" |
| 78 |
| 79 // The time periods, in milliseconds, between successive polls of the wifi data. |
| 80 extern const int kDefaultPollingInterval = 10000; // 10s |
| 81 extern const int kNoChangePollingInterval = 120000; // 2 mins |
| 82 extern const int kTwoNoChangePollingInterval = 600000; // 10 mins |
| 83 |
| 84 // Local function |
| 85 static bool GetAccessPointData(WifiData::AccessPointDataSet *access_points); |
| 86 |
| 87 // static |
| 88 template<> |
| 89 WifiDataProviderImplBase *WifiDataProvider::DefaultFactoryFunction() { |
| 90 return new LinuxWifiDataProvider(); |
| 91 } |
| 92 |
| 93 |
| 94 LinuxWifiDataProvider::LinuxWifiDataProvider() |
| 95 : is_first_scan_complete_(false) { |
| 96 Start(); |
| 97 } |
| 98 |
| 99 LinuxWifiDataProvider::~LinuxWifiDataProvider() { |
| 100 stop_event_.Signal(); |
| 101 Join(); |
| 102 } |
| 103 |
| 104 bool LinuxWifiDataProvider::GetData(WifiData *data) { |
| 105 assert(data); |
| 106 MutexLock lock(&data_mutex_); |
| 107 *data = wifi_data_; |
| 108 // If we've successfully completed a scan, indicate that we have all of the |
| 109 // data we can get. |
| 110 return is_first_scan_complete_; |
| 111 } |
| 112 |
| 113 // Thread implementation |
| 114 void LinuxWifiDataProvider::Run() { |
| 115 // Regularly get the access point data. |
| 116 int polling_interval = kDefaultPollingInterval; |
| 117 do { |
| 118 WifiData new_data; |
| 119 if (GetAccessPointData(&new_data.access_point_data)) { |
| 120 bool update_available; |
| 121 data_mutex_.Lock(); |
| 122 update_available = wifi_data_.DiffersSignificantly(new_data); |
| 123 wifi_data_ = new_data; |
| 124 data_mutex_.Unlock(); |
| 125 polling_interval = |
| 126 UpdatePollingInterval(polling_interval, update_available); |
| 127 if (update_available) { |
| 128 is_first_scan_complete_ = true; |
| 129 NotifyListeners(); |
| 130 } |
| 131 } |
| 132 } while (!stop_event_.WaitWithTimeout(polling_interval)); |
| 133 } |
| 134 |
| 135 // Local functions |
| 136 |
| 137 static bool IsValidMacAddress(const char *mac_address) { |
| 138 return isxdigit(mac_address[0]) && |
| 139 isxdigit(mac_address[1]) && |
| 140 mac_address[2] == ':' && |
| 141 isxdigit(mac_address[3]) && |
| 142 isxdigit(mac_address[4]) && |
| 143 mac_address[5] == ':' && |
| 144 isxdigit(mac_address[6]) && |
| 145 isxdigit(mac_address[7]) && |
| 146 mac_address[8] == ':' && |
| 147 isxdigit(mac_address[9]) && |
| 148 isxdigit(mac_address[10]) && |
| 149 mac_address[11] == ':' && |
| 150 isxdigit(mac_address[12]) && |
| 151 isxdigit(mac_address[13]) && |
| 152 mac_address[14] == ':' && |
| 153 isxdigit(mac_address[15]) && |
| 154 isxdigit(mac_address[16]); |
| 155 } |
| 156 |
| 157 static void ParseLine(const std::string &line, |
| 158 const std::string &mac_address_string, |
| 159 const std::string &ssid_string, |
| 160 const std::string &signal_strength_string, |
| 161 AccessPointData *access_point_data) { |
| 162 // Currently we get only MAC address, SSID and signal strength. |
| 163 // TODO(steveblock): Work out how to get age, channel and signal-to-noise. |
| 164 std::string::size_type index; |
| 165 if ((index = line.find(mac_address_string)) != std::string::npos) { |
| 166 // MAC address |
| 167 if (IsValidMacAddress(&line.at(index + mac_address_string.size()))) { |
| 168 UTF8ToString16(&line.at(index + mac_address_string.size()), |
| 169 17, // XX:XX:XX:XX:XX:XX |
| 170 &access_point_data->mac_address); |
| 171 } |
| 172 } else if ((index = line.find(ssid_string)) != std::string::npos) { |
| 173 // SSID |
| 174 // The string should be quoted. |
| 175 std::string::size_type start = index + ssid_string.size() + 1; |
| 176 std::string::size_type end = line.find('\"', start); |
| 177 // If we can't find a trailing quote, something has gone wrong. |
| 178 if (end != std::string::npos) { |
| 179 UTF8ToString16(&line.at(start), end - start, &access_point_data->ssid); |
| 180 } |
| 181 } else if ((index = line.find(signal_strength_string)) != std::string::npos) { |
| 182 // Signal strength |
| 183 // iwlist will convert to dBm if it can. If it has failed to do so, we can't |
| 184 // make use of the data. |
| 185 if (line.find("dBm") != std::string::npos) { |
| 186 // atoi will ignore trailing non-numeric characters |
| 187 access_point_data->radio_signal_strength = |
| 188 atoi(&line.at(index + signal_strength_string.size())); |
| 189 } |
| 190 } |
| 191 } |
| 192 |
| 193 static void ParseAccessPoint(const std::string &text, |
| 194 const std::string &mac_address_string, |
| 195 const std::string &ssid_string, |
| 196 const std::string &signal_strength_string, |
| 197 AccessPointData *access_point_data) { |
| 198 // Split response into lines to aid parsing. |
| 199 std::string::size_type start = 0; |
| 200 std::string::size_type end; |
| 201 do { |
| 202 end = text.find('\n', start); |
| 203 std::string::size_type length = (end == std::string::npos) ? |
| 204 std::string::npos : end - start; |
| 205 ParseLine(text.substr(start, length), |
| 206 mac_address_string, |
| 207 ssid_string, |
| 208 signal_strength_string, |
| 209 access_point_data); |
| 210 start = end + 1; |
| 211 } while (end != std::string::npos); |
| 212 } |
| 213 |
| 214 // Issues the specified command, and parses the response. Data for each access |
| 215 // point is separated by the given delimiter. Within each block of data, the |
| 216 // repsonse is split into lines and data is extracted by searching for the MAC |
| 217 // address, SSID and signal strength strings. |
| 218 bool IssueCommandAndParseResult(const char *command, |
| 219 const char *delimiter, |
| 220 const std::string &mac_address_string, |
| 221 const std::string &ssid_string, |
| 222 const std::string &signal_strength_string, |
| 223 WifiData::AccessPointDataSet *access_points) { |
| 224 // Open pipe in read mode. |
| 225 FILE *result_pipe = popen(command, "r"); |
| 226 if (result_pipe == NULL) { |
| 227 LOG(("IssueCommand(): Failed to open pipe.\n")); |
| 228 return false; |
| 229 } |
| 230 |
| 231 // Read results of command. |
| 232 static const int kBufferSize = 1024; |
| 233 char buffer[kBufferSize]; |
| 234 size_t bytes_read; |
| 235 std::string result; |
| 236 do { |
| 237 bytes_read = fread(buffer, 1, kBufferSize, result_pipe); |
| 238 result.append(buffer, bytes_read); |
| 239 } while (static_cast<int>(bytes_read) == kBufferSize); |
| 240 pclose(result_pipe); |
| 241 |
| 242 |
| 243 // Parse results. |
| 244 assert(access_points); |
| 245 access_points->clear(); |
| 246 std::string::size_type start = result.find(delimiter); |
| 247 while (start != std::string::npos) { |
| 248 std::string::size_type end = result.find(delimiter, start + 1); |
| 249 std::string::size_type length = (end == std::string::npos) ? |
| 250 std::string::npos : end - start; |
| 251 AccessPointData access_point_data; |
| 252 ParseAccessPoint(result.substr(start, length), |
| 253 mac_address_string, |
| 254 ssid_string, |
| 255 signal_strength_string, |
| 256 &access_point_data); |
| 257 access_points->insert(access_point_data); |
| 258 start = end; |
| 259 } |
| 260 |
| 261 return !access_points->empty(); |
| 262 } |
| 263 |
| 264 static bool GetAccessPointData(WifiData::AccessPointDataSet *access_points) { |
| 265 return IssueCommandAndParseResult("iwlist scan 2> /dev/null", |
| 266 "Cell ", |
| 267 "Address: ", |
| 268 "ESSID:", |
| 269 "Signal level=", |
| 270 access_points) || |
| 271 IssueCommandAndParseResult("iwconfig 2> /dev/null", |
| 272 "ESSID:\"", |
| 273 "Access Point: ", |
| 274 "ESSID:", |
| 275 "Signal level=", |
| 276 access_points); |
| 277 } |
| 278 |
| 279 #endif // LINUX && !OS_MACOSX |
| OLD | NEW |