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/threading/sequenced_worker_pool.h"
9 #include "base/threading/thread.h"
10 #include "components/onc/onc_constants.h"
11 #include "components/wifi/wifi_service.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "net/base/network_change_notifier.h"
16 #include "chrome/browser/local_discovery/wifi/credential_getter_win.h"
19 using ::wifi::WiFiService
;
21 namespace local_discovery
{
27 const int kConnectionTimeoutSeconds
= 10;
29 scoped_ptr
<base::DictionaryValue
> MakeProperties(const std::string
& ssid
,
30 const std::string
& password
) {
31 scoped_ptr
<base::DictionaryValue
> properties(new base::DictionaryValue
);
33 properties
->SetString(onc::network_config::kType
, onc::network_type::kWiFi
);
34 base::DictionaryValue
* wifi
= new base::DictionaryValue
;
35 properties
->Set(onc::network_config::kWiFi
, wifi
);
37 wifi
->SetString(onc::wifi::kSSID
, ssid
);
38 if (!password
.empty()) {
39 wifi
->SetString(onc::wifi::kPassphrase
, password
);
40 // TODO(noamsml): Allow choosing security mechanism in a more fine-grained
42 wifi
->SetString(onc::wifi::kSecurity
, onc::wifi::kWPA2_PSK
);
44 wifi
->SetString(onc::wifi::kSecurity
, onc::wifi::kSecurityNone
);
47 return properties
.Pass();
52 class WifiManagerNonChromeos::WifiServiceWrapper
53 : public net::NetworkChangeNotifier::NetworkChangeObserver
{
55 explicit WifiServiceWrapper(
56 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager
);
58 virtual ~WifiServiceWrapper();
62 void GetSSIDList(const WifiManager::SSIDListCallback
& callback
);
64 void ConfigureAndConnectPskNetwork(
65 const std::string
& ssid
,
66 const std::string
& password
,
67 const WifiManager::SuccessCallback
& callback
);
69 base::WeakPtr
<WifiManagerNonChromeos::WifiServiceWrapper
> AsWeakPtr();
73 void ConnectToNetworkByID(const std::string
& network_guid
,
74 const WifiManager::SuccessCallback
& callback
);
76 void RequestNetworkCredentials(
77 const std::string
& ssid
,
78 const WifiManager::CredentialsCallback
& callback
);
81 // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
82 virtual void OnNetworkChanged(
83 net::NetworkChangeNotifier::ConnectionType type
) OVERRIDE
;
85 void GetSSIDListInternal(NetworkPropertiesList
* ssid_list
);
87 void OnNetworkListChangedEvent(const std::vector
<std::string
>& network_guids
);
89 void OnNetworksChangedEvent(const std::vector
<std::string
>& network_guids
);
91 std::string
GetConnectedGUID();
93 bool IsConnected(const std::string
& network_guid
);
95 void OnConnectToNetworkTimeout();
97 void PostClosure(const base::Closure
& closure
);
99 bool FindAndConfigureNetwork(const std::string
& ssid
,
100 const std::string
& password
,
101 std::string
* network_guid
);
104 void PostCredentialsCallback(const WifiManager::CredentialsCallback
& callback
,
105 const std::string
& ssid
,
107 const std::string
& password
);
110 scoped_ptr
<WiFiService
> wifi_service_
;
112 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager_
;
114 WifiManager::SuccessCallback connect_success_callback_
;
115 base::CancelableClosure connect_failure_callback_
;
117 // SSID of previously connected network.
118 std::string connected_network_guid_
;
120 // SSID of network we are connecting to.
121 std::string connecting_network_guid_
;
123 scoped_refptr
<base::MessageLoopProxy
> callback_runner_
;
125 base::WeakPtrFactory
<WifiServiceWrapper
> weak_factory_
;
128 scoped_refptr
<CredentialGetterWin
> credential_getter_
;
131 DISALLOW_COPY_AND_ASSIGN(WifiServiceWrapper
);
134 WifiManagerNonChromeos::WifiServiceWrapper::WifiServiceWrapper(
135 base::WeakPtr
<WifiManagerNonChromeos
> wifi_manager
)
136 : wifi_manager_(wifi_manager
),
137 callback_runner_(base::MessageLoopProxy::current()),
138 weak_factory_(this) {
141 WifiManagerNonChromeos::WifiServiceWrapper::~WifiServiceWrapper() {
142 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
145 void WifiManagerNonChromeos::WifiServiceWrapper::Start() {
146 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
147 wifi_service_
.reset(WiFiService::Create());
149 wifi_service_
->Initialize(base::MessageLoopProxy::current());
151 wifi_service_
->SetEventObservers(
152 base::MessageLoopProxy::current(),
153 base::Bind(&WifiServiceWrapper::OnNetworksChangedEvent
, AsWeakPtr()),
154 base::Bind(&WifiServiceWrapper::OnNetworkListChangedEvent
, AsWeakPtr()));
156 net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
159 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDList(
160 const WifiManager::SSIDListCallback
& callback
) {
161 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
163 scoped_ptr
<NetworkPropertiesList
> ssid_list(new NetworkPropertiesList
);
164 GetSSIDListInternal(ssid_list
.get());
166 callback_runner_
->PostTask(
168 base::Bind(&WifiManagerNonChromeos::PostSSIDListCallback
,
171 base::Passed(&ssid_list
)));
174 void WifiManagerNonChromeos::WifiServiceWrapper::ConfigureAndConnectPskNetwork(
175 const std::string
& ssid
,
176 const std::string
& password
,
177 const WifiManager::SuccessCallback
& callback
) {
178 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
179 scoped_ptr
<base::DictionaryValue
> properties
= MakeProperties(ssid
, password
);
181 std::string network_guid
;
182 std::string error_string
;
183 // Will fail without changing system state if network already exists.
184 wifi_service_
->CreateNetwork(
185 false, properties
.Pass(), &network_guid
, &error_string
);
187 if (error_string
.empty()) {
188 ConnectToNetworkByID(network_guid
, callback
);
192 // If we cannot create the network, attempt to configure and connect to an
194 if (FindAndConfigureNetwork(ssid
, password
, &network_guid
)) {
195 ConnectToNetworkByID(network_guid
, callback
);
197 if (!callback
.is_null())
198 PostClosure(base::Bind(callback
, false /* success */));
202 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkListChangedEvent(
203 const std::vector
<std::string
>& network_guids
) {
204 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
205 scoped_ptr
<NetworkPropertiesList
> ssid_list(new NetworkPropertiesList
);
206 GetSSIDListInternal(ssid_list
.get());
207 callback_runner_
->PostTask(
209 base::Bind(&WifiManagerNonChromeos::OnNetworkListChanged
,
211 base::Passed(&ssid_list
)));
214 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworksChangedEvent(
215 const std::vector
<std::string
>& network_guids
) {
216 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
217 if (connecting_network_guid_
.empty() ||
218 !IsConnected(connecting_network_guid_
)) {
222 connecting_network_guid_
.clear();
223 connect_failure_callback_
.Cancel();
225 if (!connect_success_callback_
.is_null())
226 PostClosure(base::Bind(connect_success_callback_
, true));
228 connect_success_callback_
.Reset();
231 base::WeakPtr
<WifiManagerNonChromeos::WifiServiceWrapper
>
232 WifiManagerNonChromeos::WifiServiceWrapper::AsWeakPtr() {
233 return weak_factory_
.GetWeakPtr();
236 void WifiManagerNonChromeos::WifiServiceWrapper::RequestScan() {
237 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
238 wifi_service_
->RequestNetworkScan();
241 void WifiManagerNonChromeos::WifiServiceWrapper::ConnectToNetworkByID(
242 const std::string
& network_guid
,
243 const WifiManager::SuccessCallback
& callback
) {
244 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
246 std::string connected_network_id
= GetConnectedGUID();
247 std::string error_string
;
248 wifi_service_
->StartConnect(network_guid
, &error_string
);
250 if (!error_string
.empty()) {
251 LOG(ERROR
) << "Could not connect to network by ID: " << error_string
;
252 PostClosure(base::Bind(callback
, false /* success */));
253 wifi_service_
->StartConnect(connected_network_id
, &error_string
);
257 if (IsConnected(network_guid
)) {
258 if (!callback
.is_null())
259 PostClosure(base::Bind(callback
, true /* success */));
263 connect_success_callback_
= callback
;
264 connecting_network_guid_
= network_guid
;
265 connected_network_guid_
= connected_network_id
;
267 connect_failure_callback_
.Reset(base::Bind(
268 &WifiServiceWrapper::OnConnectToNetworkTimeout
, base::Unretained(this)));
270 base::MessageLoop::current()->PostDelayedTask(
272 connect_failure_callback_
.callback(),
273 base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds
));
276 void WifiManagerNonChromeos::WifiServiceWrapper::OnConnectToNetworkTimeout() {
277 bool connected
= IsConnected(connecting_network_guid_
);
278 std::string error_string
;
280 WifiManager::SuccessCallback connect_success_callback
=
281 connect_success_callback_
;
283 connect_success_callback_
.Reset();
285 connecting_network_guid_
.clear();
287 // If we did not connect, return to the network the user was originally
290 wifi_service_
->StartConnect(connected_network_guid_
, &error_string
);
292 PostClosure(base::Bind(connect_success_callback
, connected
/* success */));
295 void WifiManagerNonChromeos::WifiServiceWrapper::RequestNetworkCredentials(
296 const std::string
& ssid
,
297 const WifiManager::CredentialsCallback
& callback
) {
298 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
304 NetworkPropertiesList network_list
;
306 GetSSIDListInternal(&network_list
);
308 for (NetworkPropertiesList::iterator i
= network_list
.begin();
309 i
!= network_list
.end();
311 if (i
->ssid
== ssid
) {
322 PostClosure(base::Bind(callback
, success
, "", ""));
327 credential_getter_
= new CredentialGetterWin();
328 credential_getter_
->StartGetCredentials(
330 base::Bind(&WifiServiceWrapper::PostCredentialsCallback
,
336 std::string error_string
;
337 wifi_service_
->GetKeyFromSystem(guid
, &key
, &error_string
);
339 if (!error_string
.empty()) {
340 LOG(ERROR
) << "Could not get key from system: " << error_string
;
344 PostClosure(base::Bind(callback
, success
, ssid
, key
));
349 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged(
350 net::NetworkChangeNotifier::ConnectionType type
) {
351 wifi_service_
->RequestConnectedNetworkUpdate();
354 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal(
355 NetworkPropertiesList
* ssid_list
) {
356 base::ListValue visible_networks
;
358 wifi_service_
->GetVisibleNetworks(
359 onc::network_type::kWiFi
, &visible_networks
, true);
361 for (size_t i
= 0; i
< visible_networks
.GetSize(); i
++) {
362 const base::DictionaryValue
* network_value
= NULL
;
363 NetworkProperties network_properties
;
365 if (!visible_networks
.GetDictionary(i
, &network_value
)) {
369 network_properties
.UpdateFromValue(*network_value
);
371 ssid_list
->push_back(network_properties
);
375 std::string
WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() {
376 NetworkPropertiesList ssid_list
;
377 GetSSIDListInternal(&ssid_list
);
379 for (NetworkPropertiesList::const_iterator it
= ssid_list
.begin();
380 it
!= ssid_list
.end();
382 if (it
->connection_state
== onc::connection_state::kConnected
)
389 bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected(
390 const std::string
& network_guid
) {
391 NetworkPropertiesList ssid_list
;
392 GetSSIDListInternal(&ssid_list
);
394 for (NetworkPropertiesList::const_iterator it
= ssid_list
.begin();
395 it
!= ssid_list
.end();
397 if (it
->connection_state
== onc::connection_state::kConnected
&&
398 it
->guid
== network_guid
)
405 bool WifiManagerNonChromeos::WifiServiceWrapper::FindAndConfigureNetwork(
406 const std::string
& ssid
,
407 const std::string
& password
,
408 std::string
* network_guid
) {
409 std::string provisional_network_guid
;
410 NetworkPropertiesList network_property_list
;
411 GetSSIDListInternal(&network_property_list
);
413 for (size_t i
= 0; i
< network_property_list
.size(); i
++) {
414 // TODO(noamsml): Handle case where there are multiple networks with the
415 // same SSID but different security.
416 if (network_property_list
[i
].ssid
== ssid
) {
417 provisional_network_guid
= network_property_list
[i
].guid
;
422 if (provisional_network_guid
.empty())
425 scoped_ptr
<base::DictionaryValue
> properties
= MakeProperties(ssid
, password
);
427 std::string error_string
;
428 wifi_service_
->SetProperties(
429 provisional_network_guid
, properties
.Pass(), &error_string
);
431 if (!error_string
.empty()) {
432 LOG(ERROR
) << "Could not set properties on network: " << error_string
;
436 *network_guid
= provisional_network_guid
;
440 void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure(
441 const base::Closure
& closure
) {
442 callback_runner_
->PostTask(
444 base::Bind(&WifiManagerNonChromeos::PostClosure
, wifi_manager_
, closure
));
448 void WifiManagerNonChromeos::WifiServiceWrapper::PostCredentialsCallback(
449 const WifiManager::CredentialsCallback
& callback
,
450 const std::string
& ssid
,
452 const std::string
& password
) {
453 PostClosure(base::Bind(callback
, success
, ssid
, password
));
458 scoped_ptr
<WifiManager
> WifiManager::CreateDefault() {
459 return scoped_ptr
<WifiManager
>(new WifiManagerNonChromeos());
462 WifiManagerNonChromeos::WifiManagerNonChromeos()
463 : wifi_wrapper_(NULL
), weak_factory_(this) {
466 WifiManagerNonChromeos::~WifiManagerNonChromeos() {
468 content::BrowserThread::DeleteSoon(
469 content::BrowserThread::FILE, FROM_HERE
, wifi_wrapper_
);
473 void WifiManagerNonChromeos::Start() {
474 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
475 task_runner_
= content::BrowserThread::GetMessageLoopProxyForThread(
476 content::BrowserThread::FILE);
478 // Allocated on UI thread, but all initialization is done on file
479 // thread. Destroyed on file thread, which should be safe since all of the
480 // thread-unsafe members are created on the file thread.
481 wifi_wrapper_
= new WifiServiceWrapper(weak_factory_
.GetWeakPtr());
483 task_runner_
->PostTask(
485 base::Bind(&WifiServiceWrapper::Start
, wifi_wrapper_
->AsWeakPtr()));
488 void WifiManagerNonChromeos::GetSSIDList(const SSIDListCallback
& callback
) {
489 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
490 task_runner_
->PostTask(FROM_HERE
,
491 base::Bind(&WifiServiceWrapper::GetSSIDList
,
492 wifi_wrapper_
->AsWeakPtr(),
496 void WifiManagerNonChromeos::RequestScan() {
497 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
498 task_runner_
->PostTask(
500 base::Bind(&WifiServiceWrapper::RequestScan
, wifi_wrapper_
->AsWeakPtr()));
503 void WifiManagerNonChromeos::OnNetworkListChanged(
504 scoped_ptr
<NetworkPropertiesList
> ssid_list
) {
505 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
506 FOR_EACH_OBSERVER(NetworkListObserver
,
507 network_list_observers_
,
508 OnNetworkListChanged(*ssid_list
));
511 void WifiManagerNonChromeos::PostClosure(const base::Closure
& callback
) {
512 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
516 void WifiManagerNonChromeos::PostSSIDListCallback(
517 const SSIDListCallback
& callback
,
518 scoped_ptr
<NetworkPropertiesList
> ssid_list
) {
519 callback
.Run(*ssid_list
);
522 void WifiManagerNonChromeos::ConfigureAndConnectNetwork(
523 const std::string
& ssid
,
524 const WifiCredentials
& credentials
,
525 const SuccessCallback
& callback
) {
526 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
527 task_runner_
->PostTask(
529 base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork
,
530 wifi_wrapper_
->AsWeakPtr(),
536 void WifiManagerNonChromeos::ConnectToNetworkByID(
537 const std::string
& internal_id
,
538 const SuccessCallback
& callback
) {
539 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
540 task_runner_
->PostTask(FROM_HERE
,
541 base::Bind(&WifiServiceWrapper::ConnectToNetworkByID
,
542 wifi_wrapper_
->AsWeakPtr(),
547 void WifiManagerNonChromeos::RequestNetworkCredentials(
548 const std::string
& ssid
,
549 const CredentialsCallback
& callback
) {
550 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
551 task_runner_
->PostTask(
553 base::Bind(&WifiServiceWrapper::RequestNetworkCredentials
,
554 wifi_wrapper_
->AsWeakPtr(),
559 void WifiManagerNonChromeos::AddNetworkListObserver(
560 NetworkListObserver
* observer
) {
561 network_list_observers_
.AddObserver(observer
);
564 void WifiManagerNonChromeos::RemoveNetworkListObserver(
565 NetworkListObserver
* observer
) {
566 network_list_observers_
.RemoveObserver(observer
);
571 } // namespace local_discovery