| 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 |