Home | History | Annotate | Download | only in geolocation
      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 // For OSX 10.5 we use the system API function WirelessScanSplit. This function
      6 // is not documented or included in the SDK, so we use a reverse-engineered
      7 // header, osx_wifi_.h. This file is taken from the iStumbler project
      8 // (http://www.istumbler.net).
      9 
     10 #include "content/browser/geolocation/wifi_data_provider_mac.h"
     11 
     12 #include <dlfcn.h>
     13 #include <stdio.h>
     14 
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "content/browser/geolocation/osx_wifi.h"
     17 #include "content/browser/geolocation/wifi_data_provider_common.h"
     18 #include "content/browser/geolocation/wifi_data_provider_manager.h"
     19 
     20 namespace content {
     21 namespace {
     22 // The time periods, in milliseconds, between successive polls of the wifi data.
     23 const int kDefaultPollingInterval = 120000;  // 2 mins
     24 const int kNoChangePollingInterval = 300000;  // 5 mins
     25 const int kTwoNoChangePollingInterval = 600000;  // 10 mins
     26 const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s
     27 
     28 // Provides the wifi API binding for use when running on OSX 10.5 machines using
     29 // the Apple80211 framework.
     30 class Apple80211Api : public WifiDataProviderCommon::WlanApiInterface {
     31  public:
     32   Apple80211Api();
     33   virtual ~Apple80211Api();
     34 
     35   // Must be called before any other interface method. Will return false if the
     36   // Apple80211 framework cannot be initialized (e.g. running on post-10.5 OSX),
     37   // in which case no other method may be called.
     38   bool Init();
     39 
     40   // WlanApiInterface
     41   virtual bool GetAccessPointData(WifiData::AccessPointDataSet* data) OVERRIDE;
     42 
     43  private:
     44   // Handle, context and function pointers for Apple80211 library.
     45   void* apple_80211_library_;
     46   WirelessContext* wifi_context_;
     47   WirelessAttachFunction WirelessAttach_function_;
     48   WirelessScanSplitFunction WirelessScanSplit_function_;
     49   WirelessDetachFunction WirelessDetach_function_;
     50 
     51   WifiData wifi_data_;
     52 
     53   DISALLOW_COPY_AND_ASSIGN(Apple80211Api);
     54 };
     55 
     56 Apple80211Api::Apple80211Api()
     57     : apple_80211_library_(NULL), wifi_context_(NULL),
     58       WirelessAttach_function_(NULL), WirelessScanSplit_function_(NULL),
     59       WirelessDetach_function_(NULL) {
     60 }
     61 
     62 Apple80211Api::~Apple80211Api() {
     63   if (WirelessDetach_function_)
     64     (*WirelessDetach_function_)(wifi_context_);
     65   dlclose(apple_80211_library_);
     66 }
     67 
     68 bool Apple80211Api::Init() {
     69   DVLOG(1) << "Apple80211Api::Init";
     70   apple_80211_library_ = dlopen(
     71       "/System/Library/PrivateFrameworks/Apple80211.framework/Apple80211",
     72       RTLD_LAZY);
     73   if (!apple_80211_library_) {
     74     DLOG(WARNING) << "Could not open Apple80211 library";
     75     return false;
     76   }
     77   WirelessAttach_function_ = reinterpret_cast<WirelessAttachFunction>(
     78       dlsym(apple_80211_library_, "WirelessAttach"));
     79   WirelessScanSplit_function_ = reinterpret_cast<WirelessScanSplitFunction>(
     80       dlsym(apple_80211_library_, "WirelessScanSplit"));
     81   WirelessDetach_function_ = reinterpret_cast<WirelessDetachFunction>(
     82       dlsym(apple_80211_library_, "WirelessDetach"));
     83   DCHECK(WirelessAttach_function_);
     84   DCHECK(WirelessScanSplit_function_);
     85   DCHECK(WirelessDetach_function_);
     86 
     87   if (!WirelessAttach_function_ || !WirelessScanSplit_function_ ||
     88       !WirelessDetach_function_) {
     89     DLOG(WARNING) << "Symbol error. Attach: " << !!WirelessAttach_function_
     90         << " Split: " << !!WirelessScanSplit_function_
     91         << " Detach: " << !!WirelessDetach_function_;
     92     return false;
     93   }
     94 
     95   WIErr err = (*WirelessAttach_function_)(&wifi_context_, 0);
     96   if (err != noErr) {
     97     DLOG(WARNING) << "Error attaching: " << err;
     98     return false;
     99   }
    100   return true;
    101 }
    102 
    103 bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) {
    104   DVLOG(1) << "Apple80211Api::GetAccessPointData";
    105   DCHECK(data);
    106   DCHECK(WirelessScanSplit_function_);
    107   CFArrayRef managed_access_points = NULL;
    108   CFArrayRef adhoc_access_points = NULL;
    109   // Arrays returned here are owned by the caller.
    110   WIErr err = (*WirelessScanSplit_function_)(wifi_context_,
    111                                              &managed_access_points,
    112                                              &adhoc_access_points,
    113                                              0);
    114   if (err != noErr) {
    115     DLOG(WARNING) << "Error spliting scan: " << err;
    116     return false;
    117   }
    118 
    119   if (managed_access_points == NULL) {
    120     DLOG(WARNING) << "managed_access_points == NULL";
    121     return false;
    122   }
    123 
    124   int num_access_points = CFArrayGetCount(managed_access_points);
    125   DVLOG(1) << "Found " << num_access_points << " managed access points";
    126   for (int i = 0; i < num_access_points; ++i) {
    127     const WirelessNetworkInfo* access_point_info =
    128         reinterpret_cast<const WirelessNetworkInfo*>(
    129         CFDataGetBytePtr(
    130         reinterpret_cast<const CFDataRef>(
    131         CFArrayGetValueAtIndex(managed_access_points, i))));
    132 
    133     // Currently we get only MAC address, signal strength, channel
    134     // signal-to-noise and SSID
    135     AccessPointData access_point_data;
    136     access_point_data.mac_address =
    137         MacAddressAsString16(access_point_info->macAddress);
    138     // WirelessNetworkInfo::signal appears to be signal strength in dBm.
    139     access_point_data.radio_signal_strength = access_point_info->signal;
    140     access_point_data.channel = access_point_info->channel;
    141     // WirelessNetworkInfo::noise appears to be noise floor in dBm.
    142     access_point_data.signal_to_noise = access_point_info->signal -
    143                                         access_point_info->noise;
    144     if (!base::UTF8ToUTF16(reinterpret_cast<const char*>(
    145                                access_point_info->name),
    146                            access_point_info->nameLen,
    147                            &access_point_data.ssid)) {
    148       access_point_data.ssid.clear();
    149     }
    150     data->insert(access_point_data);
    151   }
    152 
    153   if (managed_access_points)
    154     CFRelease(managed_access_points);
    155   if (adhoc_access_points)
    156     CFRelease(adhoc_access_points);
    157 
    158   return true;
    159 }
    160 }  // namespace
    161 
    162 // static
    163 WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() {
    164   return new WifiDataProviderMac();
    165 }
    166 
    167 WifiDataProviderMac::WifiDataProviderMac() {
    168 }
    169 
    170 WifiDataProviderMac::~WifiDataProviderMac() {
    171 }
    172 
    173 WifiDataProviderMac::WlanApiInterface* WifiDataProviderMac::NewWlanApi() {
    174   // Try and find a API binding that works: first try the officially supported
    175   // CoreWLAN API, and if this fails (e.g. on OSX 10.5) fall back to the reverse
    176   // engineered Apple80211 API.
    177   WifiDataProviderMac::WlanApiInterface* core_wlan_api = NewCoreWlanApi();
    178   if (core_wlan_api)
    179     return core_wlan_api;
    180 
    181   scoped_ptr<Apple80211Api> wlan_api(new Apple80211Api);
    182   if (wlan_api->Init())
    183     return wlan_api.release();
    184 
    185   DVLOG(1) << "WifiDataProviderMac : failed to initialize any wlan api";
    186   return NULL;
    187 }
    188 
    189 WifiPollingPolicy* WifiDataProviderMac::NewPollingPolicy() {
    190   return new GenericWifiPollingPolicy<kDefaultPollingInterval,
    191                                       kNoChangePollingInterval,
    192                                       kTwoNoChangePollingInterval,
    193                                       kNoWifiPollingIntervalMilliseconds>;
    194 }
    195 
    196 }  // namespace content
    197