Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / geolocation / wifi_data_provider_linux.cc
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 // Provides wifi scan API binding for suitable for typical linux distributions.
6 // Currently, only the NetworkManager API is used, accessed via D-Bus (in turn
7 // accessed via the GLib wrapper).
8
9 #include "content/browser/geolocation/wifi_data_provider_linux.h"
10
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "content/browser/geolocation/wifi_data_provider_manager.h"
15 #include "dbus/bus.h"
16 #include "dbus/message.h"
17 #include "dbus/object_path.h"
18 #include "dbus/object_proxy.h"
19
20 namespace content {
21 namespace {
22 // The time periods between successive polls of the wifi data.
23 const int kDefaultPollingIntervalMilliseconds = 10 * 1000;  // 10s
24 const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000;  // 2 mins
25 const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000;  // 10 mins
26 const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
27
28 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
29 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
30 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
31
32 // From http://projects.gnome.org/NetworkManager/developers/spec.html
33 enum { NM_DEVICE_TYPE_WIFI = 2 };
34
35 // Wifi API binding to NetworkManager, to allow reuse of the polling behavior
36 // defined in WifiDataProviderCommon.
37 // TODO(joth): NetworkManager also allows for notification based handling,
38 // however this will require reworking of the threading code to run a GLib
39 // event loop (GMainLoop).
40 class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface {
41  public:
42   NetworkManagerWlanApi();
43   virtual ~NetworkManagerWlanApi();
44
45   // Must be called before any other interface method. Will return false if the
46   // NetworkManager session cannot be created (e.g. not present on this distro),
47   // in which case no other method may be called.
48   bool Init();
49
50   // Similar to Init() but can inject the bus object. Used for testing.
51   bool InitWithBus(dbus::Bus* bus);
52
53   // WifiDataProviderCommon::WlanApiInterface
54   //
55   // This function makes blocking D-Bus calls, but it's totally fine as
56   // the code runs in "Geolocation" thread, not the browser's UI thread.
57   virtual bool GetAccessPointData(WifiData::AccessPointDataSet* data) OVERRIDE;
58
59  private:
60   // Enumerates the list of available network adapter devices known to
61   // NetworkManager. Return true on success.
62   bool GetAdapterDeviceList(std::vector<dbus::ObjectPath>* device_paths);
63
64   // Given the NetworkManager path to a wireless adapater, dumps the wifi scan
65   // results and appends them to |data|. Returns false if a fatal error is
66   // encountered such that the data set could not be populated.
67   bool GetAccessPointsForAdapter(const dbus::ObjectPath& adapter_path,
68                                  WifiData::AccessPointDataSet* data);
69
70   // Internal method used by |GetAccessPointsForAdapter|, given a wifi access
71   // point proxy retrieves the named property and returns it. Returns NULL in
72   // a scoped_ptr if the property could not be read.
73   scoped_ptr<dbus::Response> GetAccessPointProperty(
74       dbus::ObjectProxy* proxy,
75       const std::string& property_name);
76
77   scoped_refptr<dbus::Bus> system_bus_;
78   dbus::ObjectProxy* network_manager_proxy_;
79
80   DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi);
81 };
82
83 // Convert a wifi frequency to the corresponding channel. Adapted from
84 // geolocaiton/wifilib.cc in googleclient (internal to google).
85 int frquency_in_khz_to_channel(int frequency_khz) {
86   if (frequency_khz >= 2412000 && frequency_khz <= 2472000)  // Channels 1-13.
87     return (frequency_khz - 2407000) / 5000;
88   if (frequency_khz == 2484000)
89     return 14;
90   if (frequency_khz > 5000000 && frequency_khz < 6000000)  // .11a bands.
91     return (frequency_khz - 5000000) / 5000;
92   // Ignore everything else.
93   return AccessPointData().channel;  // invalid channel
94 }
95
96 NetworkManagerWlanApi::NetworkManagerWlanApi()
97     : network_manager_proxy_(NULL) {
98 }
99
100 NetworkManagerWlanApi::~NetworkManagerWlanApi() {
101   // Close the connection.
102   system_bus_->ShutdownAndBlock();
103 }
104
105 bool NetworkManagerWlanApi::Init() {
106   dbus::Bus::Options options;
107   options.bus_type = dbus::Bus::SYSTEM;
108   options.connection_type = dbus::Bus::PRIVATE;
109   return InitWithBus(new dbus::Bus(options));
110 }
111
112 bool NetworkManagerWlanApi::InitWithBus(dbus::Bus* bus) {
113   system_bus_ = bus;
114   // system_bus_ will own all object proxies created from the bus.
115   network_manager_proxy_ =
116       system_bus_->GetObjectProxy(kNetworkManagerServiceName,
117                                   dbus::ObjectPath(kNetworkManagerPath));
118   // Validate the proxy object by checking we can enumerate devices.
119   std::vector<dbus::ObjectPath> adapter_paths;
120   const bool success = GetAdapterDeviceList(&adapter_paths);
121   VLOG(1) << "Init() result:  " << success;
122   return success;
123 }
124
125 bool NetworkManagerWlanApi::GetAccessPointData(
126     WifiData::AccessPointDataSet* data) {
127   std::vector<dbus::ObjectPath> device_paths;
128   if (!GetAdapterDeviceList(&device_paths)) {
129     LOG(WARNING) << "Could not enumerate access points";
130     return false;
131   }
132   int success_count = 0;
133   int fail_count = 0;
134
135   // Iterate the devices, getting APs for each wireless adapter found
136   for (size_t i = 0; i < device_paths.size(); ++i) {
137     const dbus::ObjectPath& device_path = device_paths[i];
138     VLOG(1) << "Checking device: " << device_path.value();
139
140     dbus::ObjectProxy* device_proxy =
141         system_bus_->GetObjectProxy(kNetworkManagerServiceName,
142                                     device_path);
143
144     dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
145     dbus::MessageWriter builder(&method_call);
146     builder.AppendString("org.freedesktop.NetworkManager.Device");
147     builder.AppendString("DeviceType");
148     scoped_ptr<dbus::Response> response(
149         device_proxy->CallMethodAndBlock(
150             &method_call,
151             dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
152     if (!response) {
153       LOG(WARNING) << "Failed to get the device type for "
154                    << device_path.value();
155       continue;  // Check the next device.
156     }
157     dbus::MessageReader reader(response.get());
158     uint32 device_type = 0;
159     if (!reader.PopVariantOfUint32(&device_type)) {
160       LOG(WARNING) << "Unexpected response for " << device_type << ": "
161                    << response->ToString();
162       continue;  // Check the next device.
163     }
164     VLOG(1) << "Device type: " << device_type;
165
166     if (device_type == NM_DEVICE_TYPE_WIFI) {  // Found a wlan adapter
167       if (GetAccessPointsForAdapter(device_path, data))
168         ++success_count;
169       else
170         ++fail_count;
171     }
172   }
173   // At least one successfull scan overrides any other adapter reporting error.
174   return success_count || fail_count == 0;
175 }
176
177 bool NetworkManagerWlanApi::GetAdapterDeviceList(
178     std::vector<dbus::ObjectPath>* device_paths) {
179   dbus::MethodCall method_call(kNetworkManagerInterface, "GetDevices");
180   scoped_ptr<dbus::Response> response(
181       network_manager_proxy_->CallMethodAndBlock(
182           &method_call,
183           dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
184   if (!response) {
185     LOG(WARNING) << "Failed to get the device list";
186     return false;
187   }
188
189   dbus::MessageReader reader(response.get());
190   if (!reader.PopArrayOfObjectPaths(device_paths)) {
191     LOG(WARNING) << "Unexpected response: " << response->ToString();
192     return false;
193   }
194   return true;
195 }
196
197
198 bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
199     const dbus::ObjectPath& adapter_path, WifiData::AccessPointDataSet* data) {
200   // Create a proxy object for this wifi adapter, and ask it to do a scan
201   // (or at least, dump its scan results).
202   dbus::ObjectProxy* device_proxy =
203       system_bus_->GetObjectProxy(kNetworkManagerServiceName,
204                                   adapter_path);
205   dbus::MethodCall method_call(
206       "org.freedesktop.NetworkManager.Device.Wireless",
207       "GetAccessPoints");
208   scoped_ptr<dbus::Response> response(
209       device_proxy->CallMethodAndBlock(
210           &method_call,
211           dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
212   if (!response) {
213     LOG(WARNING) << "Failed to get access points data for "
214                  << adapter_path.value();
215     return false;
216   }
217   dbus::MessageReader reader(response.get());
218   std::vector<dbus::ObjectPath> access_point_paths;
219   if (!reader.PopArrayOfObjectPaths(&access_point_paths)) {
220     LOG(WARNING) << "Unexpected response for " << adapter_path.value() << ": "
221                  << response->ToString();
222     return false;
223   }
224
225   VLOG(1) << "Wireless adapter " << adapter_path.value() << " found "
226           << access_point_paths.size() << " access points.";
227
228   for (size_t i = 0; i < access_point_paths.size(); ++i) {
229     const dbus::ObjectPath& access_point_path = access_point_paths[i];
230     VLOG(1) << "Checking access point: " << access_point_path.value();
231
232     dbus::ObjectProxy* access_point_proxy =
233         system_bus_->GetObjectProxy(kNetworkManagerServiceName,
234                                     access_point_path);
235
236     AccessPointData access_point_data;
237     {
238       scoped_ptr<dbus::Response> response(
239           GetAccessPointProperty(access_point_proxy, "Ssid"));
240       if (!response)
241         continue;
242       // The response should contain a variant that contains an array of bytes.
243       dbus::MessageReader reader(response.get());
244       dbus::MessageReader variant_reader(response.get());
245       if (!reader.PopVariant(&variant_reader)) {
246         LOG(WARNING) << "Unexpected response for " << access_point_path.value()
247                      << ": " << response->ToString();
248         continue;
249       }
250       const uint8* ssid_bytes = NULL;
251       size_t ssid_length = 0;
252       if (!variant_reader.PopArrayOfBytes(&ssid_bytes, &ssid_length)) {
253         LOG(WARNING) << "Unexpected response for " << access_point_path.value()
254                      << ": " << response->ToString();
255         continue;
256       }
257       std::string ssid(ssid_bytes, ssid_bytes + ssid_length);
258       access_point_data.ssid = base::UTF8ToUTF16(ssid);
259     }
260
261     { // Read the mac address
262       scoped_ptr<dbus::Response> response(
263           GetAccessPointProperty(access_point_proxy, "HwAddress"));
264       if (!response)
265         continue;
266       dbus::MessageReader reader(response.get());
267       std::string mac;
268       if (!reader.PopVariantOfString(&mac)) {
269         LOG(WARNING) << "Unexpected response for " << access_point_path.value()
270                      << ": " << response->ToString();
271         continue;
272       }
273
274       ReplaceSubstringsAfterOffset(&mac, 0U, ":", std::string());
275       std::vector<uint8> mac_bytes;
276       if (!base::HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) {
277         LOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size()
278                      << " bytes) so using raw string: " << mac;
279         access_point_data.mac_address = base::UTF8ToUTF16(mac);
280       } else {
281         access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]);
282       }
283     }
284
285     {  // Read signal strength.
286       scoped_ptr<dbus::Response> response(
287           GetAccessPointProperty(access_point_proxy, "Strength"));
288       if (!response)
289         continue;
290       dbus::MessageReader reader(response.get());
291       uint8 strength = 0;
292       if (!reader.PopVariantOfByte(&strength)) {
293         LOG(WARNING) << "Unexpected response for " << access_point_path.value()
294                      << ": " << response->ToString();
295         continue;
296       }
297       // Convert strength as a percentage into dBs.
298       access_point_data.radio_signal_strength = -100 + strength / 2;
299     }
300
301     { // Read the channel
302       scoped_ptr<dbus::Response> response(
303           GetAccessPointProperty(access_point_proxy, "Frequency"));
304       if (!response)
305         continue;
306       dbus::MessageReader reader(response.get());
307       uint32 frequency = 0;
308       if (!reader.PopVariantOfUint32(&frequency)) {
309         LOG(WARNING) << "Unexpected response for " << access_point_path.value()
310                      << ": " << response->ToString();
311         continue;
312       }
313
314       // NetworkManager returns frequency in MHz.
315       access_point_data.channel =
316           frquency_in_khz_to_channel(frequency * 1000);
317     }
318     VLOG(1) << "Access point data of " << access_point_path.value() << ": "
319             << "SSID: " << access_point_data.ssid << ", "
320             << "MAC: " << access_point_data.mac_address << ", "
321             << "Strength: " << access_point_data.radio_signal_strength << ", "
322             << "Channel: " << access_point_data.channel;
323
324     data->insert(access_point_data);
325   }
326   return true;
327 }
328
329 scoped_ptr<dbus::Response> NetworkManagerWlanApi::GetAccessPointProperty(
330     dbus::ObjectProxy* access_point_proxy,
331     const std::string& property_name) {
332   dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
333   dbus::MessageWriter builder(&method_call);
334   builder.AppendString("org.freedesktop.NetworkManager.AccessPoint");
335   builder.AppendString(property_name);
336   scoped_ptr<dbus::Response> response = access_point_proxy->CallMethodAndBlock(
337       &method_call,
338       dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
339   if (!response) {
340     LOG(WARNING) << "Failed to get property for " << property_name;
341   }
342   return response.Pass();
343 }
344
345 }  // namespace
346
347 // static
348 WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
349   return new WifiDataProviderLinux();
350 }
351
352 WifiDataProviderLinux::WifiDataProviderLinux() {
353 }
354
355 WifiDataProviderLinux::~WifiDataProviderLinux() {
356 }
357
358 WifiDataProviderCommon::WlanApiInterface*
359 WifiDataProviderLinux::NewWlanApi() {
360   scoped_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi);
361   if (wlan_api->Init())
362     return wlan_api.release();
363   return NULL;
364 }
365
366 WifiPollingPolicy* WifiDataProviderLinux::NewPollingPolicy() {
367   return new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds,
368                                       kNoChangePollingIntervalMilliseconds,
369                                       kTwoNoChangePollingIntervalMilliseconds,
370                                       kNoWifiPollingIntervalMilliseconds>;
371 }
372
373 WifiDataProviderCommon::WlanApiInterface*
374 WifiDataProviderLinux::NewWlanApiForTesting(dbus::Bus* bus) {
375   scoped_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi);
376   if (wlan_api->InitWithBus(bus))
377     return wlan_api.release();
378   return NULL;
379 }
380
381 }  // namespace content