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