Bug 985970. On Windows, initiate a scan of nearby wireless networks before collecting AP data. r=jimm

This commit is contained in:
Tim Abraldes 2014-08-01 12:10:48 -07:00
parent 6372a893b2
commit 703335c64e
7 changed files with 474 additions and 92 deletions

View File

@ -42,6 +42,8 @@ elif CONFIG['OS_ARCH'] == 'FreeBSD':
elif CONFIG['OS_ARCH'] == 'WINNT':
UNIFIED_SOURCES += [
'nsWifiScannerWin.cpp',
'win_wifiScanner.cpp',
'win_wlanLibrary.cpp',
]
elif CONFIG['OS_ARCH'] == 'SunOS':
CXXFLAGS += CONFIG['GLIB_CFLAGS']

View File

@ -21,6 +21,10 @@
#include "mozilla/Attributes.h"
#include "nsIInterfaceRequestor.h"
#ifdef XP_WIN
class WinWifiScanner;
#endif
#if defined(PR_LOGGING)
extern PRLogModuleInfo *gWifiMonitorLog;
#endif
@ -71,6 +75,9 @@ class nsWifiMonitor MOZ_FINAL : nsIRunnable, nsIWifiMonitor, nsIObserver
mozilla::ReentrantMonitor mReentrantMonitor;
#ifdef XP_WIN
nsAutoPtr<WinWifiScanner> mWinWifiScanner;
#endif
};
#else
#include "nsIWifi.h"

View File

@ -2,42 +2,39 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "windows.h"
#include "wlanapi.h"
#include "stdlib.h"
#include "nsWifiMonitor.h"
#include "nsWifiAccessPoint.h"
#include "nsServiceManagerUtils.h"
// moz headers (alphabetical)
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
#include "nsComponentManagerUtils.h"
#include "nsIMutableArray.h"
#define DOT11_BSS_TYPE_UNUSED static_cast<DOT11_BSS_TYPE>(0)
#include "nsServiceManagerUtils.h"
#include "nsWifiAccessPoint.h"
#include "win_wifiScanner.h"
using namespace mozilla;
/**
* `nsWifiMonitor` is declared in the cross-platform nsWifiMonitor.h and
* is mostly defined in the cross-platform nsWifiMonitor.cpp. This function
* is implemented in various platform-specific files but the implementation
* is almost identical in each file. We relegate the Windows-specific
* work to the `WinWifiScanner` class and deal with non-Windows-specific
* issues like calling listeners here. Hopefully this file can be merged
* with the other implementations of `nsWifiMonitor::DoScan` since a lot
* of the code is identical
*/
nsresult
nsWifiMonitor::DoScan()
{
HINSTANCE wlan_library = LoadLibrary("Wlanapi.dll");
if (!wlan_library)
return NS_ERROR_NOT_AVAILABLE;
decltype(::WlanOpenHandle)* WlanOpenHandle = (decltype(::WlanOpenHandle)*) GetProcAddress(wlan_library, "WlanOpenHandle");
decltype(::WlanEnumInterfaces)* WlanEnumInterfaces = (decltype(::WlanEnumInterfaces)*) GetProcAddress(wlan_library, "WlanEnumInterfaces");
decltype(::WlanGetNetworkBssList)* WlanGetNetworkBssList = (decltype(::WlanGetNetworkBssList)*) GetProcAddress(wlan_library, "WlanGetNetworkBssList");
decltype(::WlanFreeMemory)* WlanFreeMemory = (decltype(::WlanFreeMemory)*) GetProcAddress(wlan_library, "WlanFreeMemory");
decltype(::WlanCloseHandle)* WlanCloseHandle = (decltype(::WlanCloseHandle)*) GetProcAddress(wlan_library, "WlanCloseHandle");
if (!WlanOpenHandle ||
!WlanEnumInterfaces ||
!WlanGetNetworkBssList ||
!WlanFreeMemory ||
!WlanCloseHandle)
return NS_ERROR_FAILURE;
if (!mWinWifiScanner) {
mWinWifiScanner = new WinWifiScanner();
if (!mWinWifiScanner) {
// TODO: Probably return OOM error
return NS_ERROR_FAILURE;
}
}
// Regularly get the access point data.
@ -46,77 +43,15 @@ nsWifiMonitor::DoScan()
do {
accessPoints.Clear();
// Get the handle to the WLAN API.
DWORD negotiated_version;
HANDLE wlan_handle = nullptr;
// We could be executing on either Windows XP or Windows Vista, so use the
// lower version of the client WLAN API. It seems that the negotiated version
// is the Vista version irrespective of what we pass!
static const int kXpWlanClientVersion = 1;
if ((*WlanOpenHandle)(kXpWlanClientVersion,
nullptr,
&negotiated_version,
&wlan_handle) != ERROR_SUCCESS) {
return NS_ERROR_NOT_AVAILABLE;
nsresult rv = mWinWifiScanner->GetAccessPointsFromWLAN(accessPoints);
if (NS_FAILED(rv)) {
return rv;
}
// try again later.
if (!wlan_handle)
return NS_ERROR_FAILURE;
// Get the list of interfaces. WlanEnumInterfaces allocates interface_list.
WLAN_INTERFACE_INFO_LIST *interface_list = nullptr;
if ((*WlanEnumInterfaces)(wlan_handle, nullptr, &interface_list) != ERROR_SUCCESS) {
// try again later
(*WlanCloseHandle)(wlan_handle, nullptr);
return NS_ERROR_FAILURE;
}
// Go through the list of interfaces and get the data for each.
for (int i = 0; i < static_cast<int>(interface_list->dwNumberOfItems); ++i) {
WLAN_BSS_LIST *bss_list;
HRESULT rv = (*WlanGetNetworkBssList)(wlan_handle,
&interface_list->InterfaceInfo[i].InterfaceGuid,
nullptr, // Use all SSIDs.
DOT11_BSS_TYPE_UNUSED,
false, // bSecurityEnabled - unused
nullptr, // reserved
&bss_list);
if (rv != ERROR_SUCCESS) {
continue;
}
for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) {
nsWifiAccessPoint* ap = new nsWifiAccessPoint();
if (!ap)
continue;
const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
ap->setMac(bss_entry.dot11Bssid);
ap->setSignal(bss_entry.lRssi);
ap->setSSID((char*) bss_entry.dot11Ssid.ucSSID,
bss_entry.dot11Ssid.uSSIDLength);
accessPoints.AppendObject(ap);
}
(*WlanFreeMemory)(bss_list);
}
// Free interface_list.
(*WlanFreeMemory)(interface_list);
// Close the handle.
(*WlanCloseHandle)(wlan_handle, nullptr);
bool accessPointsChanged = !AccessPointsEqual(accessPoints, lastAccessPoints);
ReplaceArray(lastAccessPoints, accessPoints);
nsresult rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
rv = CallWifiListeners(lastAccessPoints, accessPointsChanged);
NS_ENSURE_SUCCESS(rv, rv);
// wait for some reasonable amount of time. pref?

View File

@ -0,0 +1,192 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "win_wifiScanner.h"
// Moz headers (alphabetical)
#include "win_wlanLibrary.h"
#define DOT11_BSS_TYPE_UNUSED static_cast<DOT11_BSS_TYPE>(0)
class InterfaceScanCallbackData {
public:
InterfaceScanCallbackData(uint32_t numInterfaces)
: mCurrentlyScanningInterfaces(numInterfaces)
{
mAllInterfacesDoneScanningEvent =
::CreateEvent(nullptr, // null security
TRUE, // manual reset event
FALSE, // initially nonsignaled
nullptr); // not named
MOZ_ASSERT(NULL != mAllInterfacesDoneScanningEvent);
}
~InterfaceScanCallbackData()
{
::CloseHandle(mAllInterfacesDoneScanningEvent);
}
void
OnInterfaceScanComplete()
{
uint32_t val = ::InterlockedDecrement(&mCurrentlyScanningInterfaces);
if (!val) {
::SetEvent(mAllInterfacesDoneScanningEvent);
}
}
void
WaitForAllInterfacesToFinishScanning(uint32_t msToWait)
{
::WaitForSingleObject(mAllInterfacesDoneScanningEvent,
msToWait);
}
private:
volatile uint32_t mCurrentlyScanningInterfaces;
HANDLE mAllInterfacesDoneScanningEvent;
};
static void
OnScanComplete(PWLAN_NOTIFICATION_DATA data, PVOID context)
{
if (WLAN_NOTIFICATION_SOURCE_ACM != data->NotificationSource) {
return;
}
if (wlan_notification_acm_scan_complete != data->NotificationCode &&
wlan_notification_acm_scan_fail != data->NotificationCode) {
return;
}
InterfaceScanCallbackData* cbData =
reinterpret_cast<InterfaceScanCallbackData*>(context);
cbData->OnInterfaceScanComplete();
}
WinWifiScanner::WinWifiScanner()
{
// NOTE: We assume that, if we were unable to load the WLAN library when
// we initially tried, we will not be able to load it in the future.
// Technically, on Windows XP SP2, a user could install the redistributable
// and make our assumption incorrect. We opt to avoid making a bunch of
// spurious LoadLibrary calls in the common case rather than load the
// WLAN API in the edge case.
mWlanLibrary = WinWLANLibrary::Load();
MOZ_ASSERT(mWlanLibrary);
}
WinWifiScanner::~WinWifiScanner()
{
}
nsresult
WinWifiScanner::GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints)
{
accessPoints.Clear();
// NOTE: We do not try to load the WLAN library if we previously failed
// to load it. See the note in WinWifiScanner constructor
if (!mWlanLibrary) {
return NS_ERROR_NOT_AVAILABLE;
}
// Get the list of interfaces. WlanEnumInterfaces allocates interface_list.
WLAN_INTERFACE_INFO_LIST *interface_list = nullptr;
if (ERROR_SUCCESS !=
(*mWlanLibrary->GetWlanEnumInterfacesPtr())(mWlanLibrary->GetWLANHandle(),
nullptr,
&interface_list)) {
return NS_ERROR_FAILURE;
}
// This ensures we call WlanFreeMemory on interface_list
ScopedWLANObject scopedInterfaceList(mWlanLibrary, interface_list);
if (!interface_list->dwNumberOfItems) {
return NS_OK;
}
InterfaceScanCallbackData cbData(interface_list->dwNumberOfItems);
DWORD wlanNotifySource;
if (ERROR_SUCCESS !=
(*mWlanLibrary->GetWlanRegisterNotificationPtr())(
mWlanLibrary->GetWLANHandle(),
WLAN_NOTIFICATION_SOURCE_ACM,
TRUE,
(WLAN_NOTIFICATION_CALLBACK)OnScanComplete,
&cbData,
NULL,
&wlanNotifySource)) {
return NS_ERROR_FAILURE;
}
// Go through the list of interfaces and call `WlanScan` on each
for (unsigned int i = 0; i < interface_list->dwNumberOfItems; ++i) {
if (ERROR_SUCCESS !=
(*mWlanLibrary->GetWlanScanPtr())(
mWlanLibrary->GetWLANHandle(),
&interface_list->InterfaceInfo[i].InterfaceGuid,
NULL,
NULL,
NULL)) {
cbData.OnInterfaceScanComplete();
}
}
// From the MSDN documentation:
// "Wireless network drivers that meet Windows logo requirements are
// required to complete a WlanScan function request in 4 seconds"
cbData.WaitForAllInterfacesToFinishScanning(5000);
// Unregister for the notifications. The documentation mentions that,
// if a callback is currently running, this will wait for the callback
// to complete.
(*mWlanLibrary->GetWlanRegisterNotificationPtr())(
mWlanLibrary->GetWLANHandle(),
WLAN_NOTIFICATION_SOURCE_NONE,
TRUE,
NULL,
NULL,
NULL,
&wlanNotifySource);
// Go through the list of interfaces and get the data for each.
for (uint32_t i = 0; i < interface_list->dwNumberOfItems; ++i) {
WLAN_BSS_LIST *bss_list;
if (ERROR_SUCCESS !=
(*mWlanLibrary->GetWlanGetNetworkBssListPtr())(
mWlanLibrary->GetWLANHandle(),
&interface_list->InterfaceInfo[i].InterfaceGuid,
nullptr, // Use all SSIDs.
DOT11_BSS_TYPE_UNUSED,
false, // bSecurityEnabled - unused
nullptr, // reserved
&bss_list)) {
continue;
}
// This ensures we call WlanFreeMemory on bss_list
ScopedWLANObject scopedBssList(mWlanLibrary, bss_list);
// Store each discovered access point in our outparam
for (int j = 0; j < static_cast<int>(bss_list->dwNumberOfItems); ++j) {
nsWifiAccessPoint* ap = new nsWifiAccessPoint();
if (!ap) {
continue;
}
const WLAN_BSS_ENTRY bss_entry = bss_list->wlanBssEntries[j];
ap->setMac(bss_entry.dot11Bssid);
ap->setSignal(bss_entry.lRssi);
ap->setSSID(reinterpret_cast<char const*>(bss_entry.dot11Ssid.ucSSID),
bss_entry.dot11Ssid.uSSIDLength);
accessPoints.AppendObject(ap);
}
}
return NS_OK;
}

View File

@ -0,0 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
// Moz headers (alphabetical)
#include "nsAutoPtr.h"
#include "nsCOMArray.h"
class nsWifiAccessPoint;
class WinWLANLibrary;
class WinWifiScanner {
public:
WinWifiScanner();
~WinWifiScanner();
/**
* GetAccessPointsFromWLAN
*
* Scans the available wireless interfaces for nearby access points and
* populates the supplied collection with them
*
* @param accessPoints The collection to populate with available APs
* @return NS_OK on success, failure codes on failure
*/
nsresult GetAccessPointsFromWLAN(nsCOMArray<nsWifiAccessPoint> &accessPoints);
private:
nsAutoPtr<WinWLANLibrary> mWlanLibrary;
};

View File

@ -0,0 +1,155 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "win_wlanLibrary.h"
// Moz headers (alphabetical)
WinWLANLibrary*
WinWLANLibrary::Load()
{
WinWLANLibrary *ret = new WinWLANLibrary();
if (!ret) {
return nullptr;
}
if (!ret->Initialize()) {
delete ret;
return nullptr;
}
return ret;
}
WinWLANLibrary::WinWLANLibrary()
: mWlanLibrary(nullptr),
mWlanHandle(nullptr),
mWlanEnumInterfacesPtr(nullptr),
mWlanGetNetworkBssListPtr(nullptr),
mWlanFreeMemoryPtr(nullptr),
mWlanCloseHandlePtr(nullptr),
mWlanOpenHandlePtr(nullptr),
mWlanRegisterNotificationPtr(nullptr),
mWlanScanPtr(nullptr)
{
}
HANDLE
WinWLANLibrary::GetWLANHandle() const
{
return mWlanHandle;
}
decltype(::WlanEnumInterfaces)*
WinWLANLibrary::GetWlanEnumInterfacesPtr() const
{
return mWlanEnumInterfacesPtr;
}
decltype(::WlanGetNetworkBssList)*
WinWLANLibrary::GetWlanGetNetworkBssListPtr() const
{
return mWlanGetNetworkBssListPtr;
}
decltype(::WlanFreeMemory)*
WinWLANLibrary::GetWlanFreeMemoryPtr() const
{
return mWlanFreeMemoryPtr;
}
decltype(::WlanCloseHandle)*
WinWLANLibrary::GetWlanCloseHandlePtr() const
{
return mWlanCloseHandlePtr;
}
decltype(::WlanOpenHandle)*
WinWLANLibrary::GetWlanOpenHandlePtr() const
{
return mWlanOpenHandlePtr;
}
decltype(::WlanRegisterNotification)*
WinWLANLibrary::GetWlanRegisterNotificationPtr() const
{
return mWlanRegisterNotificationPtr;
}
decltype(::WlanScan)*
WinWLANLibrary::GetWlanScanPtr() const
{
return mWlanScanPtr;
}
bool
WinWLANLibrary::Initialize()
{
mWlanLibrary = LoadLibrary("Wlanapi.dll");
if (!mWlanLibrary) {
return false;
}
mWlanOpenHandlePtr =
(decltype(::WlanOpenHandle)*) GetProcAddress(mWlanLibrary,
"WlanOpenHandle");
mWlanEnumInterfacesPtr =
(decltype(::WlanEnumInterfaces)*) GetProcAddress(mWlanLibrary,
"WlanEnumInterfaces");
mWlanRegisterNotificationPtr =
(decltype(::WlanRegisterNotification)*) GetProcAddress(mWlanLibrary,
"WlanRegisterNotification");
mWlanScanPtr =
(decltype(::WlanScan)*) GetProcAddress(mWlanLibrary, "WlanScan");
mWlanFreeMemoryPtr =
(decltype(::WlanFreeMemory)*) GetProcAddress(mWlanLibrary,
"WlanFreeMemory");
mWlanCloseHandlePtr =
(decltype(::WlanCloseHandle)*) GetProcAddress(mWlanLibrary,
"WlanCloseHandle");
mWlanGetNetworkBssListPtr =
(decltype(::WlanGetNetworkBssList)*) GetProcAddress(mWlanLibrary,
"WlanGetNetworkBssList");
if (!mWlanOpenHandlePtr ||
!mWlanEnumInterfacesPtr ||
!mWlanRegisterNotificationPtr ||
!mWlanGetNetworkBssListPtr ||
!mWlanScanPtr ||
!mWlanFreeMemoryPtr ||
!mWlanCloseHandlePtr) {
return false;
}
// Get the handle to the WLAN API.
DWORD negotiated_version;
// We could be executing on either Windows XP or Windows Vista, so use the
// lower version of the client WLAN API. It seems that the negotiated version
// is the Vista version irrespective of what we pass!
static const int kXpWlanClientVersion = 1;
if (ERROR_SUCCESS !=
(*mWlanOpenHandlePtr)(kXpWlanClientVersion,
nullptr,
&negotiated_version,
&mWlanHandle)) {
return false;
}
return true;
}
WinWLANLibrary::~WinWLANLibrary()
{
if (mWlanLibrary) {
if (mWlanHandle) {
(*mWlanCloseHandlePtr)(mWlanLibrary, mWlanHandle);
mWlanHandle = nullptr;
}
::FreeLibrary(mWlanLibrary);
mWlanLibrary = nullptr;
}
}

View File

@ -0,0 +1,59 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#pragma once
// Moz headers (alphabetical)
// System headers (alphabetical)
#include <windows.h> // HINSTANCE, HANDLE
#include <wlanapi.h> // Wlan* functions
class WinWLANLibrary {
public:
static WinWLANLibrary* Load();
~WinWLANLibrary();
HANDLE GetWLANHandle() const;
decltype(::WlanEnumInterfaces)* GetWlanEnumInterfacesPtr() const;
decltype(::WlanGetNetworkBssList)* GetWlanGetNetworkBssListPtr() const;
decltype(::WlanFreeMemory)* GetWlanFreeMemoryPtr() const;
decltype(::WlanCloseHandle)* GetWlanCloseHandlePtr() const;
decltype(::WlanOpenHandle)* GetWlanOpenHandlePtr() const;
decltype(::WlanRegisterNotification)* GetWlanRegisterNotificationPtr() const;
decltype(::WlanScan)* GetWlanScanPtr() const;
private:
WinWLANLibrary();
bool Initialize();
HMODULE mWlanLibrary;
HANDLE mWlanHandle;
decltype(::WlanEnumInterfaces)* mWlanEnumInterfacesPtr;
decltype(::WlanGetNetworkBssList)* mWlanGetNetworkBssListPtr;
decltype(::WlanFreeMemory)* mWlanFreeMemoryPtr;
decltype(::WlanCloseHandle)* mWlanCloseHandlePtr;
decltype(::WlanOpenHandle)* mWlanOpenHandlePtr;
decltype(::WlanRegisterNotification)* mWlanRegisterNotificationPtr;
decltype(::WlanScan)* mWlanScanPtr;
};
class ScopedWLANObject {
public:
ScopedWLANObject(WinWLANLibrary* library, void* object)
: mObject(object),
mLibrary(library)
{
}
~ScopedWLANObject()
{
(*(mLibrary->GetWlanFreeMemoryPtr()))(mObject);
}
private:
WinWLANLibrary *mLibrary;
void *mObject;
};