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

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

Issue 2192683002: Reland 2:Geolocation: move from content/browser to device/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Ignore size_t_to_int truncation warning Created 4 years, 4 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
OLDNEW
(Empty)
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 <stddef.h>
12 #include <stdint.h>
13
14 #include <memory>
15
16 #include "base/macros.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/browser/geolocation/wifi_data_provider_manager.h"
20 #include "dbus/bus.h"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "dbus/object_proxy.h"
24
25 namespace content {
26 namespace {
27 // The time periods between successive polls of the wifi data.
28 const int kDefaultPollingIntervalMilliseconds = 10 * 1000; // 10s
29 const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000; // 2 mins
30 const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000; // 10 mins
31 const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
32
33 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
34 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
35 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
36
37 // From http://projects.gnome.org/NetworkManager/developers/spec.html
38 enum { NM_DEVICE_TYPE_WIFI = 2 };
39
40 // Wifi API binding to NetworkManager, to allow reuse of the polling behavior
41 // defined in WifiDataProviderCommon.
42 // TODO(joth): NetworkManager also allows for notification based handling,
43 // however this will require reworking of the threading code to run a GLib
44 // event loop (GMainLoop).
45 class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface {
46 public:
47 NetworkManagerWlanApi();
48 ~NetworkManagerWlanApi() override;
49
50 // Must be called before any other interface method. Will return false if the
51 // NetworkManager session cannot be created (e.g. not present on this distro),
52 // in which case no other method may be called.
53 bool Init();
54
55 // Similar to Init() but can inject the bus object. Used for testing.
56 bool InitWithBus(dbus::Bus* bus);
57
58 // WifiDataProviderCommon::WlanApiInterface
59 //
60 // This function makes blocking D-Bus calls, but it's totally fine as
61 // the code runs in "Geolocation" thread, not the browser's UI thread.
62 bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
63
64 private:
65 // Enumerates the list of available network adapter devices known to
66 // NetworkManager. Return true on success.
67 bool GetAdapterDeviceList(std::vector<dbus::ObjectPath>* device_paths);
68
69 // Given the NetworkManager path to a wireless adapater, dumps the wifi scan
70 // results and appends them to |data|. Returns false if a fatal error is
71 // encountered such that the data set could not be populated.
72 bool GetAccessPointsForAdapter(const dbus::ObjectPath& adapter_path,
73 WifiData::AccessPointDataSet* data);
74
75 // Internal method used by |GetAccessPointsForAdapter|, given a wifi access
76 // point proxy retrieves the named property and returns it. Returns NULL in
77 // a scoped_ptr if the property could not be read.
78 std::unique_ptr<dbus::Response> GetAccessPointProperty(
79 dbus::ObjectProxy* proxy,
80 const std::string& property_name);
81
82 scoped_refptr<dbus::Bus> system_bus_;
83 dbus::ObjectProxy* network_manager_proxy_;
84
85 DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi);
86 };
87
88 // Convert a wifi frequency to the corresponding channel. Adapted from
89 // geolocaiton/wifilib.cc in googleclient (internal to google).
90 int frquency_in_khz_to_channel(int frequency_khz) {
91 if (frequency_khz >= 2412000 && frequency_khz <= 2472000) // Channels 1-13.
92 return (frequency_khz - 2407000) / 5000;
93 if (frequency_khz == 2484000)
94 return 14;
95 if (frequency_khz > 5000000 && frequency_khz < 6000000) // .11a bands.
96 return (frequency_khz - 5000000) / 5000;
97 // Ignore everything else.
98 return AccessPointData().channel; // invalid channel
99 }
100
101 NetworkManagerWlanApi::NetworkManagerWlanApi()
102 : network_manager_proxy_(NULL) {
103 }
104
105 NetworkManagerWlanApi::~NetworkManagerWlanApi() {
106 // Close the connection.
107 system_bus_->ShutdownAndBlock();
108 }
109
110 bool NetworkManagerWlanApi::Init() {
111 dbus::Bus::Options options;
112 options.bus_type = dbus::Bus::SYSTEM;
113 options.connection_type = dbus::Bus::PRIVATE;
114 return InitWithBus(new dbus::Bus(options));
115 }
116
117 bool NetworkManagerWlanApi::InitWithBus(dbus::Bus* bus) {
118 system_bus_ = bus;
119 // system_bus_ will own all object proxies created from the bus.
120 network_manager_proxy_ =
121 system_bus_->GetObjectProxy(kNetworkManagerServiceName,
122 dbus::ObjectPath(kNetworkManagerPath));
123 // Validate the proxy object by checking we can enumerate devices.
124 std::vector<dbus::ObjectPath> adapter_paths;
125 const bool success = GetAdapterDeviceList(&adapter_paths);
126 VLOG(1) << "Init() result: " << success;
127 return success;
128 }
129
130 bool NetworkManagerWlanApi::GetAccessPointData(
131 WifiData::AccessPointDataSet* data) {
132 std::vector<dbus::ObjectPath> device_paths;
133 if (!GetAdapterDeviceList(&device_paths)) {
134 LOG(WARNING) << "Could not enumerate access points";
135 return false;
136 }
137 int success_count = 0;
138 int fail_count = 0;
139
140 // Iterate the devices, getting APs for each wireless adapter found
141 for (size_t i = 0; i < device_paths.size(); ++i) {
142 const dbus::ObjectPath& device_path = device_paths[i];
143 VLOG(1) << "Checking device: " << device_path.value();
144
145 dbus::ObjectProxy* device_proxy =
146 system_bus_->GetObjectProxy(kNetworkManagerServiceName,
147 device_path);
148
149 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
150 dbus::MessageWriter builder(&method_call);
151 builder.AppendString("org.freedesktop.NetworkManager.Device");
152 builder.AppendString("DeviceType");
153 std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
154 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
155 if (!response) {
156 LOG(WARNING) << "Failed to get the device type for "
157 << device_path.value();
158 continue; // Check the next device.
159 }
160 dbus::MessageReader reader(response.get());
161 uint32_t device_type = 0;
162 if (!reader.PopVariantOfUint32(&device_type)) {
163 LOG(WARNING) << "Unexpected response for " << device_type << ": "
164 << response->ToString();
165 continue; // Check the next device.
166 }
167 VLOG(1) << "Device type: " << device_type;
168
169 if (device_type == NM_DEVICE_TYPE_WIFI) { // Found a wlan adapter
170 if (GetAccessPointsForAdapter(device_path, data))
171 ++success_count;
172 else
173 ++fail_count;
174 }
175 }
176 // At least one successful scan overrides any other adapter reporting error.
177 return success_count || fail_count == 0;
178 }
179
180 bool NetworkManagerWlanApi::GetAdapterDeviceList(
181 std::vector<dbus::ObjectPath>* device_paths) {
182 dbus::MethodCall method_call(kNetworkManagerInterface, "GetDevices");
183 std::unique_ptr<dbus::Response> response(
184 network_manager_proxy_->CallMethodAndBlock(
185 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
186 if (!response) {
187 LOG(WARNING) << "Failed to get the device list";
188 return false;
189 }
190
191 dbus::MessageReader reader(response.get());
192 if (!reader.PopArrayOfObjectPaths(device_paths)) {
193 LOG(WARNING) << "Unexpected response: " << response->ToString();
194 return false;
195 }
196 return true;
197 }
198
199
200 bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
201 const dbus::ObjectPath& adapter_path, WifiData::AccessPointDataSet* data) {
202 // Create a proxy object for this wifi adapter, and ask it to do a scan
203 // (or at least, dump its scan results).
204 dbus::ObjectProxy* device_proxy =
205 system_bus_->GetObjectProxy(kNetworkManagerServiceName,
206 adapter_path);
207 dbus::MethodCall method_call(
208 "org.freedesktop.NetworkManager.Device.Wireless",
209 "GetAccessPoints");
210 std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
211 &method_call, 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 std::unique_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_t* 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 std::unique_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 base::ReplaceSubstringsAfterOffset(&mac, 0U, ":", base::StringPiece());
275 std::vector<uint8_t> 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 std::unique_ptr<dbus::Response> response(
287 GetAccessPointProperty(access_point_proxy, "Strength"));
288 if (!response)
289 continue;
290 dbus::MessageReader reader(response.get());
291 uint8_t 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 std::unique_ptr<dbus::Response> response(
303 GetAccessPointProperty(access_point_proxy, "Frequency"));
304 if (!response)
305 continue;
306 dbus::MessageReader reader(response.get());
307 uint32_t 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 std::unique_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 std::unique_ptr<dbus::Response> response =
337 access_point_proxy->CallMethodAndBlock(
338 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
339 if (!response) {
340 LOG(WARNING) << "Failed to get property for " << property_name;
341 }
342 return response;
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 std::unique_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 std::unique_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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698