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