Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: content/browser/geolocation/wifi_data_provider_linux.cc

Issue 6597044: Revert 76228 - Move core pieces of geolocation from chrome to content.This is... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
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 <dbus/dbus-glib.h>
12 #include <dbus/dbus-glib-lowlevel.h>
13 #include <dbus/dbus.h>
14 #include <dlfcn.h>
15 #include <glib.h>
16
17 #include "base/scoped_ptr.h"
18 #include "base/string_number_conversions.h"
19 #include "base/utf_string_conversions.h"
20
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 // Function type matching dbus_g_bus_get_private so that we can
36 // dynamically determine the presence of this symbol (see
37 // NetworkManagerWlanApi::Init)
38 typedef DBusGConnection* DBusGBusGetPrivateFunc(
39 DBusBusType type, GMainContext* context, GError** error);
40
41 // Utility wrappers to make various GLib & DBus structs into scoped objects.
42 class ScopedGPtrArrayFree {
43 public:
44 void operator()(GPtrArray* x) const {
45 if (x)
46 g_ptr_array_free(x, TRUE);
47 }
48 };
49 // Use ScopedGPtrArrayPtr as if it were scoped_ptr<GPtrArray>
50 typedef scoped_ptr_malloc<GPtrArray, ScopedGPtrArrayFree> ScopedGPtrArrayPtr;
51
52 class ScopedGObjectFree {
53 public:
54 void operator()(void* x) const {
55 if (x)
56 g_object_unref(x);
57 }
58 };
59 // Use ScopedDBusGProxyPtr as if it were scoped_ptr<DBusGProxy>
60 typedef scoped_ptr_malloc<DBusGProxy, ScopedGObjectFree> ScopedDBusGProxyPtr;
61
62 // Use ScopedGValue::v as an instance of GValue with automatic cleanup.
63 class ScopedGValue {
64 public:
65 ScopedGValue()
66 : v(empty_gvalue()) {
67 }
68 ~ScopedGValue() {
69 g_value_unset(&v);
70 }
71 static GValue empty_gvalue() {
72 GValue value = {0};
73 return value;
74 }
75
76 GValue v;
77 };
78
79 // Use ScopedDLHandle to automatically clean up after dlopen.
80 class ScopedDLHandle {
81 public:
82 ScopedDLHandle(void* handle)
83 : handle_(handle) {
84 }
85 ~ScopedDLHandle() {
86 if (handle_)
87 dlclose(handle_);
88 }
89 void* get() {
90 return handle_;
91 }
92 private:
93 void *handle_;
94 };
95
96 // Wifi API binding to NetworkManager, to allow reuse of the polling behavior
97 // defined in WifiDataProviderCommon.
98 // TODO(joth): NetworkManager also allows for notification based handling,
99 // however this will require reworking of the threading code to run a GLib
100 // event loop (GMainLoop).
101 class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface {
102 public:
103 NetworkManagerWlanApi();
104 ~NetworkManagerWlanApi();
105
106 // Must be called before any other interface method. Will return false if the
107 // NetworkManager session cannot be created (e.g. not present on this distro),
108 // in which case no other method may be called.
109 bool Init();
110
111 // WifiDataProviderCommon::WlanApiInterface
112 bool GetAccessPointData(WifiData::AccessPointDataSet* data);
113
114 private:
115 // Checks if the last dbus call returned an error. If it did, logs the error
116 // message, frees it and returns true.
117 // This must be called after every dbus call that accepts |&error_|
118 bool CheckError();
119
120 // Enumerates the list of available network adapter devices known to
121 // NetworkManager. Ownership of the array (and contained objects) is returned
122 // to the caller.
123 GPtrArray* GetAdapterDeviceList();
124
125 // Given the NetworkManager path to a wireless adapater, dumps the wifi scan
126 // results and appends them to |data|. Returns false if a fatal error is
127 // encountered such that the data set could not be populated.
128 bool GetAccessPointsForAdapter(const gchar* adapter_path,
129 WifiData::AccessPointDataSet* data);
130
131 // Internal method used by |GetAccessPointsForAdapter|, given a wifi access
132 // point proxy retrieves the named property into |value_out|. Returns false if
133 // the property could not be read, or is not of type |expected_gvalue_type|.
134 bool GetAccessPointProperty(DBusGProxy* proxy, const char* property_name,
135 int expected_gvalue_type, GValue* value_out);
136
137 // Error from the last dbus call. NULL when there's no error. Freed and
138 // cleared by CheckError().
139 GError* error_;
140 // Connection to the dbus system bus.
141 DBusGConnection* connection_;
142 // Main context
143 GMainContext* context_;
144 // Proxy to the network manager dbus service.
145 ScopedDBusGProxyPtr proxy_;
146
147 DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi);
148 };
149
150 // Convert a wifi frequency to the corresponding channel. Adapted from
151 // geolocaiton/wifilib.cc in googleclient (internal to google).
152 int frquency_in_khz_to_channel(int frequency_khz) {
153 if (frequency_khz >= 2412000 && frequency_khz <= 2472000) // Channels 1-13.
154 return (frequency_khz - 2407000) / 5000;
155 if (frequency_khz == 2484000)
156 return 14;
157 if (frequency_khz > 5000000 && frequency_khz < 6000000) // .11a bands.
158 return (frequency_khz - 5000000) / 5000;
159 // Ignore everything else.
160 return AccessPointData().channel; // invalid channel
161 }
162
163 NetworkManagerWlanApi::NetworkManagerWlanApi()
164 : error_(NULL),
165 connection_(NULL),
166 context_(NULL)
167 {
168 }
169
170 NetworkManagerWlanApi::~NetworkManagerWlanApi() {
171 proxy_.reset();
172 if (connection_) {
173 dbus_connection_close(dbus_g_connection_get_connection(connection_));
174 dbus_g_connection_unref(connection_);
175 }
176 if (context_)
177 g_main_context_unref(context_);
178
179 DCHECK(!error_) << "Missing a call to CheckError() to clear |error_|";
180 }
181
182 bool NetworkManagerWlanApi::Init() {
183 // Chrome DLL init code handles initializing the thread system, so rather than
184 // get caught up with that nonsense here, lets just assert our requirement.
185 CHECK(g_thread_supported());
186
187 // We require a private bus to ensure that we don't interfere with the
188 // default loop on the main thread. Unforunately this functionality is only
189 // available in dbus-glib-0.84 and later. We do a dynamic symbol lookup
190 // to determine if dbus_g_bus_get_private is available. See bug
191 // http://code.google.com/p/chromium/issues/detail?id=59913 for more
192 // information.
193 ScopedDLHandle handle(dlopen(NULL, RTLD_LAZY));
194 if (!handle.get())
195 return false;
196
197 DBusGBusGetPrivateFunc *my_dbus_g_bus_get_private =
198 (DBusGBusGetPrivateFunc *) dlsym(handle.get(), "dbus_g_bus_get_private");
199
200 if (!my_dbus_g_bus_get_private) {
201 LOG(ERROR) << "We need dbus-glib >= 0.84 for wifi geolocation.";
202 return false;
203 }
204 // Get a private connection to the session bus.
205 context_ = g_main_context_new();
206 DCHECK(context_);
207 connection_ = my_dbus_g_bus_get_private(DBUS_BUS_SYSTEM, context_, &error_);
208
209 if (CheckError())
210 return false;
211 DCHECK(connection_);
212
213 // Disable timers on the connection since we don't need them and
214 // we're not going to run them anyway as the connection is associated
215 // with a private context. See bug http://crbug.com/40803
216 dbus_bool_t ok = dbus_connection_set_timeout_functions(
217 dbus_g_connection_get_connection(connection_),
218 NULL, NULL, NULL, NULL, NULL);
219 DCHECK(ok);
220
221 proxy_.reset(dbus_g_proxy_new_for_name(connection_,
222 kNetworkManagerServiceName,
223 kNetworkManagerPath,
224 kNetworkManagerInterface));
225 DCHECK(proxy_.get());
226
227 // Validate the proxy object by checking we can enumerate devices.
228 ScopedGPtrArrayPtr device_list(GetAdapterDeviceList());
229 return !!device_list.get();
230 }
231
232 bool NetworkManagerWlanApi::GetAccessPointData(
233 WifiData::AccessPointDataSet* data) {
234 ScopedGPtrArrayPtr device_list(GetAdapterDeviceList());
235 if (device_list == NULL) {
236 DLOG(WARNING) << "Could not enumerate access points";
237 return false;
238 }
239 int success_count = 0;
240 int fail_count = 0;
241
242 // Iterate the devices, getting APs for each wireless adapter found
243 for (guint i = 0; i < device_list->len; i++) {
244 const gchar* device_path =
245 reinterpret_cast<const gchar*>(g_ptr_array_index(device_list, i));
246
247 ScopedDBusGProxyPtr device_properties_proxy(dbus_g_proxy_new_from_proxy(
248 proxy_.get(), DBUS_INTERFACE_PROPERTIES, device_path));
249 ScopedGValue device_type_g_value;
250 dbus_g_proxy_call(device_properties_proxy.get(), "Get", &error_,
251 G_TYPE_STRING, "org.freedesktop.NetworkManager.Device",
252 G_TYPE_STRING, "DeviceType",
253 G_TYPE_INVALID,
254 G_TYPE_VALUE, &device_type_g_value.v,
255 G_TYPE_INVALID);
256 if (CheckError())
257 continue;
258
259 const guint device_type = g_value_get_uint(&device_type_g_value.v);
260
261 if (device_type == NM_DEVICE_TYPE_WIFI) { // Found a wlan adapter
262 if (GetAccessPointsForAdapter(device_path, data))
263 ++success_count;
264 else
265 ++fail_count;
266 }
267 }
268 // At least one successfull scan overrides any other adapter reporting error.
269 return success_count || fail_count == 0;
270 }
271
272 bool NetworkManagerWlanApi::CheckError() {
273 if (error_) {
274 LOG(ERROR) << "Failed to complete NetworkManager call: " << error_->message;
275 g_error_free(error_);
276 error_ = NULL;
277 return true;
278 }
279 return false;
280 }
281
282 GPtrArray* NetworkManagerWlanApi::GetAdapterDeviceList() {
283 GPtrArray* device_list = NULL;
284 dbus_g_proxy_call(proxy_.get(), "GetDevices", &error_,
285 G_TYPE_INVALID,
286 dbus_g_type_get_collection("GPtrArray",
287 DBUS_TYPE_G_OBJECT_PATH),
288 &device_list,
289 G_TYPE_INVALID);
290 if (CheckError())
291 return NULL;
292 return device_list;
293 }
294
295 bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
296 const gchar* adapter_path, WifiData::AccessPointDataSet* data) {
297 DCHECK(proxy_.get());
298
299 // Create a proxy object for this wifi adapter, and ask it to do a scan
300 // (or at least, dump its scan results).
301 ScopedDBusGProxyPtr wifi_adapter_proxy(dbus_g_proxy_new_from_proxy(
302 proxy_.get(), "org.freedesktop.NetworkManager.Device.Wireless",
303 adapter_path));
304
305 GPtrArray* ap_list_raw = NULL;
306 // Enumerate the access points for this adapter.
307 dbus_g_proxy_call(wifi_adapter_proxy.get(), "GetAccessPoints", &error_,
308 G_TYPE_INVALID,
309 dbus_g_type_get_collection("GPtrArray",
310 DBUS_TYPE_G_OBJECT_PATH),
311 &ap_list_raw,
312 G_TYPE_INVALID);
313 ScopedGPtrArrayPtr ap_list(ap_list_raw); // Takes ownership.
314 ap_list_raw = NULL;
315
316 if (CheckError())
317 return false;
318
319 DVLOG(1) << "Wireless adapter " << adapter_path << " found " << ap_list->len
320 << " access points.";
321
322 for (guint i = 0; i < ap_list->len; i++) {
323 const gchar* ap_path =
324 reinterpret_cast<const gchar*>(g_ptr_array_index(ap_list, i));
325 ScopedDBusGProxyPtr access_point_proxy(dbus_g_proxy_new_from_proxy(
326 proxy_.get(), DBUS_INTERFACE_PROPERTIES, ap_path));
327
328 AccessPointData access_point_data;
329 { // Read SSID.
330 ScopedGValue ssid_g_value;
331 if (!GetAccessPointProperty(access_point_proxy.get(), "Ssid",
332 G_TYPE_BOXED, &ssid_g_value.v))
333 continue;
334 const GArray* ssid =
335 reinterpret_cast<const GArray*>(g_value_get_boxed(&ssid_g_value.v));
336 UTF8ToUTF16(ssid->data, ssid->len, &access_point_data.ssid);
337 }
338
339 { // Read the mac address
340 ScopedGValue mac_g_value;
341 if (!GetAccessPointProperty(access_point_proxy.get(), "HwAddress",
342 G_TYPE_STRING, &mac_g_value.v))
343 continue;
344 std::string mac = g_value_get_string(&mac_g_value.v);
345 ReplaceSubstringsAfterOffset(&mac, 0U, ":", "");
346 std::vector<uint8> mac_bytes;
347 if (!base::HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) {
348 DLOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size()
349 << " bytes) so using raw string: " << mac;
350 access_point_data.mac_address = UTF8ToUTF16(mac);
351 } else {
352 access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]);
353 }
354 }
355
356 { // Read signal strength.
357 ScopedGValue signal_g_value;
358 if (!GetAccessPointProperty(access_point_proxy.get(), "Strength",
359 G_TYPE_UCHAR, &signal_g_value.v))
360 continue;
361 // Convert strength as a percentage into dBs.
362 access_point_data.radio_signal_strength =
363 -100 + g_value_get_uchar(&signal_g_value.v) / 2;
364 }
365
366 { // Read the channel
367 ScopedGValue freq_g_value;
368 if (!GetAccessPointProperty(access_point_proxy.get(), "Frequency",
369 G_TYPE_UINT, &freq_g_value.v))
370 continue;
371 // NetworkManager returns frequency in MHz.
372 access_point_data.channel =
373 frquency_in_khz_to_channel(g_value_get_uint(&freq_g_value.v) * 1000);
374 }
375 data->insert(access_point_data);
376 }
377 return true;
378 }
379
380 bool NetworkManagerWlanApi::GetAccessPointProperty(DBusGProxy* proxy,
381 const char* property_name,
382 int expected_gvalue_type,
383 GValue* value_out) {
384 dbus_g_proxy_call(proxy, "Get", &error_,
385 G_TYPE_STRING, "org.freedesktop.NetworkManager.AccessPoint",
386 G_TYPE_STRING, property_name,
387 G_TYPE_INVALID,
388 G_TYPE_VALUE, value_out,
389 G_TYPE_INVALID);
390 if (CheckError())
391 return false;
392 if (!G_VALUE_HOLDS(value_out, expected_gvalue_type)) {
393 DLOG(WARNING) << "Property " << property_name << " unexptected type "
394 << G_VALUE_TYPE(value_out);
395 return false;
396 }
397 return true;
398 }
399
400 } // namespace
401
402 // static
403 template<>
404 WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() {
405 return new WifiDataProviderLinux();
406 }
407
408 WifiDataProviderLinux::WifiDataProviderLinux() {
409 }
410
411 WifiDataProviderLinux::~WifiDataProviderLinux() {
412 }
413
414 WifiDataProviderCommon::WlanApiInterface*
415 WifiDataProviderLinux::NewWlanApi() {
416 scoped_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi);
417 if (wlan_api->Init())
418 return wlan_api.release();
419 return NULL;
420 }
421
422 PollingPolicyInterface* WifiDataProviderLinux::NewPollingPolicy() {
423 return new GenericPollingPolicy<kDefaultPollingIntervalMilliseconds,
424 kNoChangePollingIntervalMilliseconds,
425 kTwoNoChangePollingIntervalMilliseconds,
426 kNoWifiPollingIntervalMilliseconds>;
427 }
OLDNEW
« no previous file with comments | « content/browser/geolocation/wifi_data_provider_linux.h ('k') | content/browser/geolocation/wifi_data_provider_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698