// Copyright (c) 2010 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. // For OSX 10.5 we use the system API function WirelessScanSplit. This function // is not documented or included in the SDK, so we use a reverse-engineered // header, osx_wifi_.h. This file is taken from the iStumbler project // (http://www.istumbler.net). #include "chrome/browser/geolocation/wifi_data_provider_mac.h" #include #include #include "base/utf_string_conversions.h" #include "chrome/browser/geolocation/osx_wifi.h" #include "chrome/browser/geolocation/wifi_data_provider_common.h" namespace { // The time periods, in milliseconds, between successive polls of the wifi data. const int kDefaultPollingInterval = 120000; // 2 mins const int kNoChangePollingInterval = 300000; // 5 mins const int kTwoNoChangePollingInterval = 600000; // 10 mins const int kNoWifiPollingIntervalMilliseconds = 20 * 1000; // 20s // Provides the wifi API binding for use when running on OSX 10.5 machines using // the Apple80211 framework. class Apple80211Api : public WifiDataProviderCommon::WlanApiInterface { public: Apple80211Api(); virtual ~Apple80211Api(); // Must be called before any other interface method. Will return false if the // Apple80211 framework cannot be initialized (e.g. running on post-10.5 OSX), // in which case no other method may be called. bool Init(); // WlanApiInterface virtual bool GetAccessPointData(WifiData::AccessPointDataSet* data); private: // Handle, context and function pointers for Apple80211 library. void* apple_80211_library_; WirelessContext* wifi_context_; WirelessAttachFunction WirelessAttach_function_; WirelessScanSplitFunction WirelessScanSplit_function_; WirelessDetachFunction WirelessDetach_function_; WifiData wifi_data_; DISALLOW_COPY_AND_ASSIGN(Apple80211Api); }; Apple80211Api::Apple80211Api() : apple_80211_library_(NULL), wifi_context_(NULL), WirelessAttach_function_(NULL), WirelessScanSplit_function_(NULL), WirelessDetach_function_(NULL) { } Apple80211Api::~Apple80211Api() { if (WirelessDetach_function_) (*WirelessDetach_function_)(wifi_context_); dlclose(apple_80211_library_); } bool Apple80211Api::Init() { DLOG(INFO) << "Apple80211Api::Init"; apple_80211_library_ = dlopen( "/System/Library/PrivateFrameworks/Apple80211.framework/Apple80211", RTLD_LAZY); if (!apple_80211_library_) { DLOG(WARNING) << "Could not open Apple80211 library"; return false; } WirelessAttach_function_ = reinterpret_cast( dlsym(apple_80211_library_, "WirelessAttach")); WirelessScanSplit_function_ = reinterpret_cast( dlsym(apple_80211_library_, "WirelessScanSplit")); WirelessDetach_function_ = reinterpret_cast( dlsym(apple_80211_library_, "WirelessDetach")); DCHECK(WirelessAttach_function_); DCHECK(WirelessScanSplit_function_); DCHECK(WirelessDetach_function_); if (!WirelessAttach_function_ || !WirelessScanSplit_function_ || !WirelessDetach_function_) { DLOG(WARNING) << "Symbol error. Attach: " << !!WirelessAttach_function_ << " Split: " << !!WirelessScanSplit_function_ << " Detach: " << !!WirelessDetach_function_; return false; } WIErr err = (*WirelessAttach_function_)(&wifi_context_, 0); if (err != noErr) { DLOG(WARNING) << "Error attaching: " << err; return false; } return true; } bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) { DLOG(INFO) << "Apple80211Api::GetAccessPointData"; DCHECK(data); DCHECK(WirelessScanSplit_function_); CFArrayRef managed_access_points = NULL; CFArrayRef adhoc_access_points = NULL; WIErr err = (*WirelessScanSplit_function_)(wifi_context_, &managed_access_points, &adhoc_access_points, 0); if (err != noErr) { DLOG(WARNING) << "Error spliting scan: " << err; return false; } if (managed_access_points == NULL) { DLOG(WARNING) << "managed_access_points == NULL"; return false; } int num_access_points = CFArrayGetCount(managed_access_points); DLOG(INFO) << "Found " << num_access_points << " managed access points"; for (int i = 0; i < num_access_points; ++i) { const WirelessNetworkInfo* access_point_info = reinterpret_cast( CFDataGetBytePtr( reinterpret_cast( CFArrayGetValueAtIndex(managed_access_points, i)))); // Currently we get only MAC address, signal strength, channel // signal-to-noise and SSID AccessPointData access_point_data; access_point_data.mac_address = MacAddressAsString16(access_point_info->macAddress); // WirelessNetworkInfo::signal appears to be signal strength in dBm. access_point_data.radio_signal_strength = access_point_info->signal; access_point_data.channel = access_point_info->channel; // WirelessNetworkInfo::noise appears to be noise floor in dBm. access_point_data.signal_to_noise = access_point_info->signal - access_point_info->noise; if (!UTF8ToUTF16(reinterpret_cast(access_point_info->name), access_point_info->nameLen, &access_point_data.ssid)) { access_point_data.ssid.clear(); } data->insert(access_point_data); } return true; } } // namespace // static template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { return new MacWifiDataProvider(); } MacWifiDataProvider::MacWifiDataProvider() { } MacWifiDataProvider::~MacWifiDataProvider() { } MacWifiDataProvider::WlanApiInterface* MacWifiDataProvider::NewWlanApi() { // Try and find a API binding that works: first try the officially supported // CoreWLAN API, and if this fails (e.g. on OSX 10.5) fall back to the reverse // engineered Apple80211 API. MacWifiDataProvider::WlanApiInterface* core_wlan_api = NewCoreWlanApi(); if (core_wlan_api) return core_wlan_api; scoped_ptr wlan_api(new Apple80211Api); if (wlan_api->Init()) return wlan_api.release(); DLOG(INFO) << "MacWifiDataProvider : failed to initialize any wlan api"; return NULL; } PollingPolicyInterface* MacWifiDataProvider::NewPollingPolicy() { return new GenericPollingPolicy; }