diff options
author | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-08 11:11:24 +0000 |
---|---|---|
committer | joth@chromium.org <joth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-08 11:11:24 +0000 |
commit | 5eaf62787c9cbe75a8e016666d6dddf592e693b1 (patch) | |
tree | d54e09340ca6a613fc00f61f81978041cdab8f91 /chrome/browser/geolocation | |
parent | 3b930768ef8432b472a12d44d84b9fe6555b4ba9 (diff) | |
download | chromium_src-5eaf62787c9cbe75a8e016666d6dddf592e693b1.zip chromium_src-5eaf62787c9cbe75a8e016666d6dddf592e693b1.tar.gz chromium_src-5eaf62787c9cbe75a8e016666d6dddf592e693b1.tar.bz2 |
Add support for geolocation wifi scanning on OSX 10.6
BUG=http://crbug.com/37198
TEST=Run browser with --enable-geolocation on OSX 10.6, open maps.google.com
Review URL: http://codereview.chromium.org/669083
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40891 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/geolocation')
3 files changed, 200 insertions, 33 deletions
diff --git a/chrome/browser/geolocation/wifi_data_provider_corewlan_mac.mm b/chrome/browser/geolocation/wifi_data_provider_corewlan_mac.mm new file mode 100644 index 0000000..da960fc --- /dev/null +++ b/chrome/browser/geolocation/wifi_data_provider_corewlan_mac.mm @@ -0,0 +1,156 @@ +// 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. + +// Implements a WLAN API binding for CoreWLAN, as available on OSX 10.6 + +#include "chrome/browser/geolocation/wifi_data_provider_mac.h" + +#import <Foundation/Foundation.h> + +#include "base/scoped_nsautorelease_pool.h" +#include "base/scoped_nsobject.h" + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 +// If building on 10.6 we can include the framework header directly. +#import <CoreWLAN/CoreWLAN.h> + +#else +// Otherwise just define the interfaces we require. Class definitions required +// as we treat warnings as errors so we can't pass message to an untyped id. + +@interface CWInterface : NSObject ++ (CWInterface*)interface; +- (NSArray*)scanForNetworksWithParameters:(NSDictionary*)parameters + error:(NSError**)error; +@end + +@interface CWNetwork : NSObject <NSCopying, NSCoding> +@property(readonly) NSString* ssid; +@property(readonly) NSString* bssid; +@property(readonly) NSData* bssidData; +@property(readonly) NSNumber* securityMode; +@property(readonly) NSNumber* phyMode; +@property(readonly) NSNumber* channel; +@property(readonly) NSNumber* rssi; +@property(readonly) NSNumber* noise; +@property(readonly) NSData* ieData; +@property(readonly) BOOL isIBSS; +- (BOOL)isEqualToNetwork:(CWNetwork*)network; +@end + +// String literals derived empirically on an OSX 10.6 machine (XCode 3.2.1). +// Commented out to avoid unused variables warnings; comment back in as needed. + +// static const NSString* kCWAssocKey8021XProfile = @"ASSOC_KEY_8021X_PROFILE"; +// static const NSString* kCWAssocKeyPassphrase = @"ASSOC_KEY_PASSPHRASE"; +// static const NSString* kCWBSSIDDidChangeNotification = +// @"BSSID_CHANGED_NOTIFICATION"; +// static const NSString* kCWCountryCodeDidChangeNotification = +// @"COUNTRY_CODE_CHANGED_NOTIFICATION"; +// static const NSString* kCWErrorDomain = @"APPLE80211_ERROR_DOMAIN"; +// static const NSString* kCWIBSSKeyChannel = @"IBSS_KEY_CHANNEL"; +// static const NSString* kCWIBSSKeyPassphrase = @"IBSS_KEY_PASSPHRASE"; +// static const NSString* kCWIBSSKeySSID = @"IBSS_KEY_SSID"; +// static const NSString* kCWLinkDidChangeNotification = +// @"LINK_CHANGED_NOTIFICATION"; +// static const NSString* kCWModeDidChangeNotification = +// @"MODE_CHANGED_NOTIFICATION"; +// static const NSString* kCWPowerDidChangeNotification = +// @"POWER_CHANGED_NOTIFICATION"; +// static const NSString* kCWScanKeyBSSID = @"BSSID"; +// static const NSString* kCWScanKeyDwellTime = @"SCAN_DWELL_TIME"; +static const NSString* kCWScanKeyMerge = @"SCAN_MERGE"; +// static const NSString* kCWScanKeyRestTime = @"SCAN_REST_TIME"; +// static const NSString* kCWScanKeyScanType = @"SCAN_TYPE"; +// static const NSString* kCWScanKeySSID = @"SSID_STR"; +// static const NSString* kCWSSIDDidChangeNotification = +// @"SSID_CHANGED_NOTIFICATION"; + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 + +class CoreWlanApi : public WifiDataProviderCommon::WlanApiInterface { + public: + CoreWlanApi() {} + + // Must be called before any other interface method. Will return false if the + // CoreWLAN framework cannot be initialized (e.g. running on pre-10.6 OSX), + // in which case no other method may be called. + bool Init(); + + // WlanApiInterface + virtual bool GetAccessPointData(WifiData::AccessPointDataSet* data); + + private: + scoped_nsobject<NSBundle> bundle_; + scoped_nsobject<CWInterface> corewlan_interface_; + + DISALLOW_COPY_AND_ASSIGN(CoreWlanApi); +}; + +bool CoreWlanApi::Init() { + // As the WLAN api binding runs on its own thread, we need to provide our own + // auto release pool. It's simplest to do this as an automatic variable in + // each method that needs it, to ensure the scoping is correct and does not + // interfere with any other code using autorelease pools on the thread. + base::ScopedNSAutoreleasePool auto_pool; + bundle_.reset([[NSBundle alloc] + initWithPath:@"/System/Library/Frameworks/CoreWLAN.framework"]); + if (!bundle_) { + DLOG(INFO) << "Failed to load the CoreWLAN framework bundle"; + return false; + } + corewlan_interface_.reset([[bundle_ classNamed:@"CWInterface"] interface]); + if (!corewlan_interface_) { + DLOG(INFO) << "Failed to create the CWInterface instance"; + return false; + } + [corewlan_interface_ retain]; + return true; +} + +bool CoreWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { + base::ScopedNSAutoreleasePool auto_pool; + DCHECK(corewlan_interface_); + NSError* err = nil; + // Initialize the scan parameters with scan key merging disabled, so we get + // every AP listed in the scan without any SSID de-duping logic. + NSDictionary* params = + [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO] + forKey:kCWScanKeyMerge]; + + NSArray* scan = [corewlan_interface_ scanForNetworksWithParameters:params + error:&err]; + + const int error_code = [err code]; + const int count = [scan count]; + if (error_code && !count) { + DLOG(WARNING) << "CoreWLAN scan failed " << error_code; + return false; + } + DLOG(INFO) << "Found " << count << " wifi APs"; + + for (CWNetwork* network in scan) { + DCHECK(network); + AccessPointData access_point_data; + NSData* mac = [network bssidData]; + DCHECK([mac length] == 6); + access_point_data.mac_address = MacAddressAsString16( + static_cast<const uint8*>([mac bytes])); + access_point_data.radio_signal_strength = [[network rssi] intValue]; + access_point_data.channel = [[network channel] intValue]; + access_point_data.signal_to_noise = + access_point_data.radio_signal_strength - [[network noise] intValue]; + access_point_data.ssid = UTF8ToUTF16([[network ssid] UTF8String]); + data->insert(access_point_data); + } + return true; +} + +WifiDataProviderCommon::WlanApiInterface* NewCoreWlanApi() { + scoped_ptr<CoreWlanApi> self(new CoreWlanApi); + if (self->Init()) + return self.release(); + + return NULL; +} diff --git a/chrome/browser/geolocation/wifi_data_provider_mac.cc b/chrome/browser/geolocation/wifi_data_provider_mac.cc index df332e1..b669e74 100644 --- a/chrome/browser/geolocation/wifi_data_provider_mac.cc +++ b/chrome/browser/geolocation/wifi_data_provider_mac.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// We use the OSX 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 +// 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" @@ -21,10 +21,12 @@ const int kDefaultPollingInterval = 120000; // 2 mins const int kNoChangePollingInterval = 300000; // 5 mins const int kTwoNoChangePollingInterval = 600000; // 10 mins -class OsxWlanApi : public WifiDataProviderCommon::WlanApiInterface { +// Provides the wifi API binding for use when running on OSX 10.5 machines using +// the Apple80211 framework. +class Apple80211Api : public WifiDataProviderCommon::WlanApiInterface { public: - OsxWlanApi(); - virtual ~OsxWlanApi(); + Apple80211Api(); + virtual ~Apple80211Api(); bool Init(); @@ -42,20 +44,20 @@ class OsxWlanApi : public WifiDataProviderCommon::WlanApiInterface { WifiData wifi_data_; }; -OsxWlanApi::OsxWlanApi() +Apple80211Api::Apple80211Api() : apple_80211_library_(NULL), wifi_context_(NULL), WirelessAttach_function_(NULL), WirelessScanSplit_function_(NULL), WirelessDetach_function_(NULL) { } -OsxWlanApi::~OsxWlanApi() { +Apple80211Api::~Apple80211Api() { if (WirelessDetach_function_) (*WirelessDetach_function_)(wifi_context_); dlclose(apple_80211_library_); } -bool OsxWlanApi::Init() { - DLOG(INFO) << "OsxWlanApi::Init"; +bool Apple80211Api::Init() { + DLOG(INFO) << "Apple80211Api::Init"; apple_80211_library_ = dlopen( "/System/Library/PrivateFrameworks/Apple80211.framework/Apple80211", RTLD_LAZY); @@ -89,8 +91,8 @@ bool OsxWlanApi::Init() { return true; } -bool OsxWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { - DLOG(INFO) << "OsxWlanApi::GetAccessPointData"; +bool Apple80211Api::GetAccessPointData(WifiData::AccessPointDataSet* data) { + DLOG(INFO) << "Apple80211Api::GetAccessPointData"; DCHECK(data); DCHECK(WirelessScanSplit_function_); CFArrayRef managed_access_points = NULL; @@ -110,7 +112,7 @@ bool OsxWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { } int num_access_points = CFArrayGetCount(managed_access_points); - DLOG(INFO) << "Found " << num_access_points << " 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<const WirelessNetworkInfo*>( @@ -120,7 +122,6 @@ bool OsxWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { // Currently we get only MAC address, signal strength, channel // signal-to-noise and SSID - // TODO(steveblock): Work out how to get age. AccessPointData access_point_data; access_point_data.mac_address = MacAddressAsString16(access_point_info->macAddress); @@ -135,10 +136,6 @@ bool OsxWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { &access_point_data.ssid)) { access_point_data.ssid.clear(); } - - DLOG(INFO) << " AP " << i - << " mac: " << access_point_data.mac_address - << " ssid: " << access_point_data.ssid; data->insert(access_point_data); } return true; @@ -148,25 +145,32 @@ bool OsxWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { // static template<> WifiDataProviderImplBase* WifiDataProvider::DefaultFactoryFunction() { - return new OsxWifiDataProvider(); + return new MacWifiDataProvider(); } -OsxWifiDataProvider::OsxWifiDataProvider() { +MacWifiDataProvider::MacWifiDataProvider() { } -OsxWifiDataProvider::~OsxWifiDataProvider() { +MacWifiDataProvider::~MacWifiDataProvider() { } -OsxWifiDataProvider::WlanApiInterface* OsxWifiDataProvider::NewWlanApi() { - scoped_ptr<OsxWlanApi> wlan_api(new OsxWlanApi); - if (!wlan_api->Init()) { - DLOG(INFO) << "OsxWifiDataProvider : failed to initialize wlan api"; - return NULL; - } - return wlan_api.release(); +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<Apple80211Api> wlan_api(new Apple80211Api); + if (wlan_api->Init()) + return wlan_api.release(); + + DLOG(INFO) << "MacWifiDataProvider : failed to initialize any wlan api"; + return NULL; } -PollingPolicyInterface* OsxWifiDataProvider::NewPollingPolicy() { +PollingPolicyInterface* MacWifiDataProvider::NewPollingPolicy() { return new GenericPollingPolicy<kDefaultPollingInterval, kNoChangePollingInterval, kTwoNoChangePollingInterval>; diff --git a/chrome/browser/geolocation/wifi_data_provider_mac.h b/chrome/browser/geolocation/wifi_data_provider_mac.h index a386c59..841176e 100644 --- a/chrome/browser/geolocation/wifi_data_provider_mac.h +++ b/chrome/browser/geolocation/wifi_data_provider_mac.h @@ -7,18 +7,25 @@ #include "chrome/browser/geolocation/wifi_data_provider_common.h" -class OsxWifiDataProvider : public WifiDataProviderCommon { +// Implementation of the wifi data provider for Mac OSX. Uses different API +// bindings depending on APIs detected available at runtime in order to access +// wifi scan data: Apple80211.h on OSX 10.5, CoreWLAN framework on OSX 10.6. +class MacWifiDataProvider : public WifiDataProviderCommon { public: - OsxWifiDataProvider(); + MacWifiDataProvider(); private: - virtual ~OsxWifiDataProvider(); + virtual ~MacWifiDataProvider(); // WifiDataProviderCommon virtual WlanApiInterface* NewWlanApi(); virtual PollingPolicyInterface* NewPollingPolicy(); - DISALLOW_COPY_AND_ASSIGN(OsxWifiDataProvider); + DISALLOW_COPY_AND_ASSIGN(MacWifiDataProvider); }; +// Creates and returns a new API binding for the CoreWLAN API, or NULL if the +// API can not be initialized. +WifiDataProviderCommon::WlanApiInterface* NewCoreWlanApi(); + #endif // CHROME_BROWSER_GEOLOCATION_WIFI_DATA_PROVIDER_MAC_H_ |