blob: a5f978bf7f70b1d5268df0d109625ba2ca6b5f8d [file] [log] [blame]
// 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