summaryrefslogtreecommitdiffstats
path: root/chrome/browser/geolocation/wifi_data_provider_mac.cc
blob: f99b45f3a4a955eadbf4550d3b4126788ced0c4a (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 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 <dlfcn.h>
#include <stdio.h>
#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<WirelessAttachFunction>(
      dlsym(apple_80211_library_, "WirelessAttach"));
  WirelessScanSplit_function_ = reinterpret_cast<WirelessScanSplitFunction>(
      dlsym(apple_80211_library_, "WirelessScanSplit"));
  WirelessDetach_function_ = reinterpret_cast<WirelessDetachFunction>(
      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<const WirelessNetworkInfo*>(
        CFDataGetBytePtr(
        reinterpret_cast<const CFDataRef>(
        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<const char*>(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<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* MacWifiDataProvider::NewPollingPolicy() {
  return new GenericPollingPolicy<kDefaultPollingInterval,
                                  kNoChangePollingInterval,
                                  kTwoNoChangePollingInterval,
                                  kNoWifiPollingIntervalMilliseconds>;
}