diff options
author | mef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-22 20:25:54 +0000 |
---|---|---|
committer | mef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-22 20:25:54 +0000 |
commit | 4f9264c78eb9d30a938588528362f0a9fdfd6ce4 (patch) | |
tree | fc98a988e5dbc23be37e15b0cc15dafded32c72b /components/wifi | |
parent | 0ca10241cd75d258c1679d50e4e136d8755a2fba (diff) | |
download | chromium_src-4f9264c78eb9d30a938588528362f0a9fdfd6ce4.zip chromium_src-4f9264c78eb9d30a938588528362f0a9fdfd6ce4.tar.gz chromium_src-4f9264c78eb9d30a938588528362f0a9fdfd6ce4.tar.bz2 |
Windows-specific implementation of Networking Private API.
Previously reviewed as https://codereview.chromium.org/27722003, but
moves WiFiService Windows implementation to components/wifi.
Based on infrastructure in https://codereview.chromium.org/54323003/
BUG=267667
Review URL: https://codereview.chromium.org/68503019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@236816 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/wifi')
-rw-r--r-- | components/wifi/fake_wifi_service.cc | 6 | ||||
-rw-r--r-- | components/wifi/wifi_service.h | 9 | ||||
-rw-r--r-- | components/wifi/wifi_service_test.cc | 146 | ||||
-rw-r--r-- | components/wifi/wifi_service_win.cc | 1274 | ||||
-rw-r--r-- | components/wifi/wifi_test.cc | 156 |
5 files changed, 1590 insertions, 1 deletions
diff --git a/components/wifi/fake_wifi_service.cc b/components/wifi/fake_wifi_service.cc index 90902ed..e8c2372 100644 --- a/components/wifi/fake_wifi_service.cc +++ b/components/wifi/fake_wifi_service.cc @@ -81,6 +81,11 @@ class FakeWiFiService : public WiFiService { } } + virtual void Initialize( + scoped_refptr<base::SequencedTaskRunner> task_runner) OVERRIDE {} + + virtual void UnInitialize() OVERRIDE {} + virtual void GetProperties(const std::string& network_guid, DictionaryValue* properties, std::string* error) OVERRIDE { @@ -203,6 +208,5 @@ class FakeWiFiService : public WiFiService { }; WiFiService* WiFiService::CreateForTest() { return new FakeWiFiService(); } -WiFiService* WiFiService::Create() { return new FakeWiFiService(); } } // namespace wifi diff --git a/components/wifi/wifi_service.h b/components/wifi/wifi_service.h index e29d936..a81d1e3 100644 --- a/components/wifi/wifi_service.h +++ b/components/wifi/wifi_service.h @@ -12,6 +12,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_proxy.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/values.h" #include "components/wifi/wifi_export.h" @@ -29,6 +30,13 @@ class WIFI_EXPORT WiFiService { virtual ~WiFiService() {} + // Initialize WiFiService, store |task_runner| for posting worker tasks. + virtual void Initialize( + scoped_refptr<base::SequencedTaskRunner> task_runner) = 0; + + // UnInitialize WiFiService. + virtual void UnInitialize() = 0; + // Create instance of |WiFiService| for normal use. static WiFiService* Create(); // Create instance of |WiFiService| for unit test use. @@ -74,6 +82,7 @@ class WIFI_EXPORT WiFiService { typedef int32 Frequency; enum FrequencyEnum { + kFrequencyAny = 0, kFrequencyUnknown = 0, kFrequency2400 = 2400, kFrequency5000 = 5000 diff --git a/components/wifi/wifi_service_test.cc b/components/wifi/wifi_service_test.cc new file mode 100644 index 0000000..d8ce8af --- /dev/null +++ b/components/wifi/wifi_service_test.cc @@ -0,0 +1,146 @@ +// Copyright 2013 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 "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "components/onc/onc_constants.h" +#include "components/wifi/wifi_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace wifi { + +class WiFiServiceTest : public testing::Test { + public: + void LogError(const std::string& error_name, + scoped_ptr<base::DictionaryValue> error_data) { + LOG(ERROR) << "WiFi Error: " << error_name; + QuitRunLoop(); + } + + void TestError(const std::string& error_name, + scoped_ptr<base::DictionaryValue> error_data) { + LOG(ERROR) << "WiFi Error: " << error_name; + FAIL(); + } + + void OnDictionaryResult(const std::string& network_guid, + const base::DictionaryValue& dictionary) {} + + void OnNetworkProperties(const std::string& network_guid, + const WiFiService::NetworkProperties& properties) { + LOG(INFO) << "OnNetworkProperties" << *properties.ToValue(false).release(); + } + + void OnNetworksChangedEventWaitingForConnect( + const WiFiService::NetworkGuidList& network_guid_list) { + LOG(INFO) << "Networks Changed: " << network_guid_list.front(); + // Check that network is now connected. + wifi_service_->GetProperties( + connected_network_guid_, + base::Bind(&WiFiServiceTest::WaitForConnect, base::Unretained(this)), + base::Bind(&WiFiServiceTest::TestError, base::Unretained(this))); + } + + void OnNetworkConnectStarted(const std::string& network_guid) { + LOG(INFO) << "Started Network Connect:" << network_guid; + wifi_service_->SetNetworksChangedObserver( + base::Bind(&WiFiServiceTest::OnNetworksChangedEventWaitingForConnect, + base::Unretained(this))); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::MessageLoop::QuitClosure(), + base::TimeDelta::FromSeconds(10)); + } + + void WaitForConnect(const std::string& network_guid, + const WiFiService::NetworkProperties& properties) { + LOG(INFO) << "WaitForConnect" << *properties.ToValue(false).release(); + if (properties.connection_state == onc::connection_state::kConnected) + QuitRunLoop(); + } + + void WaitForDisconnect(const std::string& network_guid, + const WiFiService::NetworkProperties& properties) { + LOG(INFO) << "WaitForDisconnect" << *properties.ToValue(false).release(); + EXPECT_TRUE(properties.connection_state == + onc::connection_state::kNotConnected); + } + + void OnNetworkDisconnectStarted(const std::string& network_guid) { + LOG(INFO) << "Started Network Disconnect:" << network_guid; + // Check that Network state has changed to 'Not Connected'. + wifi_service_->GetProperties( + connected_network_guid_, + base::Bind(&WiFiServiceTest::WaitForDisconnect, base::Unretained(this)), + base::Bind(&WiFiServiceTest::TestError, base::Unretained(this))); + // Start connect back to the same network. + wifi_service_->StartConnect( + connected_network_guid_, + base::Bind(&WiFiServiceTest::OnNetworkConnectStarted, + base::Unretained(this)), + base::Bind(&WiFiServiceTest::TestError, base::Unretained(this))); + } + + void OnVisibleNetworks(const WiFiService::NetworkList& network_list) { + LOG(INFO) << "Visible WiFi Networks: " << network_list.size(); + } + + void FindConnectedNetwork(const WiFiService::NetworkList& network_list) { + for (WiFiService::NetworkList::const_iterator net = network_list.begin(); + net != network_list.end(); + ++net) { + if (net->connection_state == onc::connection_state::kConnected) { + connected_network_guid_ = net->guid; + LOG(INFO) << "Connected Network:\n" << *(net->ToValue(false).release()); + } + } + + if (!connected_network_guid_.empty()) { + wifi_service_->StartDisconnect( + connected_network_guid_, + base::Bind(&WiFiServiceTest::OnNetworkDisconnectStarted, + base::Unretained(this)), + base::Bind(&WiFiServiceTest::TestError, base::Unretained(this))); + } else { + LOG(INFO) << "No Connected Networks, skipping disconnect."; + QuitRunLoop(); + } + } + + void QuitRunLoop() { + base::MessageLoop::current()->PostTask(FROM_HERE, + base::MessageLoop::QuitClosure()); + } + + protected: + virtual void SetUp() { wifi_service_.reset(WiFiService::CreateService()); } + virtual void TearDown() {} + + std::string connected_network_guid_; + scoped_ptr<WiFiService> wifi_service_; + base::MessageLoop loop_; +}; + +// Test getting list of visible networks. +TEST_F(WiFiServiceTest, GetVisibleNetworks) { + wifi_service_->GetVisibleNetworks( + base::Bind(&WiFiServiceTest::OnVisibleNetworks, base::Unretained(this)), + base::Bind(&WiFiServiceTest::LogError, base::Unretained(this))); +} + +// Test that connected WiFi network can be disconnected and reconnected. +// Disabled to avoid network connection interruption unless enabled explicitly. +TEST_F(WiFiServiceTest, DISABLED_Reconnect) { + base::RunLoop run_loop; + wifi_service_->GetVisibleNetworks( + base::Bind(&WiFiServiceTest::FindConnectedNetwork, + base::Unretained(this)), + base::Bind(&WiFiServiceTest::LogError, base::Unretained(this))); + run_loop.Run(); +} + +} // namespace wifi diff --git a/components/wifi/wifi_service_win.cc b/components/wifi/wifi_service_win.cc new file mode 100644 index 0000000..052062d --- /dev/null +++ b/components/wifi/wifi_service_win.cc @@ -0,0 +1,1274 @@ +// Copyright 2013 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 "components/wifi/wifi_service.h" + +#include <iphlpapi.h> +#include <objbase.h> +#include <wlanapi.h> + +#include <set> + +#include "base/base_paths_win.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/path_service.h" +#include "base/strings/string16.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/win/registry.h" +#include "components/onc/onc_constants.h" + +namespace { +const char kWiFiServiceError[] = "Error.WiFiService"; +const wchar_t kNwCategoryWizardRegKey[] = + L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Network\\" + L"NwCategoryWizard"; +const wchar_t kNwCategoryWizardRegValue[] = L"Show"; +const wchar_t kNwCategoryWizardSavedRegValue[] = L"ShowSaved"; +const wchar_t kNwCategoryWizardDeleteRegValue[] = L"ShowDelete"; +const wchar_t kWlanApiDll[] = L"wlanapi.dll"; + +// WlanApi function names +const char kWlanConnect[] = "WlanConnect"; +const char kWlanCloseHandle[] = "WlanCloseHandle"; +const char kWlanDisconnect[] = "WlanDisconnect"; +const char kWlanEnumInterfaces[] = "WlanEnumInterfaces"; +const char kWlanFreeMemory[] = "WlanFreeMemory"; +const char kWlanGetAvailableNetworkList[] = "WlanGetAvailableNetworkList"; +const char kWlanGetNetworkBssList[] = "WlanGetNetworkBssList"; +const char kWlanGetProfile[] = "WlanGetProfile"; +const char kWlanOpenHandle[] = "WlanOpenHandle"; +const char kWlanRegisterNotification[] = "WlanRegisterNotification"; +const char kWlanSaveTemporaryProfile[] = "WlanSaveTemporaryProfile"; +const char kWlanScan[] = "WlanScan"; + +// WlanApi function definitions +typedef DWORD (WINAPI* WlanConnectFunction)( + HANDLE hClientHandle, + CONST GUID *pInterfaceGuid, + CONST PWLAN_CONNECTION_PARAMETERS pConnectionParameters, + PVOID pReserved); + +typedef DWORD (WINAPI* WlanCloseHandleFunction)( + HANDLE hClientHandle, + PVOID pReserved); + +typedef DWORD (WINAPI* WlanDisconnectFunction)( + HANDLE hClientHandle, + CONST GUID *pInterfaceGuid, + PVOID pReserved); + +typedef DWORD (WINAPI* WlanEnumInterfacesFunction)( + HANDLE hClientHandle, + PVOID pReserved, + PWLAN_INTERFACE_INFO_LIST *ppInterfaceList); + +typedef VOID (WINAPI* WlanFreeMemoryFunction)( + _In_ PVOID pMemory); + +typedef DWORD (WINAPI* WlanGetAvailableNetworkListFunction)( + HANDLE hClientHandle, + CONST GUID *pInterfaceGuid, + DWORD dwFlags, + PVOID pReserved, + PWLAN_AVAILABLE_NETWORK_LIST *ppAvailableNetworkList); + +typedef DWORD (WINAPI* WlanGetNetworkBssListFunction)( + HANDLE hClientHandle, + const GUID* pInterfaceGuid, + const PDOT11_SSID pDot11Ssid, + DOT11_BSS_TYPE dot11BssType, + BOOL bSecurityEnabled, + PVOID pReserved, + PWLAN_BSS_LIST* ppWlanBssList); + +typedef DWORD (WINAPI* WlanGetProfileFunction)( + HANDLE hClientHandle, + CONST GUID *pInterfaceGuid, + LPCWSTR strProfileName, + PVOID pReserved, + LPWSTR *pstrProfileXml, + DWORD *pdwFlags, + DWORD *pdwGrantedAccess); + +typedef DWORD (WINAPI* WlanOpenHandleFunction)( + DWORD dwClientVersion, + PVOID pReserved, + PDWORD pdwNegotiatedVersion, + PHANDLE phClientHandle); + +typedef DWORD (WINAPI* WlanRegisterNotificationFunction)( + HANDLE hClientHandle, + DWORD dwNotifSource, + BOOL bIgnoreDuplicate, + WLAN_NOTIFICATION_CALLBACK funcCallback, + PVOID pCallbackContext, + PVOID pReserved, + PDWORD pdwPrevNotifSource); + +typedef DWORD (WINAPI* WlanSaveTemporaryProfileFunction)( + HANDLE hClientHandle, + CONST GUID* pInterfaceGuid, + LPCWSTR strProfileName, + LPCWSTR strAllUserProfileSecurity, + DWORD dwFlags, + BOOL bOverWrite, + PVOID pReserved); + +typedef DWORD (WINAPI* WlanScanFunction)( + HANDLE hClientHandle, + CONST GUID *pInterfaceGuid, + CONST PDOT11_SSID pDot11Ssid, + CONST PWLAN_RAW_DATA pIeData, + PVOID pReserved); + +} // namespace + +namespace wifi { + +// Implementation of WiFiService for Windows. +class WiFiServiceImpl : public WiFiService, base::NonThreadSafe { + public: + WiFiServiceImpl(); + virtual ~WiFiServiceImpl(); + + // WiFiService interface implementation. + virtual void Initialize( + scoped_refptr<base::SequencedTaskRunner> task_runner) OVERRIDE; + + virtual void UnInitialize() OVERRIDE; + + // Get Properties of network identified by |network_guid|. Populates + // |properties| on success, |error| on failure. + virtual void GetProperties(const std::string& network_guid, + DictionaryValue* properties, + std::string* error) OVERRIDE; + + // Set Properties of network identified by |network_guid|. Populates |error| + // on failure. + virtual void SetProperties(const std::string& network_guid, + scoped_ptr<base::DictionaryValue> properties, + std::string* error) OVERRIDE; + + // Get list of visible networks. Populates |network_list| on success. + virtual void GetVisibleNetworks(ListValue* network_list) OVERRIDE; + + // Request network scan. Send |NetworkListChanged| event on completion. + virtual void RequestNetworkScan() OVERRIDE; + + // Start connect to network identified by |network_guid|. Populates |error| + // on failure. + virtual void StartConnect(const std::string& network_guid, + std::string* error) OVERRIDE; + + // Start disconnect from network identified by |network_guid|. Populates + // |error| on failure. + virtual void StartDisconnect(const std::string& network_guid, + std::string* error) OVERRIDE; + + // Set observers to run when |NetworksChanged| and |NetworksListChanged| + // events needs to be sent. Notifications are posted on |message_loop_proxy|. + virtual void SetEventObservers( + scoped_refptr<base::MessageLoopProxy> message_loop_proxy, + const NetworkGuidListCallback& networks_changed_observer, + const NetworkGuidListCallback& network_list_changed_observer) OVERRIDE; + + private: + // Static callback for Windows WLAN_NOTIFICATION. Calls OnWlanNotification + // on WiFiServiceImpl passed back as |context|. + static void __stdcall OnWlanNotificationCallback( + PWLAN_NOTIFICATION_DATA wlan_notification_data, + PVOID context); + + // Callback for Windows WLAN_NOTIFICATION. Called on random thread from + // OnWlanNotificationCallback. Handles network connectivity and scan complete + // notification and posts tasks to main thread. + void OnWlanNotification(PWLAN_NOTIFICATION_DATA wlan_notification_data); + + // Handles NetworkScanComplete notification on main thread. Sends + // |NetworkListChanged| event with new list of visible networks. + void OnNetworkScanCompleteOnMainThread(); + + // Wait up to |kMaxAttempts| with |kAttemptDelayMs| delay for connection + // to network with |network_guid|. Reset DHCP and Notify that |NetworkChanged| + // upon success. + void WaitForNetworkConnect(const std::string& network_guid, int attempt); + + // Check |error_code| and if is not |ERROR_SUCCESS|, then store |error_name| + // into |error|. + bool CheckError(DWORD error_code, + const std::string& error_name, + std::string* error) const; + + // Return |iterator| to network identified by |network_guid| in |networks| + // list. + NetworkList::iterator FindNetwork(NetworkList& networks, + const std::string& network_guid); + + // Save currently connected network profile and return its + // |connected_network_guid|, so it can be re-connected later. + DWORD SaveCurrentConnectedNetwork(std::string* connected_network_guid); + + // Sort networks, so connected/connecting is up front, then by type: + // Ethernet, WiFi, Cellular, VPN + static void SortNetworks(NetworkList* networks); + + // Open a WLAN client handle, register for WLAN notifications. + DWORD OpenClientHandle(); + + // Reset DHCP on wireless network to work around an issue when Windows + // takes forever to connect to the network, e.g. after Chromecast + // device reset. + DWORD ResetDHCP(); + + // Find |adapter_index_map| by |interface_guid| for DHCP reset. + DWORD FindAdapterIndexMapByGUID(const GUID& interface_guid, + IP_ADAPTER_INDEX_MAP* adapter_index_map); + + // Avoid the network location wizard to pop up when network is connected. + // Preserve current value in |saved_nw_category_wizard_|. + DWORD DisableNwCategoryWizard(); + + // Restore network location wizard to value saved by DisableNwCategoryWizard. + DWORD RestoreNwCategoryWizard(); + + // Ensure that |client_| handle is initialized. + DWORD EnsureInitialized(); + + // Close |client_| handle if it is open. + DWORD CloseClientHandle(); + + // Get |profile_name| from unique |network_guid|. + base::string16 ProfileNameFromGUID(const std::string& network_guid) const { + return base::UTF8ToUTF16(network_guid); + } + + // Get |dot11_ssid| from unique |network_guid|. + DOT11_SSID SSIDFromGUID(const std::string& network_guid) const; + + // Get unique |network_guid| string based on |dot11_ssid|. + std::string GUIDFromSSID(const DOT11_SSID& dot11_ssid) const { + return std::string(reinterpret_cast<const char*>(dot11_ssid.ucSSID), + dot11_ssid.uSSIDLength); + } + + // Get network |ssid| string based on |wlan|. + std::string SSIDFromWLAN(const WLAN_AVAILABLE_NETWORK& wlan) const { + return GUIDFromSSID(wlan.dot11Ssid); + } + + // Get unique |network_guid| string based on |wlan|. + std::string GUIDFromWLAN(const WLAN_AVAILABLE_NETWORK& wlan) const { + return SSIDFromWLAN(wlan); + } + + // Deduce |onc::wifi| security from |alg|. + std::string SecurityFromDot11AuthAlg(DOT11_AUTH_ALGORITHM alg) const; + + // Populate |properties| based on |wlan| and its corresponding bss info from + // |wlan_bss_list|. + void NetworkPropertiesFromAvailableNetwork(const WLAN_AVAILABLE_NETWORK& wlan, + const WLAN_BSS_LIST& wlan_bss_list, + NetworkProperties* properties); + + // Get the list of visible wireless networks. + DWORD GetVisibleNetworkList(NetworkList* network_list); + + // Find currently connected network if any. Populate |connected_network_guid| + // on success. + DWORD FindConnectedNetwork(std::string* connected_network_guid); + + // Connect to network |network_guid| using previosly stored profile if exists, + // or just network sid. If |frequency| is not |kFrequencyUnknown| then + // connects only to BSS which uses that frequency and returns + // |ERROR_NOT_FOUND| if such BSS cannot be found. + DWORD Connect(const std::string& network_guid, Frequency frequency); + + // Disconnect from currently connected network if any. + DWORD Disconnect(); + + // Get DOT11_BSSID_LIST of desired BSSIDs to connect to |ssid| network on + // given |frequency|. + DWORD GetDesiredBssList(DOT11_SSID& ssid, + Frequency frequency, + scoped_ptr<DOT11_BSSID_LIST>* desired_list); + + // Save temporary wireless profile for |network_guid|. + DWORD SaveTempProfile(const std::string& network_guid); + + // Get previously stored |profile_xml| for |network_guid|. + DWORD GetProfile(const std::string& network_guid, std::string* profile_xml); + + // Return true if there is previously stored profile xml for |network_guid|. + bool HaveProfile(const std::string& network_guid); + + // Notify |network_list_changed_observer_| that list of visible networks has + // changed to |networks|. + void NotifyNetworkListChanged(const NetworkList& networks); + + // Notify |networks_changed_observer_| that network |network_guid| status has + // changed. + void NotifyNetworkChanged(const std::string& network_guid); + + // Load WlanApi.dll from SystemDirectory and get Api function pointers. + DWORD LoadWlanLibrary(); + // Instance of WlanApi.dll. + HINSTANCE wlan_api_library_; + // WlanApi function pointers + WlanConnectFunction WlanConnect_function_; + WlanCloseHandleFunction WlanCloseHandle_function_; + WlanDisconnectFunction WlanDisconnect_function_; + WlanEnumInterfacesFunction WlanEnumInterfaces_function_; + WlanFreeMemoryFunction WlanFreeMemory_function_; + WlanGetAvailableNetworkListFunction WlanGetAvailableNetworkList_function_; + // WlanGetNetworkBssList function may not be avaiable on Windows XP. + WlanGetNetworkBssListFunction WlanGetNetworkBssList_function_; + WlanGetProfileFunction WlanGetProfile_function_; + WlanOpenHandleFunction WlanOpenHandle_function_; + WlanRegisterNotificationFunction WlanRegisterNotification_function_; + WlanScanFunction WlanScan_function_; + // WlanSaveTemporaryProfile function may not be avaiable on Windows XP. + WlanSaveTemporaryProfileFunction WlanSaveTemporaryProfile_function_; + + // WLAN service handle. + HANDLE client_; + // GUID of the currently connected interface, if any, otherwise the GUID of + // one of the WLAN interfaces. + GUID interface_guid_; + // Preserved WLAN profile xml. + std::map<std::string, std::string> saved_profiles_xml_; + // Observer to get notified when network(s) have changed (e.g. connect). + NetworkGuidListCallback networks_changed_observer_; + // Observer to get notified when network list has changed (scan complete). + NetworkGuidListCallback network_list_changed_observer_; + // Saved value of network location wizard show value. + scoped_ptr<DWORD> saved_nw_category_wizard_; + // MessageLoopProxy to post events on UI thread. + scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + // Task runner for worker tasks. + scoped_refptr<base::SequencedTaskRunner> task_runner_; + // If |false|, then |networks_changed_observer_| is not notified. + bool enable_notify_network_changed_; + // Number of attempts to check that network has connected successfully. + static const int kMaxAttempts = 100; + // Delay between attempts to check that network has connected successfully. + static const int kAttemptDelayMs = 100; + DISALLOW_COPY_AND_ASSIGN(WiFiServiceImpl); +}; + +WiFiServiceImpl::WiFiServiceImpl() + : wlan_api_library_(NULL), + WlanConnect_function_(NULL), + WlanCloseHandle_function_(NULL), + WlanDisconnect_function_(NULL), + WlanEnumInterfaces_function_(NULL), + WlanFreeMemory_function_(NULL), + WlanGetAvailableNetworkList_function_(NULL), + WlanGetNetworkBssList_function_(NULL), + WlanGetProfile_function_(NULL), + WlanOpenHandle_function_(NULL), + WlanRegisterNotification_function_(NULL), + WlanSaveTemporaryProfile_function_(NULL), + WlanScan_function_(NULL), + client_(NULL), + enable_notify_network_changed_(true) {} + +WiFiServiceImpl::~WiFiServiceImpl() { UnInitialize(); } + +void WiFiServiceImpl::Initialize( + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(!client_); + task_runner_.swap(task_runner); + // Restore NwCategoryWizard in case if we crashed during connect. + RestoreNwCategoryWizard(); + OpenClientHandle(); +} + +void WiFiServiceImpl::UnInitialize() { + CloseClientHandle(); +} + +void WiFiServiceImpl::GetProperties(const std::string& network_guid, + DictionaryValue* properties, + std::string* error) { + DWORD error_code = EnsureInitialized(); + if (error_code == ERROR_SUCCESS) { + NetworkList network_list; + error_code = GetVisibleNetworkList(&network_list); + if (error_code == ERROR_SUCCESS && !network_list.empty()) { + NetworkList::const_iterator it = FindNetwork(network_list, network_guid); + if (it != network_list.end()) { + DVLOG(1) << "Get Properties: " << network_guid << ":" + << it->connection_state; + properties->Swap(it->ToValue(false).get()); + return; + } else { + error_code = ERROR_NOT_FOUND; + } + } + } + + CheckError(error_code, kWiFiServiceError, error); +} + +void WiFiServiceImpl::SetProperties( + const std::string& network_guid, + scoped_ptr<base::DictionaryValue> properties, + std::string* error) { + // This method is not implemented in first version as it is not used by + // Google Cast extension. + CheckError(ERROR_CALL_NOT_IMPLEMENTED, kWiFiServiceError, error); +} + +void WiFiServiceImpl::GetVisibleNetworks(ListValue* network_list) { + DWORD error = EnsureInitialized(); + + if (error == ERROR_SUCCESS) { + NetworkList networks; + error = GetVisibleNetworkList(&networks); + if (error == ERROR_SUCCESS && !networks.empty()) { + SortNetworks(&networks); + for (WiFiService::NetworkList::const_iterator it = networks.begin(); + it != networks.end(); + ++it) { + scoped_ptr<DictionaryValue> network(it->ToValue(true)); + network_list->Append(network.release()); + } + } + } +} + +void WiFiServiceImpl::RequestNetworkScan() { + DWORD error = EnsureInitialized(); + if (error == ERROR_SUCCESS) { + WlanScan_function_(client_, &interface_guid_, NULL, NULL, NULL); + } +} + +void WiFiServiceImpl::StartConnect(const std::string& network_guid, + std::string* error) { + DVLOG(1) << "Start Connect: " << network_guid; + DWORD error_code = EnsureInitialized(); + if (error_code == ERROR_SUCCESS) { + std::string connected_network_guid; + error_code = SaveCurrentConnectedNetwork(&connected_network_guid); + if (error_code == ERROR_SUCCESS) { + Frequency frequency = kFrequencyAny; + // Connect only if network |network_guid| is not connected already. + if (network_guid != connected_network_guid) + error_code = Connect(network_guid, frequency); + if (error_code == ERROR_SUCCESS) { + // Notify that previously connected network has changed. + NotifyNetworkChanged(connected_network_guid); + // Start waiting for network connection state change. + if (!networks_changed_observer_.is_null()) { + DisableNwCategoryWizard(); + // Disable automatic network change notifications as they get fired + // when network is just connected, but not yet accessible (doesn't + // have valid IP address). + enable_notify_network_changed_ = false; + WaitForNetworkConnect(network_guid, 0); + return; + } + } + } + } + CheckError(error_code, kWiFiServiceError, error); +} + +void WiFiServiceImpl::StartDisconnect(const std::string& network_guid, + std::string* error) { + DVLOG(1) << "Start Disconnect: " << network_guid; + DWORD error_code = EnsureInitialized(); + if (error_code == ERROR_SUCCESS) { + std::string connected_network_guid; + error_code = SaveCurrentConnectedNetwork(&connected_network_guid); + if (error_code == ERROR_SUCCESS && network_guid == connected_network_guid) { + error_code = Disconnect(); + if (error_code == ERROR_SUCCESS) { + NotifyNetworkChanged(network_guid); + return; + } + } + } + CheckError(error_code, kWiFiServiceError, error); +} + +void WiFiServiceImpl::SetEventObservers( + scoped_refptr<base::MessageLoopProxy> message_loop_proxy, + const NetworkGuidListCallback& networks_changed_observer, + const NetworkGuidListCallback& network_list_changed_observer) { + message_loop_proxy_.swap(message_loop_proxy); + networks_changed_observer_ = networks_changed_observer; + network_list_changed_observer_ = network_list_changed_observer; +} + +void WiFiServiceImpl::OnWlanNotificationCallback( + PWLAN_NOTIFICATION_DATA wlan_notification_data, + PVOID context) { + WiFiServiceImpl* service = reinterpret_cast<WiFiServiceImpl*>(context); + service->OnWlanNotification(wlan_notification_data); +} + +void WiFiServiceImpl::OnWlanNotification( + PWLAN_NOTIFICATION_DATA wlan_notification_data) { + if (message_loop_proxy_ == NULL) + return; + switch (wlan_notification_data->NotificationCode) { + case wlan_notification_acm_disconnected: + case wlan_notification_acm_connection_complete: + case wlan_notification_acm_connection_attempt_fail: { + PWLAN_CONNECTION_NOTIFICATION_DATA wlan_connection_data = + reinterpret_cast<PWLAN_CONNECTION_NOTIFICATION_DATA>( + wlan_notification_data->pData); + message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&WiFiServiceImpl::NotifyNetworkChanged, + base::Unretained(this), + GUIDFromSSID(wlan_connection_data->dot11Ssid))); + break; + } + case wlan_notification_acm_scan_complete: + message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&WiFiServiceImpl::OnNetworkScanCompleteOnMainThread, + base::Unretained(this))); + break; + } +} + +void WiFiServiceImpl::OnNetworkScanCompleteOnMainThread() { + NetworkList networks; + // Get current list of visible networks and notify that network list has + // changed. + DWORD error = GetVisibleNetworkList(&networks); + DCHECK(error == ERROR_SUCCESS); + if (error == ERROR_SUCCESS) + NotifyNetworkListChanged(networks); +} + +void WiFiServiceImpl::WaitForNetworkConnect(const std::string& network_guid, + int attempt) { + // If network didn't get connected in |kMaxAttempts|, then restore automatic + // network change notifications and stop waiting. + if (attempt > kMaxAttempts) { + DLOG(ERROR) << kMaxAttempts << " attempts exceeded waiting for connect to " + << network_guid; + enable_notify_network_changed_ = true; + RestoreNwCategoryWizard(); + return; + } + std::string connected_network_guid; + DWORD error = FindConnectedNetwork(&connected_network_guid); + if (network_guid == connected_network_guid) { + DVLOG(1) << "WiFi Connected, Reset DHCP: " << network_guid; + // Even though wireless network is now connected, it may still be unusable, + // e.g. after Chromecast device reset. Reset DHCP on wireless network to + // work around this issue. + error = ResetDHCP(); + // Restore previously suppressed notifications. + enable_notify_network_changed_ = true; + RestoreNwCategoryWizard(); + NotifyNetworkChanged(network_guid); + } else { + // Continue waiting for network connection state change. + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&WiFiServiceImpl::WaitForNetworkConnect, + base::Unretained(this), + network_guid, + ++attempt), + base::TimeDelta::FromMilliseconds(kAttemptDelayMs)); + } +} + +bool WiFiServiceImpl::CheckError(DWORD error_code, + const std::string& error_name, + std::string* error) const { + if (error_code != ERROR_SUCCESS) { + DLOG(ERROR) << "WiFiService Error " << error_code << ": " << error_name; + *error = error_name; + return true; + } + return false; +} + +WiFiService::NetworkList::iterator WiFiServiceImpl::FindNetwork( + NetworkList& networks, + const std::string& network_guid) { + for (NetworkList::iterator it = networks.begin(); it != networks.end(); + ++it) { + if (it->guid == network_guid) + return it; + } + return networks.end(); +} + +DWORD WiFiServiceImpl::SaveCurrentConnectedNetwork( + std::string* connected_network_guid) { + // Find currently connected network. + DWORD error = FindConnectedNetwork(connected_network_guid); + if (error == ERROR_SUCCESS && !connected_network_guid->empty()) { + if (error == ERROR_SUCCESS) { + SaveTempProfile(*connected_network_guid); + std::string profile_xml; + error = GetProfile(*connected_network_guid, &profile_xml); + if (error == ERROR_SUCCESS) { + saved_profiles_xml_[*connected_network_guid] = profile_xml; + } + } + } + return error; +} + +void WiFiServiceImpl::SortNetworks(NetworkList* networks) { + networks->sort(NetworkProperties::OrderByType); +} + +DWORD WiFiServiceImpl::LoadWlanLibrary() { + // Use an absolute path to load the DLL to avoid DLL preloading attacks. + base::FilePath path; + if (!PathService::Get(base::DIR_SYSTEM, &path)) { + DLOG(ERROR) << "Unable to get system path."; + return ERROR_NOT_FOUND; + } + wlan_api_library_ = ::LoadLibraryEx(path.Append(kWlanApiDll).value().c_str(), + NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); + if (!wlan_api_library_) { + DLOG(ERROR) << "Unable to load WlanApi.dll."; + return ERROR_NOT_FOUND; + } + + // Initialize WlanApi function pointers + WlanConnect_function_ = + reinterpret_cast<WlanConnectFunction>( + ::GetProcAddress(wlan_api_library_, kWlanConnect)); + WlanCloseHandle_function_ = + reinterpret_cast<WlanCloseHandleFunction>( + ::GetProcAddress(wlan_api_library_, kWlanCloseHandle)); + WlanDisconnect_function_ = + reinterpret_cast<WlanDisconnectFunction>( + ::GetProcAddress(wlan_api_library_, kWlanDisconnect)); + WlanEnumInterfaces_function_ = + reinterpret_cast<WlanEnumInterfacesFunction>( + ::GetProcAddress(wlan_api_library_, kWlanEnumInterfaces)); + WlanFreeMemory_function_ = + reinterpret_cast<WlanFreeMemoryFunction>( + ::GetProcAddress(wlan_api_library_, kWlanFreeMemory)); + WlanGetAvailableNetworkList_function_ = + reinterpret_cast<WlanGetAvailableNetworkListFunction>( + ::GetProcAddress(wlan_api_library_, kWlanGetAvailableNetworkList)); + WlanGetNetworkBssList_function_ = + reinterpret_cast<WlanGetNetworkBssListFunction>( + ::GetProcAddress(wlan_api_library_, kWlanGetNetworkBssList)); + WlanGetProfile_function_ = + reinterpret_cast<WlanGetProfileFunction>( + ::GetProcAddress(wlan_api_library_, kWlanGetProfile)); + WlanOpenHandle_function_ = + reinterpret_cast<WlanOpenHandleFunction>( + ::GetProcAddress(wlan_api_library_, kWlanOpenHandle)); + WlanRegisterNotification_function_ = + reinterpret_cast<WlanRegisterNotificationFunction>( + ::GetProcAddress(wlan_api_library_, kWlanRegisterNotification)); + WlanSaveTemporaryProfile_function_ = + reinterpret_cast<WlanSaveTemporaryProfileFunction>( + ::GetProcAddress(wlan_api_library_, kWlanSaveTemporaryProfile)); + WlanScan_function_ = + reinterpret_cast<WlanScanFunction>( + ::GetProcAddress(wlan_api_library_, kWlanScan)); + + if (!WlanConnect_function_ || + !WlanCloseHandle_function_ || + !WlanDisconnect_function_ || + !WlanEnumInterfaces_function_ || + !WlanFreeMemory_function_ || + !WlanGetAvailableNetworkList_function_ || + !WlanGetProfile_function_ || + !WlanOpenHandle_function_ || + !WlanRegisterNotification_function_ || + !WlanScan_function_) { + DLOG(ERROR) << "Unable to find required WlanApi function."; + FreeLibrary(wlan_api_library_); + wlan_api_library_ = NULL; + return ERROR_NOT_FOUND; + } + + // Some WlanApi functions may not be available on XP. + if (!WlanGetNetworkBssList_function_ || + !WlanSaveTemporaryProfile_function_) { + DVLOG(1) << "WlanApi function is not be available on XP."; + } + + return ERROR_SUCCESS; +} + +DWORD WiFiServiceImpl::OpenClientHandle() { + DWORD error = LoadWlanLibrary(); + DWORD service_version = 0; + + if (error != ERROR_SUCCESS) + return error; + + // Open a handle to the service. + error = WlanOpenHandle_function_(1, NULL, &service_version, &client_); + + PWLAN_INTERFACE_INFO_LIST interface_list = NULL; + if (error == ERROR_SUCCESS) { + // Enumerate wireless interfaces. + error = WlanEnumInterfaces_function_(client_, NULL, &interface_list); + if (error == ERROR_SUCCESS) { + if (interface_list != NULL && interface_list->dwNumberOfItems != 0) { + // Remember first interface just in case if none are connected. + interface_guid_ = interface_list->InterfaceInfo[0].InterfaceGuid; + // Try to find a connected interface. + for (DWORD itf = 0; itf < interface_list->dwNumberOfItems; ++itf) { + if (interface_list->InterfaceInfo[itf].isState == + wlan_interface_state_connected) { + // Found connected interface, remember it! + interface_guid_ = interface_list->InterfaceInfo[itf].InterfaceGuid; + break; + } + } + WlanRegisterNotification_function_(client_, + WLAN_NOTIFICATION_SOURCE_ALL, + FALSE, + OnWlanNotificationCallback, + this, + NULL, + NULL); + } else { + error = ERROR_NOINTERFACE; + } + } + // Clean up. + if (interface_list != NULL) + WlanFreeMemory_function_(interface_list); + } + return error; +} + +DWORD WiFiServiceImpl::ResetDHCP() { + IP_ADAPTER_INDEX_MAP adapter_index_map = {0}; + DWORD error = FindAdapterIndexMapByGUID(interface_guid_, &adapter_index_map); + if (error == ERROR_SUCCESS) { + error = ::IpReleaseAddress(&adapter_index_map); + if (error == ERROR_SUCCESS) { + error = ::IpRenewAddress(&adapter_index_map); + } + } + return error; +} + +DWORD WiFiServiceImpl::FindAdapterIndexMapByGUID( + const GUID& interface_guid, + IP_ADAPTER_INDEX_MAP* adapter_index_map) { + string16 guid_string; + const int kGUIDSize = 39; + ::StringFromGUID2( + interface_guid, WriteInto(&guid_string, kGUIDSize), kGUIDSize); + + ULONG buffer_length = 0; + DWORD error = ::GetInterfaceInfo(NULL, &buffer_length); + if (error == ERROR_INSUFFICIENT_BUFFER) { + scoped_ptr<unsigned char[]> buffer(new unsigned char[buffer_length]); + IP_INTERFACE_INFO* interface_info = + reinterpret_cast<IP_INTERFACE_INFO*>(buffer.get()); + error = GetInterfaceInfo(interface_info, &buffer_length); + if (error == ERROR_SUCCESS) { + for (int adapter = 0; adapter < interface_info->NumAdapters; ++adapter) { + if (EndsWith( + interface_info->Adapter[adapter].Name, guid_string, false)) { + *adapter_index_map = interface_info->Adapter[adapter]; + break; + } + } + } + } + return error; +} + +DWORD WiFiServiceImpl::DisableNwCategoryWizard() { + base::win::RegKey nw_category_wizard; + DWORD error = nw_category_wizard.Open(HKEY_CURRENT_USER, + kNwCategoryWizardRegKey, + KEY_READ | KEY_SET_VALUE); + if (error == ERROR_SUCCESS) { + // Save current value if present. + if (nw_category_wizard.HasValue(kNwCategoryWizardRegValue)) { + DWORD saved = 0u; + error = nw_category_wizard.ReadValueDW(kNwCategoryWizardRegValue, + &saved); + if (error == ERROR_SUCCESS) { + error = nw_category_wizard.WriteValue(kNwCategoryWizardSavedRegValue, + saved); + } + } else { + // Mark that temporary value has to be deleted. + error = nw_category_wizard.WriteValue(kNwCategoryWizardDeleteRegValue, + 1u); + } + + // Disable network location wizard. + error = nw_category_wizard.WriteValue(kNwCategoryWizardRegValue, + static_cast<DWORD>(0)); + } + + return error; +} + +DWORD WiFiServiceImpl::RestoreNwCategoryWizard() { + base::win::RegKey nw_category_wizard; + DWORD error = nw_category_wizard.Open(HKEY_CURRENT_USER, + kNwCategoryWizardRegKey, + KEY_SET_VALUE); + if (error == ERROR_SUCCESS) { + // Restore saved value if present. + if (nw_category_wizard.HasValue(kNwCategoryWizardSavedRegValue)) { + DWORD saved = 0u; + error = nw_category_wizard.ReadValueDW(kNwCategoryWizardSavedRegValue, + &saved); + if (error == ERROR_SUCCESS) { + error = nw_category_wizard.WriteValue(kNwCategoryWizardRegValue, + saved); + error = nw_category_wizard.DeleteValue(kNwCategoryWizardSavedRegValue); + } + } else if (nw_category_wizard.HasValue(kNwCategoryWizardDeleteRegValue)) { + error = nw_category_wizard.DeleteValue(kNwCategoryWizardRegValue); + error = nw_category_wizard.DeleteValue(kNwCategoryWizardDeleteRegValue); + } + } + + return error; +} + +DWORD WiFiServiceImpl::EnsureInitialized() { + if (client_ != NULL) + return ERROR_SUCCESS; + return ERROR_NOINTERFACE; +} + +DWORD WiFiServiceImpl::CloseClientHandle() { + DWORD error = ERROR_SUCCESS; + if (client_ != NULL) { + error = WlanCloseHandle_function_(client_, NULL); + client_ = NULL; + } + if (wlan_api_library_ != NULL) { + WlanConnect_function_ = NULL; + WlanCloseHandle_function_ = NULL; + WlanDisconnect_function_ = NULL; + WlanEnumInterfaces_function_ = NULL; + WlanFreeMemory_function_ = NULL; + WlanGetAvailableNetworkList_function_ = NULL; + WlanGetNetworkBssList_function_ = NULL; + WlanGetProfile_function_ = NULL; + WlanOpenHandle_function_ = NULL; + WlanRegisterNotification_function_ = NULL; + WlanSaveTemporaryProfile_function_ = NULL; + WlanScan_function_ = NULL; + ::FreeLibrary(wlan_api_library_); + wlan_api_library_ = NULL; + } + return error; +} + +DOT11_SSID WiFiServiceImpl::SSIDFromGUID( + const std::string& network_guid) const { + DOT11_SSID ssid = {0}; + if (network_guid.length() <= DOT11_SSID_MAX_LENGTH) { + ssid.uSSIDLength = static_cast<ULONG>(network_guid.length()); + strncpy(reinterpret_cast<char*>(ssid.ucSSID), + network_guid.c_str(), + ssid.uSSIDLength); + } else { + NOTREACHED(); + } + return ssid; +} + +std::string WiFiServiceImpl::SecurityFromDot11AuthAlg( + DOT11_AUTH_ALGORITHM alg) const { + switch (alg) { + case DOT11_AUTH_ALGO_RSNA: + return onc::wifi::kWPA_EAP; + case DOT11_AUTH_ALGO_RSNA_PSK: + return onc::wifi::kWPA_PSK; + case DOT11_AUTH_ALGO_80211_SHARED_KEY: + return onc::wifi::kWEP_PSK; + case DOT11_AUTH_ALGO_80211_OPEN: + return onc::wifi::kNone; + default: + return onc::wifi::kWPA_EAP; + } +} + +void WiFiServiceImpl::NetworkPropertiesFromAvailableNetwork( + const WLAN_AVAILABLE_NETWORK& wlan, + const WLAN_BSS_LIST& wlan_bss_list, + NetworkProperties* properties) { + if (wlan.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) { + properties->connection_state = onc::connection_state::kConnected; + } else { + properties->connection_state = onc::connection_state::kNotConnected; + } + + properties->ssid = SSIDFromWLAN(wlan); + properties->name = properties->ssid; + properties->guid = GUIDFromWLAN(wlan); + properties->type = onc::network_type::kWiFi; + + for (size_t bss = 0; bss < wlan_bss_list.dwNumberOfItems; ++bss) { + const WLAN_BSS_ENTRY& bss_entry(wlan_bss_list.wlanBssEntries[bss]); + if (bss_entry.dot11Ssid.uSSIDLength == wlan.dot11Ssid.uSSIDLength && + 0 == memcmp(bss_entry.dot11Ssid.ucSSID, + wlan.dot11Ssid.ucSSID, + bss_entry.dot11Ssid.uSSIDLength)) { + if (bss_entry.ulChCenterFrequency < 3000000) + properties->frequency = kFrequency2400; + else + properties->frequency = kFrequency5000; + properties->frequency_list.push_back(properties->frequency); + properties->bssid = NetworkProperties::MacAddressAsString( + bss_entry.dot11Bssid); + } + } + properties->frequency_list.sort(); + properties->frequency_list.unique(); + properties->security = + SecurityFromDot11AuthAlg(wlan.dot11DefaultAuthAlgorithm); + properties->signal_strength = wlan.wlanSignalQuality; +} + +// Get the list of visible wireless networks +DWORD WiFiServiceImpl::GetVisibleNetworkList(NetworkList* network_list) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + PWLAN_AVAILABLE_NETWORK_LIST available_network_list = NULL; + PWLAN_BSS_LIST bss_list = NULL; + + error = WlanGetAvailableNetworkList_function_( + client_, + &interface_guid_, + WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES, + NULL, + &available_network_list); + + std::set<std::string> network_guids; + + if (error == ERROR_SUCCESS && + available_network_list && + WlanGetNetworkBssList_function_) { + // TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is + // needed, then different method of getting BSS (e.g. OID query) will have + // to be used. + error = WlanGetNetworkBssList_function_(client_, + &interface_guid_, + NULL, + dot11_BSS_type_any, + FALSE, + NULL, + &bss_list); + if (error == ERROR_SUCCESS && NULL != bss_list) { + for (DWORD i = 0; i < available_network_list->dwNumberOfItems; ++i) { + NetworkProperties network_properties; + NetworkPropertiesFromAvailableNetwork( + available_network_list->Network[i], + *bss_list, + &network_properties); + // Check for duplicate network guids. + if (network_guids.count(network_properties.guid)) { + // There should be no difference between properties except for + // |connection_state|, so mark it as |kConnected| if either one is. + if (network_properties.connection_state == + onc::connection_state::kConnected) { + NetworkList::iterator previous_network_properties = + FindNetwork(*network_list, network_properties.guid); + DCHECK(previous_network_properties != network_list->end()); + previous_network_properties->connection_state = + network_properties.connection_state; + } + } else { + network_list->push_back(network_properties); + } + network_guids.insert(network_properties.guid); + } + } + } + + // clean up + if (available_network_list != NULL) { + WlanFreeMemory_function_(available_network_list); + } + if (bss_list != NULL) { + WlanFreeMemory_function_(bss_list); + } + return error; +} + +// Find currently connected network. +DWORD WiFiServiceImpl::FindConnectedNetwork( + std::string* connected_network_guid) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + PWLAN_AVAILABLE_NETWORK_LIST available_network_list = NULL; + error = WlanGetAvailableNetworkList_function_( + client_, &interface_guid_, 0, NULL, &available_network_list); + + if (error == ERROR_SUCCESS && NULL != available_network_list) { + for (DWORD i = 0; i < available_network_list->dwNumberOfItems; ++i) { + const WLAN_AVAILABLE_NETWORK& wlan = available_network_list->Network[i]; + if (wlan.dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) { + *connected_network_guid = GUIDFromWLAN(wlan); + break; + } + } + } + + // clean up + if (available_network_list != NULL) { + WlanFreeMemory_function_(available_network_list); + } + + return error; +} + +DWORD WiFiServiceImpl::GetDesiredBssList( + DOT11_SSID& ssid, + Frequency frequency, + scoped_ptr<DOT11_BSSID_LIST>* desired_list) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + desired_list->reset(); + + if (frequency == kFrequencyAny) + return ERROR_SUCCESS; + + // TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is + // needed, then different method of getting BSS (e.g. OID query) will have + // to be used. + if (!WlanGetNetworkBssList_function_) + return ERROR_NOT_SUPPORTED; + + DWORD error = ERROR_SUCCESS; + PWLAN_BSS_LIST bss_list = NULL; + + error = WlanGetNetworkBssList_function_(client_, + &interface_guid_, + NULL, + dot11_BSS_type_any, + FALSE, + NULL, + &bss_list); + if (error == ERROR_SUCCESS && NULL != bss_list) { + unsigned int best_quality = 0u; + size_t best_index = 0; + Frequency bss_frequency; + + // Go through bss_list and find best quality BSSID with matching frequency. + for (size_t bss = 0; bss < bss_list->dwNumberOfItems; ++bss) { + const WLAN_BSS_ENTRY& bss_entry(bss_list->wlanBssEntries[bss]); + if (bss_entry.dot11Ssid.uSSIDLength != ssid.uSSIDLength || + 0 != memcmp(bss_entry.dot11Ssid.ucSSID, + ssid.ucSSID, + bss_entry.dot11Ssid.uSSIDLength)) + continue; + + if (bss_entry.ulChCenterFrequency < 3000000) + bss_frequency = kFrequency2400; + else + bss_frequency = kFrequency5000; + + if (bss_frequency == frequency && + bss_entry.uLinkQuality > best_quality) { + best_quality = bss_entry.uLinkQuality; + best_index = bss; + } + } + + // If any matching BSS were found, prepare the header. + if (best_quality > 0) { + const WLAN_BSS_ENTRY& bss_entry(bss_list->wlanBssEntries[best_index]); + scoped_ptr<DOT11_BSSID_LIST> selected_list(new DOT11_BSSID_LIST); + + selected_list->Header.Revision = DOT11_BSSID_LIST_REVISION_1; + selected_list->Header.Size = sizeof(DOT11_BSSID_LIST); + selected_list->Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + selected_list->uNumOfEntries = 1; + selected_list->uTotalNumOfEntries = 1; + std::copy(bss_entry.dot11Bssid, + bss_entry.dot11Bssid+sizeof(bss_entry.dot11Bssid), + selected_list->BSSIDs[0]); + desired_list->swap(selected_list); + DVLOG(1) << "Quality: " << best_quality << " BSS: " + << NetworkProperties::MacAddressAsString(bss_entry.dot11Bssid); + } else { + error = ERROR_NOT_FOUND; + } + } + + // clean up + if (bss_list != NULL) { + WlanFreeMemory_function_(bss_list); + } + return error; +} + + +DWORD WiFiServiceImpl::Connect(const std::string& network_guid, + Frequency frequency) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + DOT11_SSID ssid = SSIDFromGUID(network_guid); + scoped_ptr<DOT11_BSSID_LIST> desired_bss_list; + error = GetDesiredBssList(ssid, frequency, &desired_bss_list); + if (error == ERROR_SUCCESS) { + if (HaveProfile(network_guid)) { + base::string16 profile_name = ProfileNameFromGUID(network_guid); + WLAN_CONNECTION_PARAMETERS wlan_params = { + wlan_connection_mode_profile, + profile_name.c_str(), + NULL, + desired_bss_list.get(), + dot11_BSS_type_any, + 0}; + error = WlanConnect_function_( + client_, &interface_guid_, &wlan_params, NULL); + } else { + // TODO(mef): wlan_connection_mode_discovery_unsecure is not available on + // XP. If XP support is needed, then temporary profile will have to be + // created. + WLAN_CONNECTION_PARAMETERS wlan_params = { + wlan_connection_mode_discovery_unsecure, + NULL, + &ssid, + desired_bss_list.get(), + dot11_BSS_type_infrastructure, + 0}; + error = WlanConnect_function_( + client_, &interface_guid_, &wlan_params, NULL); + } + } + + return error; +} + +DWORD WiFiServiceImpl::Disconnect() { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + error = WlanDisconnect_function_(client_, &interface_guid_, NULL); + return error; +} + +DWORD WiFiServiceImpl::SaveTempProfile(const std::string& network_guid) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + base::string16 profile_name = ProfileNameFromGUID(network_guid); + // TODO(mef): WlanSaveTemporaryProfile is not available on XP. If XP support + // is needed, then different method of saving network profile will have to be + // used. + if (WlanSaveTemporaryProfile_function_) { + error = WlanSaveTemporaryProfile_function_(client_, + &interface_guid_, + profile_name.c_str(), + NULL, + WLAN_PROFILE_USER, + true, + NULL); + } else { + error = ERROR_NOT_SUPPORTED; + } + return error; +} + +DWORD WiFiServiceImpl::GetProfile(const std::string& network_guid, + std::string* profile_xml) { + if (client_ == NULL) { + NOTREACHED(); + return ERROR_NOINTERFACE; + } + + DWORD error = ERROR_SUCCESS; + base::string16 profile_name = ProfileNameFromGUID(network_guid); + LPWSTR str_profile_xml = NULL; + error = WlanGetProfile_function_(client_, + &interface_guid_, + profile_name.c_str(), + NULL, + &str_profile_xml, + NULL, + NULL); + + if (error == ERROR_SUCCESS && str_profile_xml != NULL) { + *profile_xml = base::UTF16ToUTF8(str_profile_xml); + } + // clean up + if (str_profile_xml != NULL) { + WlanFreeMemory_function_(str_profile_xml); + } + + return error; +} + +bool WiFiServiceImpl::HaveProfile(const std::string& network_guid) { + DWORD error = ERROR_SUCCESS; + std::string profile_xml; + return GetProfile(network_guid, &profile_xml) == ERROR_SUCCESS; +} + +void WiFiServiceImpl::NotifyNetworkListChanged(const NetworkList& networks) { + if (network_list_changed_observer_.is_null()) + return; + + NetworkGuidList current_networks; + for (NetworkList::const_iterator it = networks.begin(); + it != networks.end(); + ++it) { + current_networks.push_back(it->guid); + } + + message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(network_list_changed_observer_, current_networks)); +} + +void WiFiServiceImpl::NotifyNetworkChanged(const std::string& network_guid) { + if (enable_notify_network_changed_ && !networks_changed_observer_.is_null()) { + DVLOG(1) << "NotifyNetworkChanged: " << network_guid; + NetworkGuidList changed_networks(1, network_guid); + message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(networks_changed_observer_, changed_networks)); + } +} + +WiFiService* WiFiService::Create() { return new WiFiServiceImpl(); } + +} // namespace wifi diff --git a/components/wifi/wifi_test.cc b/components/wifi/wifi_test.cc new file mode 100644 index 0000000..8fd0aaf --- /dev/null +++ b/components/wifi/wifi_test.cc @@ -0,0 +1,156 @@ +// Copyright 2013 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 <stdio.h> +#include <iostream> +#include <string> + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/cancelable_callback.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "components/wifi/wifi_service.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +namespace wifi { + +class WiFiTest { + public: + WiFiTest() {} + ~WiFiTest() {} + + enum Result { + RESULT_ERROR = -2, + RESULT_WRONG_USAGE = -1, + RESULT_OK = 0, + RESULT_PENDING = 1, + }; + + Result Main(int argc, const char* argv[]); + + private: + bool ParseCommandLine(int argc, const char* argv[]); + + void Start() {} + void Finish(Result result) { + DCHECK_NE(RESULT_PENDING, result); + result_ = result; + if (base::MessageLoop::current()) + base::MessageLoop::current()->Quit(); + } + +#if defined(OS_MACOSX) + // Without this there will be a mem leak on osx. + base::mac::ScopedNSAutoreleasePool scoped_pool_; +#endif + + // Need AtExitManager to support AsWeakPtr (in NetLog). + base::AtExitManager exit_manager_; + + Result result_; +}; + +WiFiTest::Result WiFiTest::Main(int argc, const char* argv[]) { + if (!ParseCommandLine(argc, argv)) { + fprintf(stderr, + "usage: %s [--list]" + " [--get_properties]" + " [--connect]" + " [--disconnect]" + " [--network_guid=<network_guid>]" + " [--frequency=0|2400|5000]" + " [<network_guid>]\n", + argv[0]); + return RESULT_WRONG_USAGE; + } + + base::MessageLoopForIO loop; + result_ = RESULT_PENDING; + + return result_; +} + +bool WiFiTest::ParseCommandLine(int argc, const char* argv[]) { + CommandLine::Init(argc, argv); + const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); + std::string network_guid = + parsed_command_line.GetSwitchValueASCII("network_guid"); + + if (parsed_command_line.GetArgs().size() == 1) { +#if defined(OS_WIN) + network_guid = WideToASCII(parsed_command_line.GetArgs()[0]); +#else + network_guid = parsed_command_line.GetArgs()[0]; +#endif + } + +#if defined(OS_WIN) + if (parsed_command_line.HasSwitch("debug")) + MessageBoxA(NULL, __FUNCTION__, "Debug Me!", MB_OK); +#endif + +#if defined(OS_WIN) + scoped_ptr<WiFiService> wifi_service(WiFiService::Create()); +#else + scoped_ptr<WiFiService> wifi_service(WiFiService::CreateForTest()); +#endif + + wifi_service->Initialize(NULL); + + if (parsed_command_line.HasSwitch("list")) { + ListValue network_list; + wifi_service->GetVisibleNetworks(&network_list); + std::cout << network_list; + return true; + } + + if (parsed_command_line.HasSwitch("get_properties")) { + if (network_guid.length() > 0) { + DictionaryValue properties; + std::string error; + wifi_service->GetProperties(network_guid, &properties, &error); + std::cout << error << ":\n" << properties; + return true; + } + } + + if (parsed_command_line.HasSwitch("connect")) { + if (network_guid.length() > 0) { + std::string error; + wifi_service->StartConnect(network_guid, &error); + std::cout << error; + return true; + } + } + + if (parsed_command_line.HasSwitch("disconnect")) { + if (network_guid.length() > 0) { + std::string error; + wifi_service->StartDisconnect(network_guid, &error); + std::cout << error; + return true; + } + } + + return false; +} + +} // namespace wifi + +int main(int argc, const char* argv[]) { + wifi::WiFiTest wifi_test; + return wifi_test.Main(argc, argv); +} |