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