| Index: content/browser/geolocation/wifi_data_provider_linux.cc
 | 
| diff --git a/content/browser/geolocation/wifi_data_provider_linux.cc b/content/browser/geolocation/wifi_data_provider_linux.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..f0afc66d9bb9873d6953817295b9a8c9c165c89a
 | 
| --- /dev/null
 | 
| +++ b/content/browser/geolocation/wifi_data_provider_linux.cc
 | 
| @@ -0,0 +1,381 @@
 | 
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +// Provides wifi scan API binding for suitable for typical linux distributions.
 | 
| +// Currently, only the NetworkManager API is used, accessed via D-Bus (in turn
 | 
| +// accessed via the GLib wrapper).
 | 
| +
 | 
| +#include "content/browser/geolocation/wifi_data_provider_linux.h"
 | 
| +
 | 
| +#include <stddef.h>
 | 
| +#include <stdint.h>
 | 
| +
 | 
| +#include <memory>
 | 
| +
 | 
| +#include "base/macros.h"
 | 
| +#include "base/strings/string_number_conversions.h"
 | 
| +#include "base/strings/utf_string_conversions.h"
 | 
| +#include "content/browser/geolocation/wifi_data_provider_manager.h"
 | 
| +#include "dbus/bus.h"
 | 
| +#include "dbus/message.h"
 | 
| +#include "dbus/object_path.h"
 | 
| +#include "dbus/object_proxy.h"
 | 
| +
 | 
| +namespace content {
 | 
| +namespace {
 | 
| +// The time periods between successive polls of the wifi data.
 | 
| +const int kDefaultPollingIntervalMilliseconds = 10 * 1000;  // 10s
 | 
| +const int kNoChangePollingIntervalMilliseconds = 2 * 60 * 1000;  // 2 mins
 | 
| +const int kTwoNoChangePollingIntervalMilliseconds = 10 * 60 * 1000;  // 10 mins
 | 
| +const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
 | 
| +
 | 
| +const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
 | 
| +const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
 | 
| +const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
 | 
| +
 | 
| +// From http://projects.gnome.org/NetworkManager/developers/spec.html
 | 
| +enum { NM_DEVICE_TYPE_WIFI = 2 };
 | 
| +
 | 
| +// Wifi API binding to NetworkManager, to allow reuse of the polling behavior
 | 
| +// defined in WifiDataProviderCommon.
 | 
| +// TODO(joth): NetworkManager also allows for notification based handling,
 | 
| +// however this will require reworking of the threading code to run a GLib
 | 
| +// event loop (GMainLoop).
 | 
| +class NetworkManagerWlanApi : public WifiDataProviderCommon::WlanApiInterface {
 | 
| + public:
 | 
| +  NetworkManagerWlanApi();
 | 
| +  ~NetworkManagerWlanApi() override;
 | 
| +
 | 
| +  // Must be called before any other interface method. Will return false if the
 | 
| +  // NetworkManager session cannot be created (e.g. not present on this distro),
 | 
| +  // in which case no other method may be called.
 | 
| +  bool Init();
 | 
| +
 | 
| +  // Similar to Init() but can inject the bus object. Used for testing.
 | 
| +  bool InitWithBus(dbus::Bus* bus);
 | 
| +
 | 
| +  // WifiDataProviderCommon::WlanApiInterface
 | 
| +  //
 | 
| +  // This function makes blocking D-Bus calls, but it's totally fine as
 | 
| +  // the code runs in "Geolocation" thread, not the browser's UI thread.
 | 
| +  bool GetAccessPointData(WifiData::AccessPointDataSet* data) override;
 | 
| +
 | 
| + private:
 | 
| +  // Enumerates the list of available network adapter devices known to
 | 
| +  // NetworkManager. Return true on success.
 | 
| +  bool GetAdapterDeviceList(std::vector<dbus::ObjectPath>* device_paths);
 | 
| +
 | 
| +  // Given the NetworkManager path to a wireless adapater, dumps the wifi scan
 | 
| +  // results and appends them to |data|. Returns false if a fatal error is
 | 
| +  // encountered such that the data set could not be populated.
 | 
| +  bool GetAccessPointsForAdapter(const dbus::ObjectPath& adapter_path,
 | 
| +                                 WifiData::AccessPointDataSet* data);
 | 
| +
 | 
| +  // Internal method used by |GetAccessPointsForAdapter|, given a wifi access
 | 
| +  // point proxy retrieves the named property and returns it. Returns NULL in
 | 
| +  // a scoped_ptr if the property could not be read.
 | 
| +  std::unique_ptr<dbus::Response> GetAccessPointProperty(
 | 
| +      dbus::ObjectProxy* proxy,
 | 
| +      const std::string& property_name);
 | 
| +
 | 
| +  scoped_refptr<dbus::Bus> system_bus_;
 | 
| +  dbus::ObjectProxy* network_manager_proxy_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(NetworkManagerWlanApi);
 | 
| +};
 | 
| +
 | 
| +// Convert a wifi frequency to the corresponding channel. Adapted from
 | 
| +// geolocaiton/wifilib.cc in googleclient (internal to google).
 | 
| +int frquency_in_khz_to_channel(int frequency_khz) {
 | 
| +  if (frequency_khz >= 2412000 && frequency_khz <= 2472000)  // Channels 1-13.
 | 
| +    return (frequency_khz - 2407000) / 5000;
 | 
| +  if (frequency_khz == 2484000)
 | 
| +    return 14;
 | 
| +  if (frequency_khz > 5000000 && frequency_khz < 6000000)  // .11a bands.
 | 
| +    return (frequency_khz - 5000000) / 5000;
 | 
| +  // Ignore everything else.
 | 
| +  return AccessPointData().channel;  // invalid channel
 | 
| +}
 | 
| +
 | 
| +NetworkManagerWlanApi::NetworkManagerWlanApi()
 | 
| +    : network_manager_proxy_(NULL) {
 | 
| +}
 | 
| +
 | 
| +NetworkManagerWlanApi::~NetworkManagerWlanApi() {
 | 
| +  // Close the connection.
 | 
| +  system_bus_->ShutdownAndBlock();
 | 
| +}
 | 
| +
 | 
| +bool NetworkManagerWlanApi::Init() {
 | 
| +  dbus::Bus::Options options;
 | 
| +  options.bus_type = dbus::Bus::SYSTEM;
 | 
| +  options.connection_type = dbus::Bus::PRIVATE;
 | 
| +  return InitWithBus(new dbus::Bus(options));
 | 
| +}
 | 
| +
 | 
| +bool NetworkManagerWlanApi::InitWithBus(dbus::Bus* bus) {
 | 
| +  system_bus_ = bus;
 | 
| +  // system_bus_ will own all object proxies created from the bus.
 | 
| +  network_manager_proxy_ =
 | 
| +      system_bus_->GetObjectProxy(kNetworkManagerServiceName,
 | 
| +                                  dbus::ObjectPath(kNetworkManagerPath));
 | 
| +  // Validate the proxy object by checking we can enumerate devices.
 | 
| +  std::vector<dbus::ObjectPath> adapter_paths;
 | 
| +  const bool success = GetAdapterDeviceList(&adapter_paths);
 | 
| +  VLOG(1) << "Init() result:  " << success;
 | 
| +  return success;
 | 
| +}
 | 
| +
 | 
| +bool NetworkManagerWlanApi::GetAccessPointData(
 | 
| +    WifiData::AccessPointDataSet* data) {
 | 
| +  std::vector<dbus::ObjectPath> device_paths;
 | 
| +  if (!GetAdapterDeviceList(&device_paths)) {
 | 
| +    LOG(WARNING) << "Could not enumerate access points";
 | 
| +    return false;
 | 
| +  }
 | 
| +  int success_count = 0;
 | 
| +  int fail_count = 0;
 | 
| +
 | 
| +  // Iterate the devices, getting APs for each wireless adapter found
 | 
| +  for (size_t i = 0; i < device_paths.size(); ++i) {
 | 
| +    const dbus::ObjectPath& device_path = device_paths[i];
 | 
| +    VLOG(1) << "Checking device: " << device_path.value();
 | 
| +
 | 
| +    dbus::ObjectProxy* device_proxy =
 | 
| +        system_bus_->GetObjectProxy(kNetworkManagerServiceName,
 | 
| +                                    device_path);
 | 
| +
 | 
| +    dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
 | 
| +    dbus::MessageWriter builder(&method_call);
 | 
| +    builder.AppendString("org.freedesktop.NetworkManager.Device");
 | 
| +    builder.AppendString("DeviceType");
 | 
| +    std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
 | 
| +        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
 | 
| +    if (!response) {
 | 
| +      LOG(WARNING) << "Failed to get the device type for "
 | 
| +                   << device_path.value();
 | 
| +      continue;  // Check the next device.
 | 
| +    }
 | 
| +    dbus::MessageReader reader(response.get());
 | 
| +    uint32_t device_type = 0;
 | 
| +    if (!reader.PopVariantOfUint32(&device_type)) {
 | 
| +      LOG(WARNING) << "Unexpected response for " << device_type << ": "
 | 
| +                   << response->ToString();
 | 
| +      continue;  // Check the next device.
 | 
| +    }
 | 
| +    VLOG(1) << "Device type: " << device_type;
 | 
| +
 | 
| +    if (device_type == NM_DEVICE_TYPE_WIFI) {  // Found a wlan adapter
 | 
| +      if (GetAccessPointsForAdapter(device_path, data))
 | 
| +        ++success_count;
 | 
| +      else
 | 
| +        ++fail_count;
 | 
| +    }
 | 
| +  }
 | 
| +  // At least one successful scan overrides any other adapter reporting error.
 | 
| +  return success_count || fail_count == 0;
 | 
| +}
 | 
| +
 | 
| +bool NetworkManagerWlanApi::GetAdapterDeviceList(
 | 
| +    std::vector<dbus::ObjectPath>* device_paths) {
 | 
| +  dbus::MethodCall method_call(kNetworkManagerInterface, "GetDevices");
 | 
| +  std::unique_ptr<dbus::Response> response(
 | 
| +      network_manager_proxy_->CallMethodAndBlock(
 | 
| +          &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
 | 
| +  if (!response) {
 | 
| +    LOG(WARNING) << "Failed to get the device list";
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  dbus::MessageReader reader(response.get());
 | 
| +  if (!reader.PopArrayOfObjectPaths(device_paths)) {
 | 
| +    LOG(WARNING) << "Unexpected response: " << response->ToString();
 | 
| +    return false;
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +bool NetworkManagerWlanApi::GetAccessPointsForAdapter(
 | 
| +    const dbus::ObjectPath& adapter_path, WifiData::AccessPointDataSet* data) {
 | 
| +  // Create a proxy object for this wifi adapter, and ask it to do a scan
 | 
| +  // (or at least, dump its scan results).
 | 
| +  dbus::ObjectProxy* device_proxy =
 | 
| +      system_bus_->GetObjectProxy(kNetworkManagerServiceName,
 | 
| +                                  adapter_path);
 | 
| +  dbus::MethodCall method_call(
 | 
| +      "org.freedesktop.NetworkManager.Device.Wireless",
 | 
| +      "GetAccessPoints");
 | 
| +  std::unique_ptr<dbus::Response> response(device_proxy->CallMethodAndBlock(
 | 
| +      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
 | 
| +  if (!response) {
 | 
| +    LOG(WARNING) << "Failed to get access points data for "
 | 
| +                 << adapter_path.value();
 | 
| +    return false;
 | 
| +  }
 | 
| +  dbus::MessageReader reader(response.get());
 | 
| +  std::vector<dbus::ObjectPath> access_point_paths;
 | 
| +  if (!reader.PopArrayOfObjectPaths(&access_point_paths)) {
 | 
| +    LOG(WARNING) << "Unexpected response for " << adapter_path.value() << ": "
 | 
| +                 << response->ToString();
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  VLOG(1) << "Wireless adapter " << adapter_path.value() << " found "
 | 
| +          << access_point_paths.size() << " access points.";
 | 
| +
 | 
| +  for (size_t i = 0; i < access_point_paths.size(); ++i) {
 | 
| +    const dbus::ObjectPath& access_point_path = access_point_paths[i];
 | 
| +    VLOG(1) << "Checking access point: " << access_point_path.value();
 | 
| +
 | 
| +    dbus::ObjectProxy* access_point_proxy =
 | 
| +        system_bus_->GetObjectProxy(kNetworkManagerServiceName,
 | 
| +                                    access_point_path);
 | 
| +
 | 
| +    AccessPointData access_point_data;
 | 
| +    {
 | 
| +      std::unique_ptr<dbus::Response> response(
 | 
| +          GetAccessPointProperty(access_point_proxy, "Ssid"));
 | 
| +      if (!response)
 | 
| +        continue;
 | 
| +      // The response should contain a variant that contains an array of bytes.
 | 
| +      dbus::MessageReader reader(response.get());
 | 
| +      dbus::MessageReader variant_reader(response.get());
 | 
| +      if (!reader.PopVariant(&variant_reader)) {
 | 
| +        LOG(WARNING) << "Unexpected response for " << access_point_path.value()
 | 
| +                     << ": " << response->ToString();
 | 
| +        continue;
 | 
| +      }
 | 
| +      const uint8_t* ssid_bytes = NULL;
 | 
| +      size_t ssid_length = 0;
 | 
| +      if (!variant_reader.PopArrayOfBytes(&ssid_bytes, &ssid_length)) {
 | 
| +        LOG(WARNING) << "Unexpected response for " << access_point_path.value()
 | 
| +                     << ": " << response->ToString();
 | 
| +        continue;
 | 
| +      }
 | 
| +      std::string ssid(ssid_bytes, ssid_bytes + ssid_length);
 | 
| +      access_point_data.ssid = base::UTF8ToUTF16(ssid);
 | 
| +    }
 | 
| +
 | 
| +    { // Read the mac address
 | 
| +      std::unique_ptr<dbus::Response> response(
 | 
| +          GetAccessPointProperty(access_point_proxy, "HwAddress"));
 | 
| +      if (!response)
 | 
| +        continue;
 | 
| +      dbus::MessageReader reader(response.get());
 | 
| +      std::string mac;
 | 
| +      if (!reader.PopVariantOfString(&mac)) {
 | 
| +        LOG(WARNING) << "Unexpected response for " << access_point_path.value()
 | 
| +                     << ": " << response->ToString();
 | 
| +        continue;
 | 
| +      }
 | 
| +
 | 
| +      base::ReplaceSubstringsAfterOffset(&mac, 0U, ":", base::StringPiece());
 | 
| +      std::vector<uint8_t> mac_bytes;
 | 
| +      if (!base::HexStringToBytes(mac, &mac_bytes) || mac_bytes.size() != 6) {
 | 
| +        LOG(WARNING) << "Can't parse mac address (found " << mac_bytes.size()
 | 
| +                     << " bytes) so using raw string: " << mac;
 | 
| +        access_point_data.mac_address = base::UTF8ToUTF16(mac);
 | 
| +      } else {
 | 
| +        access_point_data.mac_address = MacAddressAsString16(&mac_bytes[0]);
 | 
| +      }
 | 
| +    }
 | 
| +
 | 
| +    {  // Read signal strength.
 | 
| +      std::unique_ptr<dbus::Response> response(
 | 
| +          GetAccessPointProperty(access_point_proxy, "Strength"));
 | 
| +      if (!response)
 | 
| +        continue;
 | 
| +      dbus::MessageReader reader(response.get());
 | 
| +      uint8_t strength = 0;
 | 
| +      if (!reader.PopVariantOfByte(&strength)) {
 | 
| +        LOG(WARNING) << "Unexpected response for " << access_point_path.value()
 | 
| +                     << ": " << response->ToString();
 | 
| +        continue;
 | 
| +      }
 | 
| +      // Convert strength as a percentage into dBs.
 | 
| +      access_point_data.radio_signal_strength = -100 + strength / 2;
 | 
| +    }
 | 
| +
 | 
| +    { // Read the channel
 | 
| +      std::unique_ptr<dbus::Response> response(
 | 
| +          GetAccessPointProperty(access_point_proxy, "Frequency"));
 | 
| +      if (!response)
 | 
| +        continue;
 | 
| +      dbus::MessageReader reader(response.get());
 | 
| +      uint32_t frequency = 0;
 | 
| +      if (!reader.PopVariantOfUint32(&frequency)) {
 | 
| +        LOG(WARNING) << "Unexpected response for " << access_point_path.value()
 | 
| +                     << ": " << response->ToString();
 | 
| +        continue;
 | 
| +      }
 | 
| +
 | 
| +      // NetworkManager returns frequency in MHz.
 | 
| +      access_point_data.channel =
 | 
| +          frquency_in_khz_to_channel(frequency * 1000);
 | 
| +    }
 | 
| +    VLOG(1) << "Access point data of " << access_point_path.value() << ": "
 | 
| +            << "SSID: " << access_point_data.ssid << ", "
 | 
| +            << "MAC: " << access_point_data.mac_address << ", "
 | 
| +            << "Strength: " << access_point_data.radio_signal_strength << ", "
 | 
| +            << "Channel: " << access_point_data.channel;
 | 
| +
 | 
| +    data->insert(access_point_data);
 | 
| +  }
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +std::unique_ptr<dbus::Response> NetworkManagerWlanApi::GetAccessPointProperty(
 | 
| +    dbus::ObjectProxy* access_point_proxy,
 | 
| +    const std::string& property_name) {
 | 
| +  dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
 | 
| +  dbus::MessageWriter builder(&method_call);
 | 
| +  builder.AppendString("org.freedesktop.NetworkManager.AccessPoint");
 | 
| +  builder.AppendString(property_name);
 | 
| +  std::unique_ptr<dbus::Response> response =
 | 
| +      access_point_proxy->CallMethodAndBlock(
 | 
| +          &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
 | 
| +  if (!response) {
 | 
| +    LOG(WARNING) << "Failed to get property for " << property_name;
 | 
| +  }
 | 
| +  return response;
 | 
| +}
 | 
| +
 | 
| +}  // namespace
 | 
| +
 | 
| +// static
 | 
| +WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
 | 
| +  return new WifiDataProviderLinux();
 | 
| +}
 | 
| +
 | 
| +WifiDataProviderLinux::WifiDataProviderLinux() {
 | 
| +}
 | 
| +
 | 
| +WifiDataProviderLinux::~WifiDataProviderLinux() {
 | 
| +}
 | 
| +
 | 
| +WifiDataProviderCommon::WlanApiInterface*
 | 
| +WifiDataProviderLinux::NewWlanApi() {
 | 
| +  std::unique_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi);
 | 
| +  if (wlan_api->Init())
 | 
| +    return wlan_api.release();
 | 
| +  return NULL;
 | 
| +}
 | 
| +
 | 
| +WifiPollingPolicy* WifiDataProviderLinux::NewPollingPolicy() {
 | 
| +  return new GenericWifiPollingPolicy<kDefaultPollingIntervalMilliseconds,
 | 
| +                                      kNoChangePollingIntervalMilliseconds,
 | 
| +                                      kTwoNoChangePollingIntervalMilliseconds,
 | 
| +                                      kNoWifiPollingIntervalMilliseconds>;
 | 
| +}
 | 
| +
 | 
| +WifiDataProviderCommon::WlanApiInterface*
 | 
| +WifiDataProviderLinux::NewWlanApiForTesting(dbus::Bus* bus) {
 | 
| +  std::unique_ptr<NetworkManagerWlanApi> wlan_api(new NetworkManagerWlanApi);
 | 
| +  if (wlan_api->InitWithBus(bus))
 | 
| +    return wlan_api.release();
 | 
| +  return NULL;
 | 
| +}
 | 
| +
 | 
| +}  // namespace content
 | 
| 
 |