summaryrefslogtreecommitdiff
path: root/chromium/services/device/geolocation/wifi_data_provider_mac.mm
blob: 1b53ce41c426baeac0d2ee36d1ff278d4cb26408 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// 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 "services/device/geolocation/wifi_data_provider_mac.h"

#import <CoreWLAN/CoreWLAN.h>
#import <Foundation/Foundation.h>

// This file uses the deprecated CWInterface API, but CWWiFiClient appears to be
// different in ways that are relevant to this code, so for now ignore the
// deprecation. See <https://crbug.com/841631>.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/sys_string_conversions.h"
#include "services/device/geolocation/wifi_data_provider_common.h"
#include "services/device/geolocation/wifi_data_provider_manager.h"

@interface CWInterface (Private)
- (NSSet<CWNetwork *> *)scanForNetworksWithName:(NSString *)networkName
                                    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;

  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;
    NSSet<CWNetwork *>* scan =
        [corewlan_interface scanForNetworksWithName:nil 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() {}

std::unique_ptr<WifiDataProviderMac::WlanApiInterface>
WifiDataProviderMac::CreateWlanApi() {
  return std::make_unique<CoreWlanApi>();
}

std::unique_ptr<WifiPollingPolicy> WifiDataProviderMac::CreatePollingPolicy() {
  return std::make_unique<GenericWifiPollingPolicy<
      kDefaultPollingInterval, kNoChangePollingInterval,
      kTwoNoChangePollingInterval, kNoWifiPollingIntervalMilliseconds>>();
}

}  // namespace device

#pragma clang diagnostic pop