Index: device/geolocation/wifi_data_provider_mac.mm |
diff --git a/device/geolocation/wifi_data_provider_mac.mm b/device/geolocation/wifi_data_provider_mac.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a5f978bf7f70b1d5268df0d109625ba2ca6b5f8d |
--- /dev/null |
+++ b/device/geolocation/wifi_data_provider_mac.mm |
@@ -0,0 +1,135 @@ |
+// Copyright 2016 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. |
+ |
+#include "device/geolocation/wifi_data_provider_mac.h" |
+ |
+#import <CoreWLAN/CoreWLAN.h> |
+#import <Foundation/Foundation.h> |
+ |
+#include "base/mac/scoped_nsautorelease_pool.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/macros.h" |
+#include "base/metrics/histogram_macros.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "device/geolocation/wifi_data_provider_common.h" |
+#include "device/geolocation/wifi_data_provider_manager.h" |
+ |
+extern "C" NSString* const kCWScanKeyMerge; |
+ |
+@interface CWInterface (Private) |
+- (NSArray*)scanForNetworksWithParameters:(NSDictionary*)params |
+ error:(NSError**)error; |
+@end |
+ |
+namespace device { |
+ |
+namespace { |
+ |
+class CoreWlanApi : public WifiDataProviderCommon::WlanApiInterface { |
+ public: |
+ CoreWlanApi() {} |
+ |
+ // WlanApiInterface: |
+ bool GetAccessPointData(WifiData::AccessPointDataSet* data) override; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(CoreWlanApi); |
+}; |
+ |
+bool CoreWlanApi::GetAccessPointData(WifiData::AccessPointDataSet* data) { |
+ base::mac::ScopedNSAutoreleasePool auto_pool; |
+ // 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 = @{ kCWScanKeyMerge : @NO }; |
+ |
+ NSSet* supported_interfaces = [CWInterface interfaceNames]; |
+ NSUInteger interface_error_count = 0; |
+ for (NSString* interface_name in supported_interfaces) { |
+ CWInterface* corewlan_interface = |
+ [CWInterface interfaceWithName:interface_name]; |
+ if (!corewlan_interface) { |
+ DLOG(WARNING) << interface_name << ": initWithName failed"; |
+ ++interface_error_count; |
+ continue; |
+ } |
+ |
+ const base::TimeTicks start_time = base::TimeTicks::Now(); |
+ |
+ NSError* err = nil; |
+ NSArray* scan = |
+ [corewlan_interface scanForNetworksWithParameters:params error:&err]; |
+ const int error_code = [err code]; |
+ const int count = [scan count]; |
+ // We could get an error code but count != 0 if the scan was interrupted, |
+ // for example. For our purposes this is not fatal, so process as normal. |
+ if (error_code && count == 0) { |
+ DLOG(WARNING) << interface_name << ": CoreWLAN scan failed with error " |
+ << error_code; |
+ ++interface_error_count; |
+ continue; |
+ } |
+ |
+ const base::TimeDelta duration = base::TimeTicks::Now() - start_time; |
+ |
+ UMA_HISTOGRAM_CUSTOM_TIMES("Net.Wifi.ScanLatency", duration, |
+ base::TimeDelta::FromMilliseconds(1), |
+ base::TimeDelta::FromMinutes(1), 100); |
+ |
+ DVLOG(1) << interface_name << ": found " << count << " wifi APs"; |
+ |
+ for (CWNetwork* network in scan) { |
+ DCHECK(network); |
+ AccessPointData access_point_data; |
+ // -[CWNetwork bssid] uses colons to separate the components of the MAC |
+ // address, but AccessPointData requires they be separated with a dash. |
+ access_point_data.mac_address = base::SysNSStringToUTF16([[network bssid] |
+ stringByReplacingOccurrencesOfString:@":" |
+ withString:@"-"]); |
+ access_point_data.radio_signal_strength = [network rssiValue]; |
+ access_point_data.channel = [[network wlanChannel] channelNumber]; |
+ access_point_data.signal_to_noise = |
+ access_point_data.radio_signal_strength - [network noiseMeasurement]; |
+ access_point_data.ssid = base::SysNSStringToUTF16([network ssid]); |
+ data->insert(access_point_data); |
+ } |
+ } |
+ |
+ UMA_HISTOGRAM_CUSTOM_COUNTS( |
+ "Net.Wifi.InterfaceCount", |
+ [supported_interfaces count] - interface_error_count, 1, 5, 6); |
+ |
+ // Return true even if some interfaces failed to scan, so long as at least |
+ // one interface did not fail. |
+ return interface_error_count == 0 || |
+ [supported_interfaces count] > interface_error_count; |
+}; |
+ |
+// 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 |
+ |
+} // namespace |
+ |
+// static |
+WifiDataProvider* WifiDataProviderManager::DefaultFactoryFunction() { |
+ return new WifiDataProviderMac(); |
+} |
+ |
+WifiDataProviderMac::WifiDataProviderMac() {} |
+ |
+WifiDataProviderMac::~WifiDataProviderMac() {} |
+ |
+WifiDataProviderMac::WlanApiInterface* WifiDataProviderMac::NewWlanApi() { |
+ return new CoreWlanApi(); |
+} |
+ |
+WifiPollingPolicy* WifiDataProviderMac::NewPollingPolicy() { |
+ return new GenericWifiPollingPolicy< |
+ kDefaultPollingInterval, kNoChangePollingInterval, |
+ kTwoNoChangePollingInterval, kNoWifiPollingIntervalMilliseconds>; |
+} |
+ |
+} // namespace device |