1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/wifi/wifi_service.h"
13 #include "base/base_paths_win.h"
14 #include "base/bind.h"
15 #include "base/files/file_path.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/path_service.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/win/registry.h"
23 #include "components/onc/onc_constants.h"
24 #include "components/wifi/network_properties.h"
25 #include "third_party/libxml/chromium/libxml_utils.h"
28 const wchar_t kNwCategoryWizardRegKey
[] =
29 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Network\\"
31 const wchar_t kNwCategoryWizardRegValue
[] = L
"Show";
32 const wchar_t kNwCategoryWizardSavedRegValue
[] = L
"ShowSaved";
33 const wchar_t kNwCategoryWizardDeleteRegValue
[] = L
"ShowDelete";
34 const wchar_t kWlanApiDll
[] = L
"wlanapi.dll";
36 // Created Profile Dictionary keys
37 const char kProfileXmlKey
[] = "xml";
38 const char kProfileSharedKey
[] = "shared";
40 // WlanApi function names
41 const char kWlanConnect
[] = "WlanConnect";
42 const char kWlanCloseHandle
[] = "WlanCloseHandle";
43 const char kWlanDeleteProfile
[] = "WlanDeleteProfile";
44 const char kWlanDisconnect
[] = "WlanDisconnect";
45 const char kWlanEnumInterfaces
[] = "WlanEnumInterfaces";
46 const char kWlanFreeMemory
[] = "WlanFreeMemory";
47 const char kWlanGetAvailableNetworkList
[] = "WlanGetAvailableNetworkList";
48 const char kWlanGetNetworkBssList
[] = "WlanGetNetworkBssList";
49 const char kWlanGetProfile
[] = "WlanGetProfile";
50 const char kWlanOpenHandle
[] = "WlanOpenHandle";
51 const char kWlanQueryInterface
[] = "WlanQueryInterface";
52 const char kWlanRegisterNotification
[] = "WlanRegisterNotification";
53 const char kWlanSaveTemporaryProfile
[] = "WlanSaveTemporaryProfile";
54 const char kWlanScan
[] = "WlanScan";
55 const char kWlanSetProfile
[] = "WlanSetProfile";
57 // WlanApi function definitions
58 typedef DWORD(WINAPI
* WlanConnectFunction
)(
60 CONST GUID
* pInterfaceGuid
,
61 CONST PWLAN_CONNECTION_PARAMETERS pConnectionParameters
,
64 typedef DWORD (WINAPI
* WlanCloseHandleFunction
)(
68 typedef DWORD(WINAPI
* WlanDeleteProfileFunction
)(HANDLE hClientHandle
,
69 const GUID
* pInterfaceGuid
,
70 LPCWSTR strProfileName
,
73 typedef DWORD(WINAPI
* WlanDisconnectFunction
)(HANDLE hClientHandle
,
74 CONST GUID
* pInterfaceGuid
,
77 typedef DWORD(WINAPI
* WlanEnumInterfacesFunction
)(
80 PWLAN_INTERFACE_INFO_LIST
* ppInterfaceList
);
82 typedef VOID (WINAPI
* WlanFreeMemoryFunction
)(
85 typedef DWORD(WINAPI
* WlanGetAvailableNetworkListFunction
)(
87 CONST GUID
* pInterfaceGuid
,
90 PWLAN_AVAILABLE_NETWORK_LIST
* ppAvailableNetworkList
);
92 typedef DWORD (WINAPI
* WlanGetNetworkBssListFunction
)(
94 const GUID
* pInterfaceGuid
,
95 const PDOT11_SSID pDot11Ssid
,
96 DOT11_BSS_TYPE dot11BssType
,
97 BOOL bSecurityEnabled
,
99 PWLAN_BSS_LIST
* ppWlanBssList
);
101 typedef DWORD(WINAPI
* WlanGetProfileFunction
)(HANDLE hClientHandle
,
102 CONST GUID
* pInterfaceGuid
,
103 LPCWSTR strProfileName
,
105 LPWSTR
* pstrProfileXml
,
107 DWORD
* pdwGrantedAccess
);
109 typedef DWORD (WINAPI
* WlanOpenHandleFunction
)(
110 DWORD dwClientVersion
,
112 PDWORD pdwNegotiatedVersion
,
113 PHANDLE phClientHandle
);
115 typedef DWORD(WINAPI
* WlanQueryInterfaceFunction
)(
116 HANDLE hClientHandle
,
117 const GUID
* pInterfaceGuid
,
118 WLAN_INTF_OPCODE OpCode
,
122 PWLAN_OPCODE_VALUE_TYPE pWlanOpcodeValueType
);
124 typedef DWORD (WINAPI
* WlanRegisterNotificationFunction
)(
125 HANDLE hClientHandle
,
127 BOOL bIgnoreDuplicate
,
128 WLAN_NOTIFICATION_CALLBACK funcCallback
,
129 PVOID pCallbackContext
,
131 PDWORD pdwPrevNotifSource
);
133 typedef DWORD (WINAPI
* WlanSaveTemporaryProfileFunction
)(
134 HANDLE hClientHandle
,
135 CONST GUID
* pInterfaceGuid
,
136 LPCWSTR strProfileName
,
137 LPCWSTR strAllUserProfileSecurity
,
142 typedef DWORD(WINAPI
* WlanScanFunction
)(HANDLE hClientHandle
,
143 CONST GUID
* pInterfaceGuid
,
144 CONST PDOT11_SSID pDot11Ssid
,
145 CONST PWLAN_RAW_DATA pIeData
,
148 typedef DWORD(WINAPI
* WlanSetProfileFunction
)(HANDLE hClientHandle
,
149 const GUID
* pInterfaceGuid
,
151 LPCWSTR strProfileXml
,
152 LPCWSTR strAllUserProfileSecurity
,
155 DWORD
* pdwReasonCode
);
157 // Values for WLANProfile XML.
158 const char kAuthenticationOpen
[] = "open";
159 const char kAuthenticationWepPsk
[] = "WEP";
160 const char kAuthenticationWpaPsk
[] = "WPAPSK";
161 const char kAuthenticationWpa2Psk
[] = "WPA2PSK";
162 const char kEncryptionAES
[] = "AES";
163 const char kEncryptionNone
[] = "none";
164 const char kEncryptionTKIP
[] = "TKIP";
165 const char kEncryptionWEP
[] = "WEP";
166 const char kKeyTypeNetwork
[] = "networkKey";
167 const char kKeyTypePassphrase
[] = "passPhrase";
173 // Implementation of WiFiService for Windows.
174 class WiFiServiceImpl
: public WiFiService
{
177 virtual ~WiFiServiceImpl();
179 // WiFiService interface implementation.
180 virtual void Initialize(
181 scoped_refptr
<base::SequencedTaskRunner
> task_runner
) OVERRIDE
;
183 virtual void UnInitialize() OVERRIDE
;
185 virtual void GetProperties(const std::string
& network_guid
,
186 base::DictionaryValue
* properties
,
187 std::string
* error
) OVERRIDE
;
189 virtual void GetManagedProperties(const std::string
& network_guid
,
190 base::DictionaryValue
* managed_properties
,
191 std::string
* error
) OVERRIDE
;
193 virtual void GetState(const std::string
& network_guid
,
194 base::DictionaryValue
* properties
,
195 std::string
* error
) OVERRIDE
;
197 virtual void SetProperties(const std::string
& network_guid
,
198 scoped_ptr
<base::DictionaryValue
> properties
,
199 std::string
* error
) OVERRIDE
;
201 virtual void CreateNetwork(bool shared
,
202 scoped_ptr
<base::DictionaryValue
> properties
,
203 std::string
* network_guid
,
204 std::string
* error
) OVERRIDE
;
206 virtual void GetVisibleNetworks(const std::string
& network_type
,
207 base::ListValue
* network_list
,
208 bool include_details
) OVERRIDE
;
210 virtual void RequestNetworkScan() OVERRIDE
;
212 virtual void StartConnect(const std::string
& network_guid
,
213 std::string
* error
) OVERRIDE
;
215 virtual void StartDisconnect(const std::string
& network_guid
,
216 std::string
* error
) OVERRIDE
;
218 virtual void GetKeyFromSystem(const std::string
& network_guid
,
219 std::string
* key_data
,
220 std::string
* error
) OVERRIDE
;
222 virtual void SetEventObservers(
223 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy
,
224 const NetworkGuidListCallback
& networks_changed_observer
,
225 const NetworkGuidListCallback
& network_list_changed_observer
) OVERRIDE
;
227 virtual void RequestConnectedNetworkUpdate() OVERRIDE
{}
230 typedef int32 EncryptionType
;
231 enum EncryptionTypeEnum
{
232 kEncryptionTypeAny
= 0,
233 kEncryptionTypeAES
= 1,
234 kEncryptionTypeTKIP
= 2
237 // Static callback for Windows WLAN_NOTIFICATION. Calls OnWlanNotification
238 // on WiFiServiceImpl passed back as |context|.
239 static void __stdcall
OnWlanNotificationCallback(
240 PWLAN_NOTIFICATION_DATA wlan_notification_data
,
243 // Callback for Windows WLAN_NOTIFICATION. Called on random thread from
244 // OnWlanNotificationCallback. Handles network connectivity and scan complete
245 // notification and posts tasks to main thread.
246 void OnWlanNotification(PWLAN_NOTIFICATION_DATA wlan_notification_data
);
248 // Handles NetworkScanComplete notification on main thread. Sends
249 // |NetworkListChanged| event with new list of visible networks.
250 void OnNetworkScanCompleteOnMainThread();
252 // Wait up to |kMaxAttempts| with |kAttemptDelayMs| delay for connection
253 // to network with |network_guid|. Reset DHCP and Notify that |NetworkChanged|
255 void WaitForNetworkConnect(const std::string
& network_guid
, int attempt
);
257 // Check |error_code| and if is not |ERROR_SUCCESS|, then store |error_name|
259 bool CheckError(DWORD error_code
,
260 const std::string
& error_name
,
261 std::string
* error
) const;
263 // Return |iterator| to network identified by |network_guid| in |networks|
265 NetworkList::iterator
FindNetwork(NetworkList
& networks
,
266 const std::string
& network_guid
);
268 // Save currently connected network profile so it can be re-connected later.
269 DWORD
SaveCurrentConnectedNetwork(const NetworkProperties
& properties
);
271 // Sort networks, so connected/connecting is up front, then by type:
272 // Ethernet, WiFi, Cellular, VPN
273 static void SortNetworks(NetworkList
* networks
);
275 // Open a WLAN client handle, register for WLAN notifications.
276 DWORD
OpenClientHandle();
278 // Reset DHCP on wireless network to work around an issue when Windows
279 // takes forever to connect to the network, e.g. after Chromecast
283 // Find |adapter_index_map| by |interface_guid| for DHCP reset.
284 DWORD
FindAdapterIndexMapByGUID(const GUID
& interface_guid
,
285 IP_ADAPTER_INDEX_MAP
* adapter_index_map
);
287 // Avoid the network location wizard to pop up when network is connected.
288 // Preserve current value in |saved_nw_category_wizard_|.
289 DWORD
DisableNwCategoryWizard();
291 // Restore network location wizard to value saved by DisableNwCategoryWizard.
292 DWORD
RestoreNwCategoryWizard();
294 // Ensure that |client_| handle is initialized.
295 DWORD
EnsureInitialized();
297 // Close |client_| handle if it is open.
298 DWORD
CloseClientHandle();
300 // Get |profile_name| from unique |network_guid|.
301 base::string16
ProfileNameFromGUID(const std::string
& network_guid
) const {
302 return base::UTF8ToUTF16(network_guid
);
305 // Get |dot11_ssid| from unique |network_guid|.
306 DOT11_SSID
SSIDFromGUID(const std::string
& network_guid
) const;
308 // Get unique |network_guid| string based on |dot11_ssid|.
309 std::string
GUIDFromSSID(const DOT11_SSID
& dot11_ssid
) const {
310 return std::string(reinterpret_cast<const char*>(dot11_ssid
.ucSSID
),
311 dot11_ssid
.uSSIDLength
);
314 // Get network |ssid| string based on |wlan|.
315 std::string
SSIDFromWLAN(const WLAN_AVAILABLE_NETWORK
& wlan
) const {
316 return GUIDFromSSID(wlan
.dot11Ssid
);
319 // Get unique |network_guid| string based on |wlan|.
320 std::string
GUIDFromWLAN(const WLAN_AVAILABLE_NETWORK
& wlan
) const {
321 return SSIDFromWLAN(wlan
);
324 // Deduce |onc::wifi| security from |alg|.
325 std::string
SecurityFromDot11AuthAlg(DOT11_AUTH_ALGORITHM alg
) const;
327 // Deduce |onc::connection_state| from |wlan_state|.
328 std::string
ConnectionStateFromInterfaceState(
329 WLAN_INTERFACE_STATE wlan_state
) const;
331 // Convert |EncryptionType| into WPA(2) encryption type string.
332 std::string
WpaEncryptionFromEncryptionType(
333 EncryptionType encryption_type
) const;
335 // Deduce WLANProfile |authEncryption| values from |onc::wifi| security.
336 bool AuthEncryptionFromSecurity(const std::string
& security
,
337 EncryptionType encryption_type
,
338 std::string
* authentication
,
339 std::string
* encryption
,
340 std::string
* key_type
) const;
342 // Populate |properties| based on |wlan|.
343 void NetworkPropertiesFromAvailableNetwork(const WLAN_AVAILABLE_NETWORK
& wlan
,
344 NetworkProperties
* properties
);
346 // Update |properties| based on bss info from |wlan_bss_list|. If |bssid| in
347 // |properties| is not empty, then it is not changed and |frequency| is set
348 // based on that bssid.
349 void UpdateNetworkPropertiesFromBssList(const std::string
& network_guid
,
350 const WLAN_BSS_LIST
& wlan_bss_list
,
351 NetworkProperties
* properties
);
353 // Get the list of visible wireless networks.
354 DWORD
GetVisibleNetworkList(NetworkList
* network_list
);
356 // Get properties of the network currently used (connected or in transition)
357 // by interface. Populate |current_properties| on success.
358 DWORD
GetCurrentProperties(NetworkProperties
* current_properties
);
360 // Connect to network |network_guid| using previosly stored profile if exists,
361 // or just network sid. If |frequency| is not |kFrequencyUnknown| then
362 // connects only to BSS which uses that frequency and returns
363 // |ERROR_NOT_FOUND| if such BSS cannot be found.
364 DWORD
Connect(const std::string
& network_guid
, Frequency frequency
);
366 // Disconnect from currently connected network if any.
369 // Get desired connection freqency if it was set using |SetProperties|.
370 // Default to |kFrequencyAny|.
371 Frequency
GetFrequencyToConnect(const std::string
& network_guid
) const;
373 // Get DOT11_BSSID_LIST of desired BSSIDs to connect to |ssid| network on
374 // given |frequency|.
375 DWORD
GetDesiredBssList(DOT11_SSID
& ssid
,
377 scoped_ptr
<DOT11_BSSID_LIST
>* desired_list
);
379 // Normalizes |frequency_in_mhz| into one of |Frequency| values.
380 Frequency
GetNormalizedFrequency(int frequency_in_mhz
) const;
382 // Create |profile_xml| based on |network_properties|. If |encryption_type|
383 // is |kEncryptionTypeAny| applies the type most suitable for parameters in
384 // |network_properties|.
385 bool CreateProfile(const NetworkProperties
& network_properties
,
386 EncryptionType encryption_type
,
387 std::string
* profile_xml
);
389 // Save temporary wireless profile for |network_guid|.
390 DWORD
SaveTempProfile(const std::string
& network_guid
);
392 // Get previously stored |profile_xml| for |network_guid|.
393 // If |get_plaintext_key| is true, and process has sufficient privileges, then
394 // <sharedKey> data in |profile_xml| will be unprotected.
395 DWORD
GetProfile(const std::string
& network_guid
,
396 bool get_plaintext_key
,
397 std::string
* profile_xml
);
399 // Set |profile_xml| to current user or all users depending on |shared| flag.
400 // If |overwrite| is false, then returns an error if profile exists.
401 DWORD
SetProfile(bool shared
, const std::string
& profile_xml
, bool overwrite
);
403 // Return true if there is previously stored profile xml for |network_guid|.
404 bool HaveProfile(const std::string
& network_guid
);
406 // Delete profile that was created, but failed to connect.
407 DWORD
DeleteCreatedProfile(const std::string
& network_guid
);
409 // Notify |network_list_changed_observer_| that list of visible networks has
410 // changed to |networks|.
411 void NotifyNetworkListChanged(const NetworkList
& networks
);
413 // Notify |networks_changed_observer_| that network |network_guid| status has
415 void NotifyNetworkChanged(const std::string
& network_guid
);
417 // Load WlanApi.dll from SystemDirectory and get Api function pointers.
418 DWORD
LoadWlanLibrary();
419 // Instance of WlanApi.dll.
420 HINSTANCE wlan_api_library_
;
421 // WlanApi function pointers
422 WlanConnectFunction WlanConnect_function_
;
423 WlanCloseHandleFunction WlanCloseHandle_function_
;
424 WlanDeleteProfileFunction WlanDeleteProfile_function_
;
425 WlanDisconnectFunction WlanDisconnect_function_
;
426 WlanEnumInterfacesFunction WlanEnumInterfaces_function_
;
427 WlanFreeMemoryFunction WlanFreeMemory_function_
;
428 WlanGetAvailableNetworkListFunction WlanGetAvailableNetworkList_function_
;
429 // WlanGetNetworkBssList function may not be avaiable on Windows XP.
430 WlanGetNetworkBssListFunction WlanGetNetworkBssList_function_
;
431 WlanGetProfileFunction WlanGetProfile_function_
;
432 WlanOpenHandleFunction WlanOpenHandle_function_
;
433 WlanQueryInterfaceFunction WlanQueryInterface_function_
;
434 WlanRegisterNotificationFunction WlanRegisterNotification_function_
;
435 WlanScanFunction WlanScan_function_
;
436 WlanSetProfileFunction WlanSetProfile_function_
;
437 // WlanSaveTemporaryProfile function may not be avaiable on Windows XP.
438 WlanSaveTemporaryProfileFunction WlanSaveTemporaryProfile_function_
;
440 // WLAN service handle.
442 // GUID of the currently connected interface, if any, otherwise the GUID of
443 // one of the WLAN interfaces.
444 GUID interface_guid_
;
445 // Temporary storage of network properties indexed by |network_guid|. Persist
447 base::DictionaryValue connect_properties_
;
448 // Preserved WLAN profile xml.
449 std::map
<std::string
, std::string
> saved_profiles_xml_
;
450 // Created WLAN Profiles, indexed by |network_guid|. Contains xml with TKIP
451 // encryption type saved by |CreateNetwork| if applicable. Profile has to be
452 // deleted if connection fails. Implicitly created profiles have to be deleted
453 // if connection succeeds. Persist only in memory.
454 base::DictionaryValue created_profiles_
;
455 // Observer to get notified when network(s) have changed (e.g. connect).
456 NetworkGuidListCallback networks_changed_observer_
;
457 // Observer to get notified when network list has changed (scan complete).
458 NetworkGuidListCallback network_list_changed_observer_
;
459 // Saved value of network location wizard show value.
460 scoped_ptr
<DWORD
> saved_nw_category_wizard_
;
461 // MessageLoopProxy to post events on UI thread.
462 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy_
;
463 // Task runner for worker tasks.
464 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
465 // If |false|, then |networks_changed_observer_| is not notified.
466 bool enable_notify_network_changed_
;
467 // Number of attempts to check that network has connected successfully.
468 static const int kMaxAttempts
= 100;
469 // Delay between attempts to check that network has connected successfully.
470 static const int kAttemptDelayMs
= 100;
471 DISALLOW_COPY_AND_ASSIGN(WiFiServiceImpl
);
474 WiFiServiceImpl::WiFiServiceImpl()
475 : wlan_api_library_(NULL
),
476 WlanConnect_function_(NULL
),
477 WlanCloseHandle_function_(NULL
),
478 WlanDeleteProfile_function_(NULL
),
479 WlanDisconnect_function_(NULL
),
480 WlanEnumInterfaces_function_(NULL
),
481 WlanFreeMemory_function_(NULL
),
482 WlanGetAvailableNetworkList_function_(NULL
),
483 WlanGetNetworkBssList_function_(NULL
),
484 WlanGetProfile_function_(NULL
),
485 WlanOpenHandle_function_(NULL
),
486 WlanRegisterNotification_function_(NULL
),
487 WlanSaveTemporaryProfile_function_(NULL
),
488 WlanScan_function_(NULL
),
489 WlanSetProfile_function_(NULL
),
491 enable_notify_network_changed_(true) {}
493 WiFiServiceImpl::~WiFiServiceImpl() { UnInitialize(); }
495 void WiFiServiceImpl::Initialize(
496 scoped_refptr
<base::SequencedTaskRunner
> task_runner
) {
498 task_runner_
.swap(task_runner
);
499 // Restore NwCategoryWizard in case if we crashed during connect.
500 RestoreNwCategoryWizard();
504 void WiFiServiceImpl::UnInitialize() {
508 void WiFiServiceImpl::GetProperties(const std::string
& network_guid
,
509 base::DictionaryValue
* properties
,
510 std::string
* error
) {
511 DWORD error_code
= EnsureInitialized();
512 if (CheckError(error_code
, kErrorWiFiService
, error
))
515 NetworkProperties connected_properties
;
516 error_code
= GetCurrentProperties(&connected_properties
);
517 if (error_code
== ERROR_SUCCESS
&&
518 connected_properties
.guid
== network_guid
) {
519 properties
->Swap(connected_properties
.ToValue(false).get());
523 NetworkList network_list
;
524 error_code
= GetVisibleNetworkList(&network_list
);
525 if (error_code
== ERROR_SUCCESS
) {
526 NetworkList::const_iterator it
= FindNetwork(network_list
, network_guid
);
527 if (it
!= network_list
.end()) {
528 DVLOG(1) << "Get Properties: " << network_guid
<< ":"
529 << it
->connection_state
;
530 properties
->Swap(it
->ToValue(false).get());
533 error_code
= ERROR_NOT_FOUND
;
536 CheckError(error_code
, kErrorWiFiService
, error
);
539 void WiFiServiceImpl::GetManagedProperties(
540 const std::string
& network_guid
,
541 base::DictionaryValue
* managed_properties
,
542 std::string
* error
) {
543 CheckError(ERROR_CALL_NOT_IMPLEMENTED
, kErrorWiFiService
, error
);
546 void WiFiServiceImpl::GetState(const std::string
& network_guid
,
547 base::DictionaryValue
* properties
,
548 std::string
* error
) {
549 CheckError(ERROR_CALL_NOT_IMPLEMENTED
, kErrorWiFiService
, error
);
552 void WiFiServiceImpl::SetProperties(
553 const std::string
& network_guid
,
554 scoped_ptr
<base::DictionaryValue
> properties
,
555 std::string
* error
) {
556 // Temporary preserve WiFi properties (desired frequency, wifi password) to
557 // use in StartConnect.
558 DCHECK(properties
.get());
559 if (!properties
->HasKey(onc::network_type::kWiFi
)) {
560 DVLOG(0) << "Missing WiFi properties:" << *properties
;
561 *error
= kErrorWiFiService
;
565 base::DictionaryValue
* existing_properties
;
566 // If the network properties already exist, don't override previously set
567 // properties, unless they are set in |properties|.
568 if (connect_properties_
.GetDictionaryWithoutPathExpansion(
569 network_guid
, &existing_properties
)) {
570 existing_properties
->MergeDictionary(properties
.get());
572 connect_properties_
.SetWithoutPathExpansion(network_guid
,
573 properties
.release());
577 void WiFiServiceImpl::CreateNetwork(
579 scoped_ptr
<base::DictionaryValue
> properties
,
580 std::string
* network_guid
,
581 std::string
* error
) {
582 DWORD error_code
= EnsureInitialized();
583 if (CheckError(error_code
, kErrorWiFiService
, error
))
586 NetworkProperties network_properties
;
587 if (!network_properties
.UpdateFromValue(*properties
)) {
588 CheckError(ERROR_INVALID_DATA
, kErrorWiFiService
, error
);
592 network_properties
.guid
= network_properties
.ssid
;
593 std::string profile_xml
;
594 if (!CreateProfile(network_properties
, kEncryptionTypeAny
, &profile_xml
)) {
595 CheckError(ERROR_INVALID_DATA
, kErrorWiFiService
, error
);
599 error_code
= SetProfile(shared
, profile_xml
, false);
600 if (CheckError(error_code
, kErrorWiFiService
, error
)) {
601 DVLOG(0) << profile_xml
;
605 // WAP and WAP2 networks could use either AES or TKIP encryption type.
606 // Preserve alternative profile to use in case if connection with default
607 // encryption type fails.
608 std::string tkip_profile_xml
;
609 if (!CreateProfile(network_properties
,
611 &tkip_profile_xml
)) {
612 CheckError(ERROR_INVALID_DATA
, kErrorWiFiService
, error
);
616 if (tkip_profile_xml
!= profile_xml
) {
617 scoped_ptr
<base::DictionaryValue
> tkip_profile(new base::DictionaryValue());
618 tkip_profile
->SetString(kProfileXmlKey
, tkip_profile_xml
);
619 tkip_profile
->SetBoolean(kProfileSharedKey
, shared
);
620 created_profiles_
.SetWithoutPathExpansion(network_properties
.guid
,
621 tkip_profile
.release());
624 *network_guid
= network_properties
.guid
;
627 void WiFiServiceImpl::GetVisibleNetworks(const std::string
& network_type
,
628 base::ListValue
* network_list
,
629 bool include_details
) {
630 if (!network_type
.empty() &&
631 network_type
!= onc::network_type::kAllTypes
&&
632 network_type
!= onc::network_type::kWiFi
) {
636 DWORD error
= EnsureInitialized();
637 if (error
== ERROR_SUCCESS
) {
638 NetworkList networks
;
639 error
= GetVisibleNetworkList(&networks
);
640 if (error
== ERROR_SUCCESS
&& !networks
.empty()) {
641 SortNetworks(&networks
);
642 for (NetworkList::const_iterator it
= networks
.begin();
643 it
!= networks
.end();
645 scoped_ptr
<base::DictionaryValue
> network(
646 it
->ToValue(!include_details
));
647 network_list
->Append(network
.release());
653 void WiFiServiceImpl::RequestNetworkScan() {
654 DWORD error
= EnsureInitialized();
655 if (error
== ERROR_SUCCESS
) {
656 WlanScan_function_(client_
, &interface_guid_
, NULL
, NULL
, NULL
);
660 void WiFiServiceImpl::StartConnect(const std::string
& network_guid
,
661 std::string
* error
) {
662 DVLOG(1) << "Start Connect: " << network_guid
;
663 DWORD error_code
= EnsureInitialized();
664 if (CheckError(error_code
, kErrorWiFiService
, error
))
667 // Check, if the network is already connected on desired frequency.
668 Frequency frequency
= GetFrequencyToConnect(network_guid
);
669 NetworkProperties properties
;
670 GetCurrentProperties(&properties
);
671 bool already_connected
=
672 network_guid
== properties
.guid
&&
673 properties
.connection_state
== onc::connection_state::kConnected
&&
674 (frequency
== kFrequencyAny
|| frequency
== properties
.frequency
);
676 // Connect only if network |network_guid| is not connected already.
677 if (!already_connected
) {
678 SaveCurrentConnectedNetwork(properties
);
679 error_code
= Connect(network_guid
, frequency
);
681 if (error_code
== ERROR_SUCCESS
) {
682 // Notify that previously connected network has changed.
683 NotifyNetworkChanged(properties
.guid
);
684 // Start waiting for network connection state change.
685 if (!networks_changed_observer_
.is_null()) {
686 DisableNwCategoryWizard();
687 // Disable automatic network change notifications as they get fired
688 // when network is just connected, but not yet accessible (doesn't
689 // have valid IP address).
690 enable_notify_network_changed_
= false;
691 WaitForNetworkConnect(network_guid
, 0);
694 } else if (error_code
== ERROR_ACCESS_DENIED
) {
695 CheckError(error_code
, kErrorNotConfigured
, error
);
697 CheckError(error_code
, kErrorWiFiService
, error
);
701 void WiFiServiceImpl::StartDisconnect(const std::string
& network_guid
,
702 std::string
* error
) {
703 DVLOG(1) << "Start Disconnect: " << network_guid
;
704 DWORD error_code
= EnsureInitialized();
705 if (CheckError(error_code
, kErrorWiFiService
, error
))
708 // Check, if the network is currently connected.
709 NetworkProperties properties
;
710 GetCurrentProperties(&properties
);
711 if (network_guid
== properties
.guid
) {
712 if (properties
.connection_state
== onc::connection_state::kConnected
)
713 SaveCurrentConnectedNetwork(properties
);
714 error_code
= Disconnect();
715 if (error_code
== ERROR_SUCCESS
) {
716 NotifyNetworkChanged(network_guid
);
720 CheckError(error_code
, kErrorWiFiService
, error
);
723 void WiFiServiceImpl::GetKeyFromSystem(const std::string
& network_guid
,
724 std::string
* key_data
,
725 std::string
* error
) {
726 DWORD error_code
= EnsureInitialized();
727 if (CheckError(error_code
, kErrorWiFiService
, error
))
730 std::string profile_xml
;
731 error_code
= GetProfile(network_guid
, true, &profile_xml
);
732 if (CheckError(error_code
, kErrorWiFiService
, error
))
735 const char kSharedKeyElement
[] = "sharedKey";
736 const char kProtectedElement
[] = "protected";
737 const char kKeyMaterialElement
[] = "keyMaterial";
739 // Quick check to verify presence of <sharedKey> element.
740 if (profile_xml
.find(kSharedKeyElement
) == std::string::npos
) {
741 *error
= kErrorWiFiService
;
746 if (reader
.Load(profile_xml
)) {
747 while (reader
.Read()) {
748 reader
.SkipToElement();
749 if (reader
.NodeName() == kSharedKeyElement
) {
750 while (reader
.Read()) {
751 reader
.SkipToElement();
752 if (reader
.NodeName() == kKeyMaterialElement
) {
753 reader
.ReadElementContent(key_data
);
754 } else if (reader
.NodeName() == kProtectedElement
) {
755 std::string protected_data
;
756 reader
.ReadElementContent(&protected_data
);
757 // Without UAC privilege escalation call to |GetProfile| with
758 // |WLAN_PROFILE_GET_PLAINTEXT_KEY| flag returns success, but has
759 // protected keyMaterial. Report an error in this case.
760 if (protected_data
!= "false") {
761 *error
= kErrorWiFiService
;
771 // Did not find passphrase in the profile.
772 *error
= kErrorWiFiService
;
775 void WiFiServiceImpl::SetEventObservers(
776 scoped_refptr
<base::MessageLoopProxy
> message_loop_proxy
,
777 const NetworkGuidListCallback
& networks_changed_observer
,
778 const NetworkGuidListCallback
& network_list_changed_observer
) {
779 DWORD error_code
= EnsureInitialized();
780 if (error_code
!= ERROR_SUCCESS
)
782 message_loop_proxy_
.swap(message_loop_proxy
);
783 if (!networks_changed_observer_
.is_null() ||
784 !network_list_changed_observer_
.is_null()) {
785 // Stop listening to WLAN notifications.
786 WlanRegisterNotification_function_(client_
,
787 WLAN_NOTIFICATION_SOURCE_NONE
,
789 OnWlanNotificationCallback
,
794 networks_changed_observer_
= networks_changed_observer
;
795 network_list_changed_observer_
= network_list_changed_observer
;
796 if (!networks_changed_observer_
.is_null() ||
797 !network_list_changed_observer_
.is_null()) {
798 // Start listening to WLAN notifications.
799 WlanRegisterNotification_function_(client_
,
800 WLAN_NOTIFICATION_SOURCE_ALL
,
802 OnWlanNotificationCallback
,
809 void WiFiServiceImpl::OnWlanNotificationCallback(
810 PWLAN_NOTIFICATION_DATA wlan_notification_data
,
812 WiFiServiceImpl
* service
= reinterpret_cast<WiFiServiceImpl
*>(context
);
813 service
->OnWlanNotification(wlan_notification_data
);
816 void WiFiServiceImpl::OnWlanNotification(
817 PWLAN_NOTIFICATION_DATA wlan_notification_data
) {
818 if (message_loop_proxy_
== NULL
)
820 switch (wlan_notification_data
->NotificationCode
) {
821 case wlan_notification_acm_disconnected
:
822 case wlan_notification_acm_connection_complete
:
823 case wlan_notification_acm_connection_attempt_fail
: {
824 PWLAN_CONNECTION_NOTIFICATION_DATA wlan_connection_data
=
825 reinterpret_cast<PWLAN_CONNECTION_NOTIFICATION_DATA
>(
826 wlan_notification_data
->pData
);
827 message_loop_proxy_
->PostTask(
829 base::Bind(&WiFiServiceImpl::NotifyNetworkChanged
,
830 base::Unretained(this),
831 GUIDFromSSID(wlan_connection_data
->dot11Ssid
)));
834 case wlan_notification_acm_scan_complete
:
835 case wlan_notification_acm_interface_removal
:
836 message_loop_proxy_
->PostTask(
838 base::Bind(&WiFiServiceImpl::OnNetworkScanCompleteOnMainThread
,
839 base::Unretained(this)));
844 void WiFiServiceImpl::OnNetworkScanCompleteOnMainThread() {
845 NetworkList networks
;
846 // Get current list of visible networks and notify that network list has
848 DWORD error
= GetVisibleNetworkList(&networks
);
849 if (error
!= ERROR_SUCCESS
)
851 NotifyNetworkListChanged(networks
);
854 void WiFiServiceImpl::WaitForNetworkConnect(const std::string
& network_guid
,
856 // If network didn't get connected in |kMaxAttempts|, then try to connect
857 // using different profile if it was created recently.
858 if (attempt
> kMaxAttempts
) {
859 LOG(ERROR
) << kMaxAttempts
<< " attempts exceeded waiting for connect to "
862 base::DictionaryValue
* created_profile
= NULL
;
863 // Check, whether this connection is using newly created profile.
864 if (created_profiles_
.GetDictionaryWithoutPathExpansion(
865 network_guid
, &created_profile
)) {
866 std::string tkip_profile_xml
;
868 // Check, if this connection there is alternative TKIP profile xml that
869 // should be tried. If there is, then set it up and try to connect again.
870 if (created_profile
->GetString(kProfileXmlKey
, &tkip_profile_xml
) &&
871 created_profile
->GetBoolean(kProfileSharedKey
, &shared
)) {
872 // Remove TKIP profile xml, so it will not be tried again.
873 created_profile
->Remove(kProfileXmlKey
, NULL
);
874 created_profile
->Remove(kProfileSharedKey
, NULL
);
875 DWORD error_code
= SetProfile(shared
, tkip_profile_xml
, true);
876 if (error_code
== ERROR_SUCCESS
) {
877 // Try to connect with new profile.
878 error_code
= Connect(network_guid
,
879 GetFrequencyToConnect(network_guid
));
880 if (error_code
== ERROR_SUCCESS
) {
881 // Start waiting again.
882 WaitForNetworkConnect(network_guid
, 0);
885 LOG(ERROR
) << "Failed to set created profile for " << network_guid
886 << " error=" << error_code
;
890 // Connection has failed, so delete bad created profile.
891 DWORD error_code
= DeleteCreatedProfile(network_guid
);
892 if (error_code
!= ERROR_SUCCESS
) {
893 LOG(ERROR
) << "Failed to delete created profile for " << network_guid
894 << " error=" << error_code
;
898 // Restore automatic network change notifications and stop waiting.
899 enable_notify_network_changed_
= true;
900 RestoreNwCategoryWizard();
903 NetworkProperties current_properties
;
904 DWORD error
= GetCurrentProperties(¤t_properties
);
905 if (network_guid
== current_properties
.guid
&&
906 current_properties
.connection_state
==
907 onc::connection_state::kConnected
) {
908 DVLOG(1) << "WiFi Connected, Reset DHCP: " << network_guid
;
909 // Even though wireless network is now connected, it may still be unusable,
910 // e.g. after Chromecast device reset. Reset DHCP on wireless network to
911 // work around this issue.
913 if (error
!= ERROR_SUCCESS
)
915 // There is no need to keep created profile as network is connected.
916 created_profiles_
.RemoveWithoutPathExpansion(network_guid
, NULL
);
917 // Restore previously suppressed notifications.
918 enable_notify_network_changed_
= true;
919 RestoreNwCategoryWizard();
920 NotifyNetworkChanged(network_guid
);
922 // Continue waiting for network connection state change.
923 task_runner_
->PostDelayedTask(
925 base::Bind(&WiFiServiceImpl::WaitForNetworkConnect
,
926 base::Unretained(this),
929 base::TimeDelta::FromMilliseconds(kAttemptDelayMs
));
933 bool WiFiServiceImpl::CheckError(DWORD error_code
,
934 const std::string
& error_name
,
935 std::string
* error
) const {
936 if (error_code
!= ERROR_SUCCESS
) {
937 DLOG(ERROR
) << "WiFiService Error " << error_code
<< ": " << error_name
;
944 NetworkList::iterator
WiFiServiceImpl::FindNetwork(
945 NetworkList
& networks
,
946 const std::string
& network_guid
) {
947 for (NetworkList::iterator it
= networks
.begin(); it
!= networks
.end();
949 if (it
->guid
== network_guid
)
952 return networks
.end();
955 DWORD
WiFiServiceImpl::SaveCurrentConnectedNetwork(
956 const NetworkProperties
& current_properties
) {
957 DWORD error
= ERROR_SUCCESS
;
958 // Save currently connected network.
959 if (!current_properties
.guid
.empty() &&
960 current_properties
.connection_state
==
961 onc::connection_state::kConnected
) {
962 error
= SaveTempProfile(current_properties
.guid
);
967 void WiFiServiceImpl::SortNetworks(NetworkList
* networks
) {
968 networks
->sort(NetworkProperties::OrderByType
);
971 DWORD
WiFiServiceImpl::LoadWlanLibrary() {
972 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
974 if (!PathService::Get(base::DIR_SYSTEM
, &path
)) {
975 LOG(ERROR
) << "Unable to get system path.";
976 return ERROR_NOT_FOUND
;
978 wlan_api_library_
= ::LoadLibraryEx(path
.Append(kWlanApiDll
).value().c_str(),
980 LOAD_WITH_ALTERED_SEARCH_PATH
);
981 if (!wlan_api_library_
) {
982 LOG(ERROR
) << "Unable to load WlanApi.dll.";
983 return ERROR_NOT_FOUND
;
986 // Initialize WlanApi function pointers
987 WlanConnect_function_
=
988 reinterpret_cast<WlanConnectFunction
>(
989 ::GetProcAddress(wlan_api_library_
, kWlanConnect
));
990 WlanCloseHandle_function_
=
991 reinterpret_cast<WlanCloseHandleFunction
>(
992 ::GetProcAddress(wlan_api_library_
, kWlanCloseHandle
));
993 WlanDeleteProfile_function_
=
994 reinterpret_cast<WlanDeleteProfileFunction
>(
995 ::GetProcAddress(wlan_api_library_
, kWlanDeleteProfile
));
996 WlanDisconnect_function_
=
997 reinterpret_cast<WlanDisconnectFunction
>(
998 ::GetProcAddress(wlan_api_library_
, kWlanDisconnect
));
999 WlanEnumInterfaces_function_
=
1000 reinterpret_cast<WlanEnumInterfacesFunction
>(
1001 ::GetProcAddress(wlan_api_library_
, kWlanEnumInterfaces
));
1002 WlanFreeMemory_function_
=
1003 reinterpret_cast<WlanFreeMemoryFunction
>(
1004 ::GetProcAddress(wlan_api_library_
, kWlanFreeMemory
));
1005 WlanGetAvailableNetworkList_function_
=
1006 reinterpret_cast<WlanGetAvailableNetworkListFunction
>(
1007 ::GetProcAddress(wlan_api_library_
, kWlanGetAvailableNetworkList
));
1008 WlanGetNetworkBssList_function_
=
1009 reinterpret_cast<WlanGetNetworkBssListFunction
>(
1010 ::GetProcAddress(wlan_api_library_
, kWlanGetNetworkBssList
));
1011 WlanGetProfile_function_
=
1012 reinterpret_cast<WlanGetProfileFunction
>(
1013 ::GetProcAddress(wlan_api_library_
, kWlanGetProfile
));
1014 WlanOpenHandle_function_
=
1015 reinterpret_cast<WlanOpenHandleFunction
>(
1016 ::GetProcAddress(wlan_api_library_
, kWlanOpenHandle
));
1017 WlanQueryInterface_function_
=
1018 reinterpret_cast<WlanQueryInterfaceFunction
>(
1019 ::GetProcAddress(wlan_api_library_
, kWlanQueryInterface
));
1020 WlanRegisterNotification_function_
=
1021 reinterpret_cast<WlanRegisterNotificationFunction
>(
1022 ::GetProcAddress(wlan_api_library_
, kWlanRegisterNotification
));
1023 WlanSaveTemporaryProfile_function_
=
1024 reinterpret_cast<WlanSaveTemporaryProfileFunction
>(
1025 ::GetProcAddress(wlan_api_library_
, kWlanSaveTemporaryProfile
));
1026 WlanScan_function_
=
1027 reinterpret_cast<WlanScanFunction
>(
1028 ::GetProcAddress(wlan_api_library_
, kWlanScan
));
1029 WlanSetProfile_function_
=
1030 reinterpret_cast<WlanSetProfileFunction
>(
1031 ::GetProcAddress(wlan_api_library_
, kWlanSetProfile
));
1033 if (!WlanConnect_function_
||
1034 !WlanCloseHandle_function_
||
1035 !WlanDeleteProfile_function_
||
1036 !WlanDisconnect_function_
||
1037 !WlanEnumInterfaces_function_
||
1038 !WlanFreeMemory_function_
||
1039 !WlanGetAvailableNetworkList_function_
||
1040 !WlanGetProfile_function_
||
1041 !WlanOpenHandle_function_
||
1042 !WlanQueryInterface_function_
||
1043 !WlanRegisterNotification_function_
||
1044 !WlanScan_function_
||
1045 !WlanSetProfile_function_
) {
1046 LOG(ERROR
) << "Unable to find required WlanApi function.";
1047 FreeLibrary(wlan_api_library_
);
1048 wlan_api_library_
= NULL
;
1049 return ERROR_NOT_FOUND
;
1052 // Some WlanApi functions may not be available on XP.
1053 if (!WlanGetNetworkBssList_function_
||
1054 !WlanSaveTemporaryProfile_function_
) {
1055 DVLOG(1) << "WlanApi function is not be available on XP.";
1058 return ERROR_SUCCESS
;
1061 DWORD
WiFiServiceImpl::OpenClientHandle() {
1062 DWORD error
= LoadWlanLibrary();
1063 DWORD service_version
= 0;
1065 if (error
!= ERROR_SUCCESS
)
1068 // Open a handle to the service.
1069 error
= WlanOpenHandle_function_(1, NULL
, &service_version
, &client_
);
1071 PWLAN_INTERFACE_INFO_LIST interface_list
= NULL
;
1072 if (error
== ERROR_SUCCESS
) {
1073 // Enumerate wireless interfaces.
1074 error
= WlanEnumInterfaces_function_(client_
, NULL
, &interface_list
);
1075 if (error
== ERROR_SUCCESS
) {
1076 if (interface_list
!= NULL
&& interface_list
->dwNumberOfItems
!= 0) {
1077 // Remember first interface just in case if none are connected.
1078 interface_guid_
= interface_list
->InterfaceInfo
[0].InterfaceGuid
;
1079 // Try to find a connected interface.
1080 for (DWORD itf
= 0; itf
< interface_list
->dwNumberOfItems
; ++itf
) {
1081 if (interface_list
->InterfaceInfo
[itf
].isState
==
1082 wlan_interface_state_connected
) {
1083 // Found connected interface, remember it!
1084 interface_guid_
= interface_list
->InterfaceInfo
[itf
].InterfaceGuid
;
1089 error
= ERROR_NOINTERFACE
;
1093 if (interface_list
!= NULL
)
1094 WlanFreeMemory_function_(interface_list
);
1099 DWORD
WiFiServiceImpl::ResetDHCP() {
1100 IP_ADAPTER_INDEX_MAP adapter_index_map
= {0};
1101 DWORD error
= FindAdapterIndexMapByGUID(interface_guid_
, &adapter_index_map
);
1102 if (error
!= ERROR_SUCCESS
) {
1103 LOG(ERROR
) << error
;
1106 error
= ::IpReleaseAddress(&adapter_index_map
);
1107 if (error
!= ERROR_SUCCESS
) {
1108 if (error
!= ERROR_ADDRESS_NOT_ASSOCIATED
) {
1109 LOG(ERROR
) << error
;
1112 DVLOG(1) << "Ignoring IpReleaseAddress Error: " << error
;
1114 error
= ::IpRenewAddress(&adapter_index_map
);
1115 if (error
!= ERROR_SUCCESS
)
1116 LOG(ERROR
) << error
;
1120 DWORD
WiFiServiceImpl::FindAdapterIndexMapByGUID(
1121 const GUID
& interface_guid
,
1122 IP_ADAPTER_INDEX_MAP
* adapter_index_map
) {
1123 base::string16 guid_string
;
1124 const int kGUIDSize
= 39;
1126 interface_guid
, WriteInto(&guid_string
, kGUIDSize
), kGUIDSize
);
1128 ULONG buffer_length
= 0;
1129 DWORD error
= ::GetInterfaceInfo(NULL
, &buffer_length
);
1130 if (error
== ERROR_INSUFFICIENT_BUFFER
) {
1131 scoped_ptr
<unsigned char[]> buffer(new unsigned char[buffer_length
]);
1132 IP_INTERFACE_INFO
* interface_info
=
1133 reinterpret_cast<IP_INTERFACE_INFO
*>(buffer
.get());
1134 error
= GetInterfaceInfo(interface_info
, &buffer_length
);
1135 if (error
== ERROR_SUCCESS
) {
1136 for (int adapter
= 0; adapter
< interface_info
->NumAdapters
; ++adapter
) {
1138 interface_info
->Adapter
[adapter
].Name
, guid_string
, false)) {
1139 *adapter_index_map
= interface_info
->Adapter
[adapter
];
1148 DWORD
WiFiServiceImpl::DisableNwCategoryWizard() {
1149 base::win::RegKey nw_category_wizard
;
1150 DWORD error
= nw_category_wizard
.Open(HKEY_CURRENT_USER
,
1151 kNwCategoryWizardRegKey
,
1152 KEY_READ
| KEY_SET_VALUE
);
1153 if (error
== ERROR_SUCCESS
) {
1154 // Save current value if present.
1155 if (nw_category_wizard
.HasValue(kNwCategoryWizardRegValue
)) {
1157 error
= nw_category_wizard
.ReadValueDW(kNwCategoryWizardRegValue
,
1159 if (error
== ERROR_SUCCESS
) {
1160 error
= nw_category_wizard
.WriteValue(kNwCategoryWizardSavedRegValue
,
1164 // Mark that temporary value has to be deleted.
1165 error
= nw_category_wizard
.WriteValue(kNwCategoryWizardDeleteRegValue
,
1169 // Disable network location wizard.
1170 error
= nw_category_wizard
.WriteValue(kNwCategoryWizardRegValue
,
1171 static_cast<DWORD
>(0));
1177 DWORD
WiFiServiceImpl::RestoreNwCategoryWizard() {
1178 base::win::RegKey nw_category_wizard
;
1179 DWORD error
= nw_category_wizard
.Open(HKEY_CURRENT_USER
,
1180 kNwCategoryWizardRegKey
,
1182 if (error
== ERROR_SUCCESS
) {
1183 // Restore saved value if present.
1184 if (nw_category_wizard
.HasValue(kNwCategoryWizardSavedRegValue
)) {
1186 error
= nw_category_wizard
.ReadValueDW(kNwCategoryWizardSavedRegValue
,
1188 if (error
== ERROR_SUCCESS
) {
1189 error
= nw_category_wizard
.WriteValue(kNwCategoryWizardRegValue
,
1191 error
= nw_category_wizard
.DeleteValue(kNwCategoryWizardSavedRegValue
);
1193 } else if (nw_category_wizard
.HasValue(kNwCategoryWizardDeleteRegValue
)) {
1194 error
= nw_category_wizard
.DeleteValue(kNwCategoryWizardRegValue
);
1195 error
= nw_category_wizard
.DeleteValue(kNwCategoryWizardDeleteRegValue
);
1202 DWORD
WiFiServiceImpl::EnsureInitialized() {
1203 if (client_
!= NULL
)
1204 return ERROR_SUCCESS
;
1205 return ERROR_NOINTERFACE
;
1208 DWORD
WiFiServiceImpl::CloseClientHandle() {
1209 DWORD error
= ERROR_SUCCESS
;
1210 if (client_
!= NULL
) {
1211 error
= WlanCloseHandle_function_(client_
, NULL
);
1214 if (wlan_api_library_
!= NULL
) {
1215 WlanConnect_function_
= NULL
;
1216 WlanCloseHandle_function_
= NULL
;
1217 WlanDeleteProfile_function_
= NULL
;
1218 WlanDisconnect_function_
= NULL
;
1219 WlanEnumInterfaces_function_
= NULL
;
1220 WlanFreeMemory_function_
= NULL
;
1221 WlanGetAvailableNetworkList_function_
= NULL
;
1222 WlanGetNetworkBssList_function_
= NULL
;
1223 WlanGetProfile_function_
= NULL
;
1224 WlanOpenHandle_function_
= NULL
;
1225 WlanRegisterNotification_function_
= NULL
;
1226 WlanSaveTemporaryProfile_function_
= NULL
;
1227 WlanScan_function_
= NULL
;
1228 WlanSetProfile_function_
= NULL
;
1229 ::FreeLibrary(wlan_api_library_
);
1230 wlan_api_library_
= NULL
;
1235 DOT11_SSID
WiFiServiceImpl::SSIDFromGUID(
1236 const std::string
& network_guid
) const {
1237 DOT11_SSID ssid
= {0};
1238 if (network_guid
.length() <= DOT11_SSID_MAX_LENGTH
) {
1239 ssid
.uSSIDLength
= static_cast<ULONG
>(network_guid
.length());
1240 strncpy(reinterpret_cast<char*>(ssid
.ucSSID
),
1241 network_guid
.c_str(),
1249 std::string
WiFiServiceImpl::SecurityFromDot11AuthAlg(
1250 DOT11_AUTH_ALGORITHM alg
) const {
1252 case DOT11_AUTH_ALGO_RSNA
:
1253 return onc::wifi::kWPA_EAP
;
1254 case DOT11_AUTH_ALGO_RSNA_PSK
:
1255 return onc::wifi::kWPA_PSK
;
1256 case DOT11_AUTH_ALGO_80211_SHARED_KEY
:
1257 return onc::wifi::kWEP_PSK
;
1258 case DOT11_AUTH_ALGO_80211_OPEN
:
1259 return onc::wifi::kNone
;
1261 return onc::wifi::kWPA_EAP
;
1265 std::string
WiFiServiceImpl::ConnectionStateFromInterfaceState(
1266 WLAN_INTERFACE_STATE wlan_state
) const {
1267 switch (wlan_state
) {
1268 case wlan_interface_state_connected
:
1269 // TODO(mef): Even if |wlan_state| is connected, the network may still
1270 // not be reachable, and should be resported as |kConnecting|.
1271 return onc::connection_state::kConnected
;
1272 case wlan_interface_state_associating
:
1273 case wlan_interface_state_discovering
:
1274 case wlan_interface_state_authenticating
:
1275 return onc::connection_state::kConnecting
;
1277 return onc::connection_state::kNotConnected
;
1281 void WiFiServiceImpl::NetworkPropertiesFromAvailableNetwork(
1282 const WLAN_AVAILABLE_NETWORK
& wlan
,
1283 NetworkProperties
* properties
) {
1284 // TODO(mef): It would be nice for the connection states in
1285 // getVisibleNetworks and getProperties results to be consistent.
1286 if (wlan
.dwFlags
& WLAN_AVAILABLE_NETWORK_CONNECTED
) {
1287 properties
->connection_state
= onc::connection_state::kConnected
;
1289 properties
->connection_state
= onc::connection_state::kNotConnected
;
1292 properties
->ssid
= SSIDFromWLAN(wlan
);
1293 properties
->name
= properties
->ssid
;
1294 properties
->guid
= GUIDFromWLAN(wlan
);
1295 properties
->type
= onc::network_type::kWiFi
;
1296 properties
->security
=
1297 SecurityFromDot11AuthAlg(wlan
.dot11DefaultAuthAlgorithm
);
1298 properties
->signal_strength
= wlan
.wlanSignalQuality
;
1301 void WiFiServiceImpl::UpdateNetworkPropertiesFromBssList(
1302 const std::string
& network_guid
,
1303 const WLAN_BSS_LIST
& wlan_bss_list
,
1304 NetworkProperties
* properties
) {
1305 if (network_guid
.empty())
1308 DOT11_SSID ssid
= SSIDFromGUID(network_guid
);
1309 for (size_t bss
= 0; bss
< wlan_bss_list
.dwNumberOfItems
; ++bss
) {
1310 const WLAN_BSS_ENTRY
& bss_entry(wlan_bss_list
.wlanBssEntries
[bss
]);
1311 if (bss_entry
.dot11Ssid
.uSSIDLength
== ssid
.uSSIDLength
&&
1312 0 == memcmp(bss_entry
.dot11Ssid
.ucSSID
,
1314 bss_entry
.dot11Ssid
.uSSIDLength
)) {
1315 std::string bssid
= NetworkProperties::MacAddressAsString(
1316 bss_entry
.dot11Bssid
);
1317 Frequency frequency
= GetNormalizedFrequency(
1318 bss_entry
.ulChCenterFrequency
/ 1000);
1319 properties
->frequency_set
.insert(frequency
);
1320 if (properties
->bssid
.empty() || properties
->bssid
== bssid
) {
1321 properties
->frequency
= frequency
;
1322 properties
->bssid
= bssid
;
1328 // Get the list of visible wireless networks
1329 DWORD
WiFiServiceImpl::GetVisibleNetworkList(NetworkList
* network_list
) {
1330 if (client_
== NULL
) {
1332 return ERROR_NOINTERFACE
;
1335 DWORD error
= ERROR_SUCCESS
;
1336 PWLAN_AVAILABLE_NETWORK_LIST available_network_list
= NULL
;
1337 PWLAN_BSS_LIST bss_list
= NULL
;
1339 error
= WlanGetAvailableNetworkList_function_(
1342 WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES
,
1344 &available_network_list
);
1346 std::set
<std::string
> network_guids
;
1348 if (error
== ERROR_SUCCESS
&&
1349 available_network_list
&&
1350 WlanGetNetworkBssList_function_
) {
1351 // TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is
1352 // needed, then different method of getting BSS (e.g. OID query) will have
1354 error
= WlanGetNetworkBssList_function_(client_
,
1361 if (error
== ERROR_SUCCESS
&& NULL
!= bss_list
) {
1362 for (DWORD i
= 0; i
< available_network_list
->dwNumberOfItems
; ++i
) {
1363 NetworkProperties network_properties
;
1364 NetworkPropertiesFromAvailableNetwork(
1365 available_network_list
->Network
[i
],
1366 &network_properties
);
1367 UpdateNetworkPropertiesFromBssList(network_properties
.guid
,
1369 &network_properties
);
1370 // Check for duplicate network guids.
1371 if (network_guids
.count(network_properties
.guid
)) {
1372 // There should be no difference between properties except for
1373 // |connection_state|, so mark it as |kConnected| if either one is.
1374 if (network_properties
.connection_state
==
1375 onc::connection_state::kConnected
) {
1376 NetworkList::iterator previous_network_properties
=
1377 FindNetwork(*network_list
, network_properties
.guid
);
1378 DCHECK(previous_network_properties
!= network_list
->end());
1379 previous_network_properties
->connection_state
=
1380 network_properties
.connection_state
;
1383 network_list
->push_back(network_properties
);
1385 network_guids
.insert(network_properties
.guid
);
1391 if (available_network_list
!= NULL
) {
1392 WlanFreeMemory_function_(available_network_list
);
1394 if (bss_list
!= NULL
) {
1395 WlanFreeMemory_function_(bss_list
);
1400 DWORD
WiFiServiceImpl::GetCurrentProperties(NetworkProperties
* properties
) {
1401 if (client_
== NULL
) {
1403 return ERROR_NOINTERFACE
;
1406 // TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is
1407 // needed, then different method of getting BSS (e.g. OID query) will have
1409 if (WlanGetNetworkBssList_function_
== NULL
)
1410 return ERROR_NOINTERFACE
;
1412 DWORD error
= ERROR_SUCCESS
;
1413 DWORD data_size
= 0;
1414 PWLAN_CONNECTION_ATTRIBUTES wlan_connection_attributes
= NULL
;
1415 PWLAN_BSS_LIST bss_list
= NULL
;
1416 error
= WlanQueryInterface_function_(
1419 wlan_intf_opcode_current_connection
,
1422 reinterpret_cast<PVOID
*>(&wlan_connection_attributes
),
1424 if (error
== ERROR_SUCCESS
&&
1425 wlan_connection_attributes
!= NULL
) {
1426 WLAN_ASSOCIATION_ATTRIBUTES
& connected_wlan
=
1427 wlan_connection_attributes
->wlanAssociationAttributes
;
1429 properties
->connection_state
= ConnectionStateFromInterfaceState(
1430 wlan_connection_attributes
->isState
);
1431 properties
->ssid
= GUIDFromSSID(connected_wlan
.dot11Ssid
);
1432 properties
->name
= properties
->ssid
;
1433 properties
->guid
= GUIDFromSSID(connected_wlan
.dot11Ssid
);
1434 properties
->type
= onc::network_type::kWiFi
;
1435 properties
->bssid
= NetworkProperties::MacAddressAsString(
1436 connected_wlan
.dot11Bssid
);
1437 properties
->security
= SecurityFromDot11AuthAlg(
1438 wlan_connection_attributes
->wlanSecurityAttributes
.dot11AuthAlgorithm
);
1439 properties
->signal_strength
= connected_wlan
.wlanSignalQuality
;
1441 error
= WlanGetNetworkBssList_function_(client_
,
1443 &connected_wlan
.dot11Ssid
,
1444 connected_wlan
.dot11BssType
,
1448 if (error
== ERROR_SUCCESS
&& NULL
!= bss_list
) {
1449 UpdateNetworkPropertiesFromBssList(properties
->guid
,
1456 if (wlan_connection_attributes
!= NULL
)
1457 WlanFreeMemory_function_(wlan_connection_attributes
);
1459 if (bss_list
!= NULL
)
1460 WlanFreeMemory_function_(bss_list
);
1465 Frequency
WiFiServiceImpl::GetFrequencyToConnect(
1466 const std::string
& network_guid
) const {
1467 // Check whether desired frequency is set in |connect_properties_|.
1468 const base::DictionaryValue
* properties
;
1469 const base::DictionaryValue
* wifi
;
1471 if (connect_properties_
.GetDictionaryWithoutPathExpansion(
1472 network_guid
, &properties
) &&
1473 properties
->GetDictionary(onc::network_type::kWiFi
, &wifi
) &&
1474 wifi
->GetInteger(onc::wifi::kFrequency
, &frequency
)) {
1475 return GetNormalizedFrequency(frequency
);
1477 return kFrequencyAny
;
1480 DWORD
WiFiServiceImpl::GetDesiredBssList(
1482 Frequency frequency
,
1483 scoped_ptr
<DOT11_BSSID_LIST
>* desired_list
) {
1484 if (client_
== NULL
) {
1486 return ERROR_NOINTERFACE
;
1489 desired_list
->reset();
1491 if (frequency
== kFrequencyAny
)
1492 return ERROR_SUCCESS
;
1494 // TODO(mef): WlanGetNetworkBssList is not available on XP. If XP support is
1495 // needed, then different method of getting BSS (e.g. OID query) will have
1497 if (!WlanGetNetworkBssList_function_
)
1498 return ERROR_NOT_SUPPORTED
;
1500 DWORD error
= ERROR_SUCCESS
;
1501 PWLAN_BSS_LIST bss_list
= NULL
;
1503 error
= WlanGetNetworkBssList_function_(client_
,
1506 dot11_BSS_type_infrastructure
,
1510 if (error
== ERROR_SUCCESS
&& NULL
!= bss_list
) {
1511 unsigned int best_quality
= 0u;
1512 size_t best_index
= 0;
1513 Frequency bss_frequency
;
1515 // Go through bss_list and find best quality BSSID with matching frequency.
1516 for (size_t bss
= 0; bss
< bss_list
->dwNumberOfItems
; ++bss
) {
1517 const WLAN_BSS_ENTRY
& bss_entry(bss_list
->wlanBssEntries
[bss
]);
1518 if (bss_entry
.dot11Ssid
.uSSIDLength
!= ssid
.uSSIDLength
||
1519 0 != memcmp(bss_entry
.dot11Ssid
.ucSSID
,
1521 bss_entry
.dot11Ssid
.uSSIDLength
))
1524 bss_frequency
= GetNormalizedFrequency(
1525 bss_entry
.ulChCenterFrequency
/ 1000);
1526 if (bss_frequency
== frequency
&&
1527 bss_entry
.uLinkQuality
> best_quality
) {
1528 best_quality
= bss_entry
.uLinkQuality
;
1533 // If any matching BSS were found, prepare the header.
1534 if (best_quality
> 0) {
1535 const WLAN_BSS_ENTRY
& bss_entry(bss_list
->wlanBssEntries
[best_index
]);
1536 scoped_ptr
<DOT11_BSSID_LIST
> selected_list(new DOT11_BSSID_LIST
);
1538 selected_list
->Header
.Revision
= DOT11_BSSID_LIST_REVISION_1
;
1539 selected_list
->Header
.Size
= sizeof(DOT11_BSSID_LIST
);
1540 selected_list
->Header
.Type
= NDIS_OBJECT_TYPE_DEFAULT
;
1541 selected_list
->uNumOfEntries
= 1;
1542 selected_list
->uTotalNumOfEntries
= 1;
1543 std::copy(bss_entry
.dot11Bssid
,
1544 bss_entry
.dot11Bssid
+sizeof(bss_entry
.dot11Bssid
),
1545 selected_list
->BSSIDs
[0]);
1546 desired_list
->swap(selected_list
);
1547 DVLOG(1) << "Quality: " << best_quality
<< " BSS: "
1548 << NetworkProperties::MacAddressAsString(bss_entry
.dot11Bssid
);
1550 error
= ERROR_NOT_FOUND
;
1555 if (bss_list
!= NULL
) {
1556 WlanFreeMemory_function_(bss_list
);
1561 Frequency
WiFiServiceImpl::GetNormalizedFrequency(int frequency_in_mhz
) const {
1562 if (frequency_in_mhz
== 0)
1563 return kFrequencyAny
;
1564 if (frequency_in_mhz
< 3000)
1565 return kFrequency2400
;
1566 return kFrequency5000
;
1569 DWORD
WiFiServiceImpl::Connect(const std::string
& network_guid
,
1570 Frequency frequency
) {
1571 if (client_
== NULL
) {
1573 return ERROR_NOINTERFACE
;
1576 DWORD error
= ERROR_SUCCESS
;
1577 DOT11_SSID ssid
= SSIDFromGUID(network_guid
);
1578 scoped_ptr
<DOT11_BSSID_LIST
> desired_bss_list
;
1579 error
= GetDesiredBssList(ssid
, frequency
, &desired_bss_list
);
1580 if (error
== ERROR_SUCCESS
) {
1581 if (HaveProfile(network_guid
)) {
1582 base::string16 profile_name
= ProfileNameFromGUID(network_guid
);
1583 WLAN_CONNECTION_PARAMETERS wlan_params
= {
1584 wlan_connection_mode_profile
,
1585 profile_name
.c_str(),
1587 desired_bss_list
.get(),
1590 error
= WlanConnect_function_(
1591 client_
, &interface_guid_
, &wlan_params
, NULL
);
1593 // If network is available, but is not open security, then it cannot be
1594 // connected without profile, so return 'access denied' error.
1595 scoped_ptr
<base::DictionaryValue
> properties (new base::DictionaryValue
);
1596 const base::DictionaryValue
* wifi
;
1597 std::string wifi_security
;
1598 std::string error_string
;
1599 GetProperties(network_guid
, properties
.get(), &error_string
);
1600 if (error_string
.empty() &&
1601 properties
->GetDictionary(onc::network_type::kWiFi
, &wifi
) &&
1602 wifi
->GetString(onc::wifi::kSecurity
, &wifi_security
) &&
1603 wifi_security
!= onc::wifi::kNone
) {
1604 error
= ERROR_ACCESS_DENIED
;
1605 LOG(ERROR
) << error
;
1608 WLAN_CONNECTION_PARAMETERS wlan_params
= {
1609 wlan_connection_mode_discovery_unsecure
,
1612 desired_bss_list
.get(),
1613 dot11_BSS_type_infrastructure
,
1615 error
= WlanConnect_function_(
1616 client_
, &interface_guid_
, &wlan_params
, NULL
);
1623 DWORD
WiFiServiceImpl::Disconnect() {
1624 if (client_
== NULL
) {
1626 return ERROR_NOINTERFACE
;
1629 DWORD error
= ERROR_SUCCESS
;
1630 error
= WlanDisconnect_function_(client_
, &interface_guid_
, NULL
);
1634 DWORD
WiFiServiceImpl::SaveTempProfile(const std::string
& network_guid
) {
1635 if (client_
== NULL
) {
1637 return ERROR_NOINTERFACE
;
1640 DWORD error
= ERROR_SUCCESS
;
1641 base::string16 profile_name
= ProfileNameFromGUID(network_guid
);
1642 // TODO(mef): WlanSaveTemporaryProfile is not available on XP. If XP support
1643 // is needed, then different method of saving network profile will have to be
1645 if (WlanSaveTemporaryProfile_function_
) {
1646 error
= WlanSaveTemporaryProfile_function_(client_
,
1648 profile_name
.c_str(),
1654 error
= ERROR_NOT_SUPPORTED
;
1659 DWORD
WiFiServiceImpl::GetProfile(const std::string
& network_guid
,
1660 bool get_plaintext_key
,
1661 std::string
* profile_xml
) {
1662 if (client_
== NULL
) {
1664 return ERROR_NOINTERFACE
;
1667 DWORD error
= ERROR_SUCCESS
;
1668 base::string16 profile_name
= ProfileNameFromGUID(network_guid
);
1669 DWORD flags
= get_plaintext_key
? WLAN_PROFILE_GET_PLAINTEXT_KEY
: 0;
1670 LPWSTR str_profile_xml
= NULL
;
1671 error
= WlanGetProfile_function_(client_
,
1673 profile_name
.c_str(),
1679 if (error
== ERROR_SUCCESS
&& str_profile_xml
!= NULL
) {
1680 *profile_xml
= base::UTF16ToUTF8(str_profile_xml
);
1683 if (str_profile_xml
!= NULL
) {
1684 WlanFreeMemory_function_(str_profile_xml
);
1690 DWORD
WiFiServiceImpl::SetProfile(bool shared
,
1691 const std::string
& profile_xml
,
1693 DWORD error_code
= ERROR_SUCCESS
;
1695 base::string16
profile_xml16(base::UTF8ToUTF16(profile_xml
));
1696 DWORD reason_code
= 0u;
1698 error_code
= WlanSetProfile_function_(client_
,
1700 shared
? 0 : WLAN_PROFILE_USER
,
1701 profile_xml16
.c_str(),
1709 bool WiFiServiceImpl::HaveProfile(const std::string
& network_guid
) {
1710 DWORD error
= ERROR_SUCCESS
;
1711 std::string profile_xml
;
1712 return GetProfile(network_guid
, false, &profile_xml
) == ERROR_SUCCESS
;
1716 DWORD
WiFiServiceImpl::DeleteCreatedProfile(const std::string
& network_guid
) {
1717 base::DictionaryValue
* created_profile
= NULL
;
1718 DWORD error_code
= ERROR_SUCCESS
;
1719 // Check, whether this connection is using new created profile, and remove it.
1720 if (created_profiles_
.GetDictionaryWithoutPathExpansion(
1721 network_guid
, &created_profile
)) {
1722 // Connection has failed, so delete it.
1723 base::string16 profile_name
= ProfileNameFromGUID(network_guid
);
1724 error_code
= WlanDeleteProfile_function_(client_
,
1726 profile_name
.c_str(),
1728 created_profiles_
.RemoveWithoutPathExpansion(network_guid
, NULL
);
1733 std::string
WiFiServiceImpl::WpaEncryptionFromEncryptionType(
1734 EncryptionType encryption_type
) const {
1735 if (encryption_type
== kEncryptionTypeTKIP
)
1736 return kEncryptionTKIP
;
1737 return kEncryptionAES
;
1740 bool WiFiServiceImpl::AuthEncryptionFromSecurity(
1741 const std::string
& security
,
1742 EncryptionType encryption_type
,
1743 std::string
* authentication
,
1744 std::string
* encryption
,
1745 std::string
* key_type
) const {
1746 if (security
== onc::wifi::kNone
) {
1747 *authentication
= kAuthenticationOpen
;
1748 *encryption
= kEncryptionNone
;
1749 } else if (security
== onc::wifi::kWEP_PSK
) {
1750 *authentication
= kAuthenticationOpen
;
1751 *encryption
= kEncryptionWEP
;
1752 *key_type
= kKeyTypeNetwork
;
1753 } else if (security
== onc::wifi::kWPA_PSK
) {
1754 *authentication
= kAuthenticationWpaPsk
;
1755 *encryption
= WpaEncryptionFromEncryptionType(encryption_type
);
1756 *key_type
= kKeyTypePassphrase
;
1757 } else if (security
== onc::wifi::kWPA2_PSK
) {
1758 *authentication
= kAuthenticationWpa2Psk
;
1759 *encryption
= WpaEncryptionFromEncryptionType(encryption_type
);
1760 *key_type
= kKeyTypePassphrase
;
1767 bool WiFiServiceImpl::CreateProfile(
1768 const NetworkProperties
& network_properties
,
1769 EncryptionType encryption_type
,
1770 std::string
* profile_xml
) {
1771 // Get authentication and encryption values from security.
1772 std::string authentication
;
1773 std::string encryption
;
1774 std::string key_type
;
1775 bool valid
= AuthEncryptionFromSecurity(network_properties
.security
,
1783 // Generate profile XML.
1784 XmlWriter xml_writer
;
1785 xml_writer
.StartWriting();
1786 xml_writer
.StartElement("WLANProfile");
1787 xml_writer
.AddAttribute(
1789 "http://www.microsoft.com/networking/WLAN/profile/v1");
1790 xml_writer
.WriteElement("name", network_properties
.guid
);
1791 xml_writer
.StartElement("SSIDConfig");
1792 xml_writer
.StartElement("SSID");
1793 xml_writer
.WriteElement("name", network_properties
.ssid
);
1794 xml_writer
.EndElement(); // Ends "SSID" element.
1795 xml_writer
.EndElement(); // Ends "SSIDConfig" element.
1796 xml_writer
.WriteElement("connectionType", "ESS");
1797 xml_writer
.WriteElement("connectionMode", "manual");
1798 xml_writer
.StartElement("MSM");
1799 xml_writer
.StartElement("security");
1800 xml_writer
.StartElement("authEncryption");
1801 xml_writer
.WriteElement("authentication", authentication
);
1802 xml_writer
.WriteElement("encryption", encryption
);
1803 xml_writer
.WriteElement("useOneX", "false");
1804 xml_writer
.EndElement(); // Ends "authEncryption" element.
1805 if (!key_type
.empty()) {
1806 xml_writer
.StartElement("sharedKey");
1807 xml_writer
.WriteElement("keyType", key_type
);
1808 xml_writer
.WriteElement("protected", "false");
1809 xml_writer
.WriteElement("keyMaterial", network_properties
.password
);
1810 xml_writer
.EndElement(); // Ends "sharedKey" element.
1812 xml_writer
.EndElement(); // Ends "security" element.
1813 xml_writer
.EndElement(); // Ends "MSM" element.
1814 xml_writer
.EndElement(); // Ends "WLANProfile" element.
1815 xml_writer
.StopWriting();
1816 *profile_xml
= xml_writer
.GetWrittenString();
1821 void WiFiServiceImpl::NotifyNetworkListChanged(const NetworkList
& networks
) {
1822 if (network_list_changed_observer_
.is_null())
1825 NetworkGuidList current_networks
;
1826 for (NetworkList::const_iterator it
= networks
.begin();
1827 it
!= networks
.end();
1829 current_networks
.push_back(it
->guid
);
1832 message_loop_proxy_
->PostTask(
1834 base::Bind(network_list_changed_observer_
, current_networks
));
1837 void WiFiServiceImpl::NotifyNetworkChanged(const std::string
& network_guid
) {
1838 if (enable_notify_network_changed_
&& !networks_changed_observer_
.is_null()) {
1839 DVLOG(1) << "NotifyNetworkChanged: " << network_guid
;
1840 NetworkGuidList
changed_networks(1, network_guid
);
1841 message_loop_proxy_
->PostTask(
1843 base::Bind(networks_changed_observer_
, changed_networks
));
1847 WiFiService
* WiFiService::Create() { return new WiFiServiceImpl(); }