1 // Copyright 2014 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 "chrome/browser/local_discovery/wifi/wifi_manager_nonchromeos.h"
7 #include "base/cancelable_callback.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "base/threading/sequenced_worker_pool.h"
10 #include "base/threading/thread.h"
11 #include "components/onc/onc_constants.h"
12 #include "components/wifi/wifi_service.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/network_change_notifier.h"
17 #include "chrome/browser/local_discovery/wifi/credential_getter_win.h"
20 using ::wifi::WiFiService
;
22 namespace local_discovery
{
28 const int kConnectionTimeoutSeconds
= 10;
30 scoped_ptr
<base::DictionaryValue
> MakeProperties(const std::string
& ssid
,
31 const std::string
& password
) {
32 scoped_ptr
<base::DictionaryValue
> properties(new base::DictionaryValue
);
34 properties
->SetString(onc::network_config::kType
, onc::network_type::kWiFi
);
35 base::DictionaryValue
* wifi
= new base::DictionaryValue
;
36 properties
->Set(onc::network_config::kWiFi
, wifi
);
38 wifi
->SetString(onc::wifi::kSSID
, ssid
);
39 if (!password
.empty()) {
40 wifi
->SetString(onc::wifi::kPassphrase
, password
);
41 // TODO(noamsml): Allow choosing security mechanism in a more fine-grained
43 wifi
->SetString(onc::wifi::kSecurity
, onc::wifi::kWPA2_PSK
);
45 wifi
->SetString(onc::wifi::kSecurity
, onc::wifi::kSecurityNone
);
48 return properties
.Pass();
53 class WifiManagerNonChromeos::WifiServiceWrapper
54 : public net::NetworkChangeNotifier::NetworkChangeObserver
{
56 explicit WifiServiceWrapper(
57 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager
);
59 ~WifiServiceWrapper() override
;
63 void GetSSIDList(const WifiManager::SSIDListCallback
& callback
);
65 void ConfigureAndConnectPskNetwork(
66 const std::string
& ssid
,
67 const std::string
& password
,
68 const WifiManager::SuccessCallback
& callback
);
70 base::WeakPtr
<WifiManagerNonChromeos::WifiServiceWrapper
> AsWeakPtr();
74 void ConnectToNetworkByID(const std::string
& network_guid
,
75 const WifiManager::SuccessCallback
& callback
);
77 void RequestNetworkCredentials(
78 const std::string
& ssid
,
79 const WifiManager::CredentialsCallback
& callback
);
82 // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
83 void OnNetworkChanged(
84 net::NetworkChangeNotifier::ConnectionType type
) override
;
86 void GetSSIDListInternal(NetworkPropertiesList
* ssid_list
);
88 void OnNetworkListChangedEvent(const std::vector
<std::string
>& network_guids
);
90 void OnNetworksChangedEvent(const std::vector
<std::string
>& network_guids
);
92 std::string
GetConnectedGUID();
94 bool IsConnected(const std::string
& network_guid
);
96 void OnConnectToNetworkTimeout();
98 void PostClosure(const base::Closure
& closure
);
100 bool FindAndConfigureNetwork(const std::string
& ssid
,
101 const std::string
& password
,
102 std::string
* network_guid
);
105 void PostCredentialsCallback(const WifiManager::CredentialsCallback
& callback
,
106 const std::string
& ssid
,
108 const std::string
& password
);
111 scoped_ptr
<WiFiService
> wifi_service_
;
113 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager_
;
115 WifiManager::SuccessCallback connect_success_callback_
;
116 base::CancelableClosure connect_failure_callback_
;
118 // SSID of previously connected network.
119 std::string connected_network_guid_
;
121 // SSID of network we are connecting to.
122 std::string connecting_network_guid_
;
124 scoped_refptr
<base::SingleThreadTaskRunner
> callback_runner_
;
127 scoped_refptr
<CredentialGetterWin
> credential_getter_
;
130 base::WeakPtrFactory
<WifiServiceWrapper
> weak_factory_
;
132 DISALLOW_COPY_AND_ASSIGN(WifiServiceWrapper
);
135 WifiManagerNonChromeos::WifiServiceWrapper::WifiServiceWrapper(
136 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager
)
137 : wifi_manager_(wifi_manager
),
138 callback_runner_(base::ThreadTaskRunnerHandle::Get()),
139 weak_factory_(this) {
142 WifiManagerNonChromeos::WifiServiceWrapper::~WifiServiceWrapper() {
143 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
146 void WifiManagerNonChromeos::WifiServiceWrapper::Start() {
147 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
148 wifi_service_
.reset(WiFiService::Create());
150 wifi_service_
->Initialize(base::ThreadTaskRunnerHandle::Get());
152 wifi_service_
->SetEventObservers(
153 base::ThreadTaskRunnerHandle::Get(),
154 base::Bind(&WifiServiceWrapper::OnNetworksChangedEvent
, AsWeakPtr()),
155 base::Bind(&WifiServiceWrapper::OnNetworkListChangedEvent
, AsWeakPtr()));
157 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
160 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDList(
161 const WifiManager::SSIDListCallback
& callback
) {
162 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
164 scoped_ptr
<NetworkPropertiesList
> ssid_list(new NetworkPropertiesList
);
165 GetSSIDListInternal(ssid_list
.get());
167 callback_runner_
->PostTask(
169 base::Bind(&WifiManagerNonChromeos::PostSSIDListCallback
,
172 base::Passed(&ssid_list
)));
175 void WifiManagerNonChromeos::WifiServiceWrapper::ConfigureAndConnectPskNetwork(
176 const std::string
& ssid
,
177 const std::string
& password
,
178 const WifiManager::SuccessCallback
& callback
) {
179 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
180 scoped_ptr
<base::DictionaryValue
> properties
= MakeProperties(ssid
, password
);
182 std::string network_guid
;
183 std::string error_string
;
184 // Will fail without changing system state if network already exists.
185 wifi_service_
->CreateNetwork(
186 false, properties
.Pass(), &network_guid
, &error_string
);
188 if (error_string
.empty()) {
189 ConnectToNetworkByID(network_guid
, callback
);
193 // If we cannot create the network, attempt to configure and connect to an
195 if (FindAndConfigureNetwork(ssid
, password
, &network_guid
)) {
196 ConnectToNetworkByID(network_guid
, callback
);
198 if (!callback
.is_null())
199 PostClosure(base::Bind(callback
, false /* success */));
203 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkListChangedEvent(
204 const std::vector
<std::string
>& network_guids
) {
205 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
206 scoped_ptr
<NetworkPropertiesList
> ssid_list(new NetworkPropertiesList
);
207 GetSSIDListInternal(ssid_list
.get());
208 callback_runner_
->PostTask(
210 base::Bind(&WifiManagerNonChromeos::OnNetworkListChanged
,
212 base::Passed(&ssid_list
)));
215 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworksChangedEvent(
216 const std::vector
<std::string
>& network_guids
) {
217 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
218 if (connecting_network_guid_
.empty() ||
219 !IsConnected(connecting_network_guid_
)) {
223 connecting_network_guid_
.clear();
224 connect_failure_callback_
.Cancel();
226 if (!connect_success_callback_
.is_null())
227 PostClosure(base::Bind(connect_success_callback_
, true));
229 connect_success_callback_
.Reset();
232 base::WeakPtr
<WifiManagerNonChromeos::WifiServiceWrapper
>
233 WifiManagerNonChromeos::WifiServiceWrapper::AsWeakPtr() {
234 return weak_factory_
.GetWeakPtr();
237 void WifiManagerNonChromeos::WifiServiceWrapper::RequestScan() {
238 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
239 wifi_service_
->RequestNetworkScan();
242 void WifiManagerNonChromeos::WifiServiceWrapper::ConnectToNetworkByID(
243 const std::string
& network_guid
,
244 const WifiManager::SuccessCallback
& callback
) {
245 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
247 std::string connected_network_id
= GetConnectedGUID();
248 std::string error_string
;
249 wifi_service_
->StartConnect(network_guid
, &error_string
);
251 if (!error_string
.empty()) {
252 LOG(ERROR
) << "Could not connect to network by ID: " << error_string
;
253 PostClosure(base::Bind(callback
, false /* success */));
254 wifi_service_
->StartConnect(connected_network_id
, &error_string
);
258 if (IsConnected(network_guid
)) {
259 if (!callback
.is_null())
260 PostClosure(base::Bind(callback
, true /* success */));
264 connect_success_callback_
= callback
;
265 connecting_network_guid_
= network_guid
;
266 connected_network_guid_
= connected_network_id
;
268 connect_failure_callback_
.Reset(base::Bind(
269 &WifiServiceWrapper::OnConnectToNetworkTimeout
, base::Unretained(this)));
271 base::MessageLoop::current()->PostDelayedTask(
273 connect_failure_callback_
.callback(),
274 base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds
));
277 void WifiManagerNonChromeos::WifiServiceWrapper::OnConnectToNetworkTimeout() {
278 bool connected
= IsConnected(connecting_network_guid_
);
279 std::string error_string
;
281 WifiManager::SuccessCallback connect_success_callback
=
282 connect_success_callback_
;
284 connect_success_callback_
.Reset();
286 connecting_network_guid_
.clear();
288 // If we did not connect, return to the network the user was originally
291 wifi_service_
->StartConnect(connected_network_guid_
, &error_string
);
293 PostClosure(base::Bind(connect_success_callback
, connected
/* success */));
296 void WifiManagerNonChromeos::WifiServiceWrapper::RequestNetworkCredentials(
297 const std::string
& ssid
,
298 const WifiManager::CredentialsCallback
& callback
) {
299 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
305 NetworkPropertiesList network_list
;
307 GetSSIDListInternal(&network_list
);
309 for (NetworkPropertiesList::iterator i
= network_list
.begin();
310 i
!= network_list
.end();
312 if (i
->ssid
== ssid
) {
323 PostClosure(base::Bind(callback
, success
, "", ""));
328 credential_getter_
= new CredentialGetterWin();
329 credential_getter_
->StartGetCredentials(
331 base::Bind(&WifiServiceWrapper::PostCredentialsCallback
,
337 std::string error_string
;
338 wifi_service_
->GetKeyFromSystem(guid
, &key
, &error_string
);
340 if (!error_string
.empty()) {
341 LOG(ERROR
) << "Could not get key from system: " << error_string
;
345 PostClosure(base::Bind(callback
, success
, ssid
, key
));
350 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged(
351 net::NetworkChangeNotifier::ConnectionType type
) {
352 wifi_service_
->RequestConnectedNetworkUpdate();
355 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal(
356 NetworkPropertiesList
* ssid_list
) {
357 base::ListValue visible_networks
;
359 wifi_service_
->GetVisibleNetworks(
360 onc::network_type::kWiFi
, &visible_networks
, true);
362 for (size_t i
= 0; i
< visible_networks
.GetSize(); i
++) {
363 const base::DictionaryValue
* network_value
= NULL
;
364 NetworkProperties network_properties
;
366 if (!visible_networks
.GetDictionary(i
, &network_value
)) {
370 network_properties
.UpdateFromValue(*network_value
);
372 ssid_list
->push_back(network_properties
);
376 std::string
WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() {
377 NetworkPropertiesList ssid_list
;
378 GetSSIDListInternal(&ssid_list
);
380 for (NetworkPropertiesList::const_iterator it
= ssid_list
.begin();
381 it
!= ssid_list
.end();
383 if (it
->connection_state
== onc::connection_state::kConnected
)
390 bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected(
391 const std::string
& network_guid
) {
392 NetworkPropertiesList ssid_list
;
393 GetSSIDListInternal(&ssid_list
);
395 for (NetworkPropertiesList::const_iterator it
= ssid_list
.begin();
396 it
!= ssid_list
.end();
398 if (it
->connection_state
== onc::connection_state::kConnected
&&
399 it
->guid
== network_guid
)
406 bool WifiManagerNonChromeos::WifiServiceWrapper::FindAndConfigureNetwork(
407 const std::string
& ssid
,
408 const std::string
& password
,
409 std::string
* network_guid
) {
410 std::string provisional_network_guid
;
411 NetworkPropertiesList network_property_list
;
412 GetSSIDListInternal(&network_property_list
);
414 for (size_t i
= 0; i
< network_property_list
.size(); i
++) {
415 // TODO(noamsml): Handle case where there are multiple networks with the
416 // same SSID but different security.
417 if (network_property_list
[i
].ssid
== ssid
) {
418 provisional_network_guid
= network_property_list
[i
].guid
;
423 if (provisional_network_guid
.empty())
426 scoped_ptr
<base::DictionaryValue
> properties
= MakeProperties(ssid
, password
);
428 std::string error_string
;
429 wifi_service_
->SetProperties(
430 provisional_network_guid
, properties
.Pass(), &error_string
);
432 if (!error_string
.empty()) {
433 LOG(ERROR
) << "Could not set properties on network: " << error_string
;
437 *network_guid
= provisional_network_guid
;
441 void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure(
442 const base::Closure
& closure
) {
443 callback_runner_
->PostTask(
445 base::Bind(&WifiManagerNonChromeos::PostClosure
, wifi_manager_
, closure
));
449 void WifiManagerNonChromeos::WifiServiceWrapper::PostCredentialsCallback(
450 const WifiManager::CredentialsCallback
& callback
,
451 const std::string
& ssid
,
453 const std::string
& password
) {
454 PostClosure(base::Bind(callback
, success
, ssid
, password
));
459 scoped_ptr
<WifiManager
> WifiManager::CreateDefault() {
460 return scoped_ptr
<WifiManager
>(new WifiManagerNonChromeos());
463 WifiManagerNonChromeos::WifiManagerNonChromeos()
464 : wifi_wrapper_(NULL
), weak_factory_(this) {
467 WifiManagerNonChromeos::~WifiManagerNonChromeos() {
469 content::BrowserThread::DeleteSoon(
470 content::BrowserThread::FILE, FROM_HERE
, wifi_wrapper_
);
474 void WifiManagerNonChromeos::Start() {
475 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
476 task_runner_
= content::BrowserThread::GetMessageLoopProxyForThread(
477 content::BrowserThread::FILE);
479 // Allocated on UI thread, but all initialization is done on file
480 // thread. Destroyed on file thread, which should be safe since all of the
481 // thread-unsafe members are created on the file thread.
482 wifi_wrapper_
= new WifiServiceWrapper(weak_factory_
.GetWeakPtr());
484 task_runner_
->PostTask(
486 base::Bind(&WifiServiceWrapper::Start
, wifi_wrapper_
->AsWeakPtr()));
489 void WifiManagerNonChromeos::GetSSIDList(const SSIDListCallback
& callback
) {
490 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
491 task_runner_
->PostTask(FROM_HERE
,
492 base::Bind(&WifiServiceWrapper::GetSSIDList
,
493 wifi_wrapper_
->AsWeakPtr(),
497 void WifiManagerNonChromeos::RequestScan() {
498 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
499 task_runner_
->PostTask(
501 base::Bind(&WifiServiceWrapper::RequestScan
, wifi_wrapper_
->AsWeakPtr()));
504 void WifiManagerNonChromeos::OnNetworkListChanged(
505 scoped_ptr
<NetworkPropertiesList
> ssid_list
) {
506 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
507 FOR_EACH_OBSERVER(NetworkListObserver
,
508 network_list_observers_
,
509 OnNetworkListChanged(*ssid_list
));
512 void WifiManagerNonChromeos::PostClosure(const base::Closure
& callback
) {
513 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
517 void WifiManagerNonChromeos::PostSSIDListCallback(
518 const SSIDListCallback
& callback
,
519 scoped_ptr
<NetworkPropertiesList
> ssid_list
) {
520 callback
.Run(*ssid_list
);
523 void WifiManagerNonChromeos::ConfigureAndConnectNetwork(
524 const std::string
& ssid
,
525 const WifiCredentials
& credentials
,
526 const SuccessCallback
& callback
) {
527 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
528 task_runner_
->PostTask(
530 base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork
,
531 wifi_wrapper_
->AsWeakPtr(),
537 void WifiManagerNonChromeos::ConnectToNetworkByID(
538 const std::string
& internal_id
,
539 const SuccessCallback
& callback
) {
540 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
541 task_runner_
->PostTask(FROM_HERE
,
542 base::Bind(&WifiServiceWrapper::ConnectToNetworkByID
,
543 wifi_wrapper_
->AsWeakPtr(),
548 void WifiManagerNonChromeos::RequestNetworkCredentials(
549 const std::string
& ssid
,
550 const CredentialsCallback
& callback
) {
551 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
552 task_runner_
->PostTask(
554 base::Bind(&WifiServiceWrapper::RequestNetworkCredentials
,
555 wifi_wrapper_
->AsWeakPtr(),
560 void WifiManagerNonChromeos::AddNetworkListObserver(
561 NetworkListObserver
* observer
) {
562 network_list_observers_
.AddObserver(observer
);
565 void WifiManagerNonChromeos::RemoveNetworkListObserver(
566 NetworkListObserver
* observer
) {
567 network_list_observers_
.RemoveObserver(observer
);
572 } // namespace local_discovery