wifi_manager_nonchromeos.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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.
4
5#include "chrome/browser/local_discovery/wifi/wifi_manager_nonchromeos.h"
6
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"
14
15using ::wifi::WiFiService;
16
17namespace local_discovery {
18
19namespace wifi {
20
21namespace {
22
23const int kConnectionTimeoutSeconds = 10;
24
25scoped_ptr<base::DictionaryValue> MakeProperties(const std::string& ssid,
26                                                 const std::string& password) {
27  scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue);
28
29  properties->SetString(onc::network_config::kType, onc::network_type::kWiFi);
30  base::DictionaryValue* wifi = new base::DictionaryValue;
31  properties->Set(onc::network_config::kWiFi, wifi);
32
33  wifi->SetString(onc::wifi::kSSID, ssid);
34  if (!password.empty()) {
35    wifi->SetString(onc::wifi::kPassphrase, password);
36    // TODO(noamsml): Allow choosing security mechanism in a more fine-grained
37    // manner.
38    wifi->SetString(onc::wifi::kSecurity, onc::wifi::kWPA2_PSK);
39  } else {
40    wifi->SetString(onc::wifi::kSecurity, onc::wifi::kSecurityNone);
41  }
42
43  return properties.Pass();
44}
45
46}  // namespace
47
48class WifiManagerNonChromeos::WifiServiceWrapper
49    : public net::NetworkChangeNotifier::NetworkChangeObserver {
50 public:
51  explicit WifiServiceWrapper(
52      base::WeakPtr<WifiManagerNonChromeos> wifi_manager);
53
54  virtual ~WifiServiceWrapper();
55
56  void Start();
57
58  void GetSSIDList(const WifiManager::SSIDListCallback& callback);
59
60  void ConfigureAndConnectPskNetwork(
61      const std::string& ssid,
62      const std::string& password,
63      const WifiManager::SuccessCallback& callback);
64
65  base::WeakPtr<WifiManagerNonChromeos::WifiServiceWrapper> AsWeakPtr();
66
67  void RequestScan();
68
69  void ConnectToNetworkByID(const std::string& network_guid,
70                            const WifiManager::SuccessCallback& callback);
71
72  void RequestNetworkCredentials(
73      const std::string& ssid,
74      const WifiManager::CredentialsCallback& callback);
75
76 private:
77  // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
78  virtual void OnNetworkChanged(
79      net::NetworkChangeNotifier::ConnectionType type) OVERRIDE;
80
81  void GetSSIDListInternal(NetworkPropertiesList* ssid_list);
82
83  void OnNetworkListChangedEvent(const std::vector<std::string>& network_guids);
84
85  void OnNetworksChangedEvent(const std::vector<std::string>& network_guids);
86
87  std::string GetConnectedGUID();
88
89  bool IsConnected(const std::string& network_guid);
90
91  void OnConnectToNetworkTimeout();
92
93  void PostClosure(const base::Closure& closure);
94
95  bool FindAndConfigureNetwork(const std::string& ssid,
96                               const std::string& password,
97                               std::string* network_guid);
98
99  scoped_ptr<WiFiService> wifi_service_;
100
101  base::WeakPtr<WifiManagerNonChromeos> wifi_manager_;
102
103  WifiManager::SuccessCallback connect_success_callback_;
104  base::CancelableClosure connect_failure_callback_;
105
106  // SSID of previously connected network.
107  std::string connected_network_guid_;
108
109  // SSID of network we are connecting to.
110  std::string connecting_network_guid_;
111
112  scoped_refptr<base::MessageLoopProxy> callback_runner_;
113
114  base::WeakPtrFactory<WifiServiceWrapper> weak_factory_;
115
116  DISALLOW_COPY_AND_ASSIGN(WifiServiceWrapper);
117};
118
119WifiManagerNonChromeos::WifiServiceWrapper::WifiServiceWrapper(
120    base::WeakPtr<WifiManagerNonChromeos> wifi_manager)
121    : wifi_manager_(wifi_manager),
122      callback_runner_(base::MessageLoopProxy::current()),
123      weak_factory_(this) {
124}
125
126WifiManagerNonChromeos::WifiServiceWrapper::~WifiServiceWrapper() {
127  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
128}
129
130void WifiManagerNonChromeos::WifiServiceWrapper::Start() {
131  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
132  wifi_service_.reset(WiFiService::Create());
133
134  wifi_service_->Initialize(base::MessageLoopProxy::current());
135
136  wifi_service_->SetEventObservers(
137      base::MessageLoopProxy::current(),
138      base::Bind(&WifiServiceWrapper::OnNetworksChangedEvent, AsWeakPtr()),
139      base::Bind(&WifiServiceWrapper::OnNetworkListChangedEvent, AsWeakPtr()));
140
141  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
142}
143
144void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDList(
145    const WifiManager::SSIDListCallback& callback) {
146  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
147
148  scoped_ptr<NetworkPropertiesList> ssid_list(new NetworkPropertiesList);
149  GetSSIDListInternal(ssid_list.get());
150
151  callback_runner_->PostTask(
152      FROM_HERE,
153      base::Bind(&WifiManagerNonChromeos::PostSSIDListCallback,
154                 wifi_manager_,
155                 callback,
156                 base::Passed(&ssid_list)));
157}
158
159void WifiManagerNonChromeos::WifiServiceWrapper::ConfigureAndConnectPskNetwork(
160    const std::string& ssid,
161    const std::string& password,
162    const WifiManager::SuccessCallback& callback) {
163  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
164  scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password);
165
166  std::string network_guid;
167  std::string error_string;
168  // Will fail without changing system state if network already exists.
169  wifi_service_->CreateNetwork(
170      false, properties.Pass(), &network_guid, &error_string);
171
172  if (error_string.empty()) {
173    ConnectToNetworkByID(network_guid, callback);
174    return;
175  }
176
177  // If we cannot create the network, attempt to configure and connect to an
178  // existing network.
179  if (FindAndConfigureNetwork(ssid, password, &network_guid)) {
180    ConnectToNetworkByID(network_guid, callback);
181  } else {
182    if (!callback.is_null())
183      PostClosure(base::Bind(callback, false /* success */));
184  }
185}
186
187void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkListChangedEvent(
188    const std::vector<std::string>& network_guids) {
189  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
190  scoped_ptr<NetworkPropertiesList> ssid_list(new NetworkPropertiesList);
191  GetSSIDListInternal(ssid_list.get());
192  callback_runner_->PostTask(
193      FROM_HERE,
194      base::Bind(&WifiManagerNonChromeos::OnNetworkListChanged,
195                 wifi_manager_,
196                 base::Passed(&ssid_list)));
197}
198
199void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworksChangedEvent(
200    const std::vector<std::string>& network_guids) {
201  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
202  if (connecting_network_guid_.empty() ||
203      !IsConnected(connecting_network_guid_)) {
204    return;
205  }
206
207  connecting_network_guid_.clear();
208  connect_failure_callback_.Cancel();
209
210  if (!connect_success_callback_.is_null())
211    PostClosure(base::Bind(connect_success_callback_, true));
212
213  connect_success_callback_.Reset();
214}
215
216base::WeakPtr<WifiManagerNonChromeos::WifiServiceWrapper>
217WifiManagerNonChromeos::WifiServiceWrapper::AsWeakPtr() {
218  return weak_factory_.GetWeakPtr();
219}
220
221void WifiManagerNonChromeos::WifiServiceWrapper::RequestScan() {
222  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
223  wifi_service_->RequestNetworkScan();
224}
225
226void WifiManagerNonChromeos::WifiServiceWrapper::ConnectToNetworkByID(
227    const std::string& network_guid,
228    const WifiManager::SuccessCallback& callback) {
229  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
230
231  std::string connected_network_id = GetConnectedGUID();
232  std::string error_string;
233  wifi_service_->StartConnect(network_guid, &error_string);
234
235  if (!error_string.empty()) {
236    LOG(ERROR) << "Could not connect to network by ID: " << error_string;
237    PostClosure(base::Bind(callback, false /* success */));
238    wifi_service_->StartConnect(connected_network_id, &error_string);
239    return;
240  }
241
242  if (IsConnected(network_guid)) {
243    if (!callback.is_null())
244      PostClosure(base::Bind(callback, true /* success */));
245    return;
246  }
247
248  connect_success_callback_ = callback;
249  connecting_network_guid_ = network_guid;
250  connected_network_guid_ = connected_network_id;
251
252  connect_failure_callback_.Reset(base::Bind(
253      &WifiServiceWrapper::OnConnectToNetworkTimeout, base::Unretained(this)));
254
255  base::MessageLoop::current()->PostDelayedTask(
256      FROM_HERE,
257      connect_failure_callback_.callback(),
258      base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds));
259}
260
261void WifiManagerNonChromeos::WifiServiceWrapper::OnConnectToNetworkTimeout() {
262  bool connected = IsConnected(connecting_network_guid_);
263  std::string error_string;
264
265  WifiManager::SuccessCallback connect_success_callback =
266      connect_success_callback_;
267
268  connect_success_callback_.Reset();
269
270  connecting_network_guid_.clear();
271
272  // If we did not connect, return to the network the user was originally
273  // connected to.
274  if (!connected)
275    wifi_service_->StartConnect(connected_network_guid_, &error_string);
276
277  PostClosure(base::Bind(connect_success_callback, connected /* success */));
278}
279
280void WifiManagerNonChromeos::WifiServiceWrapper::RequestNetworkCredentials(
281    const std::string& ssid,
282    const WifiManager::CredentialsCallback& callback) {
283  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
284
285  bool success = true;
286  std::string guid;
287  std::string key;
288
289#if defined(OS_WIN)
290  NOTIMPLEMENTED();
291  success = false;
292#else
293  NetworkPropertiesList network_list;
294
295  GetSSIDListInternal(&network_list);
296
297  for (NetworkPropertiesList::iterator i = network_list.begin();
298       i != network_list.end();
299       i++) {
300    if (i->ssid == ssid) {
301      guid = i->guid;
302      break;
303    }
304  }
305
306  if (guid.empty()) {
307    success = false;
308  }
309
310  if (success) {
311    std::string error_string;
312    wifi_service_->GetKeyFromSystem(guid, &key, &error_string);
313
314    if (!error_string.empty()) {
315      LOG(ERROR) << "Could not get key from system: " << error_string;
316      success = false;
317    }
318  }
319#endif  // OS_WIN
320  PostClosure(base::Bind(callback, success, ssid, key));
321}
322
323void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged(
324    net::NetworkChangeNotifier::ConnectionType type) {
325  wifi_service_->RequestConnectedNetworkUpdate();
326}
327
328void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal(
329    NetworkPropertiesList* ssid_list) {
330  base::ListValue visible_networks;
331
332  wifi_service_->GetVisibleNetworks(
333      onc::network_type::kWiFi, &visible_networks, true);
334
335  for (size_t i = 0; i < visible_networks.GetSize(); i++) {
336    const base::DictionaryValue* network_value = NULL;
337    NetworkProperties network_properties;
338
339    if (!visible_networks.GetDictionary(i, &network_value)) {
340      NOTREACHED();
341    }
342
343    network_properties.UpdateFromValue(*network_value);
344
345    ssid_list->push_back(network_properties);
346  }
347}
348
349std::string WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() {
350  NetworkPropertiesList ssid_list;
351  GetSSIDListInternal(&ssid_list);
352
353  for (NetworkPropertiesList::const_iterator it = ssid_list.begin();
354       it != ssid_list.end();
355       ++it) {
356    if (it->connection_state == onc::connection_state::kConnected)
357      return it->guid;
358  }
359
360  return "";
361}
362
363bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected(
364    const std::string& network_guid) {
365  NetworkPropertiesList ssid_list;
366  GetSSIDListInternal(&ssid_list);
367
368  for (NetworkPropertiesList::const_iterator it = ssid_list.begin();
369       it != ssid_list.end();
370       ++it) {
371    if (it->connection_state == onc::connection_state::kConnected &&
372        it->guid == network_guid)
373      return true;
374  }
375
376  return false;
377}
378
379bool WifiManagerNonChromeos::WifiServiceWrapper::FindAndConfigureNetwork(
380    const std::string& ssid,
381    const std::string& password,
382    std::string* network_guid) {
383  std::string provisional_network_guid;
384  NetworkPropertiesList network_property_list;
385  GetSSIDListInternal(&network_property_list);
386
387  for (size_t i = 0; i < network_property_list.size(); i++) {
388    // TODO(noamsml): Handle case where there are multiple networks with the
389    // same SSID but different security.
390    if (network_property_list[i].ssid == ssid) {
391      provisional_network_guid = network_property_list[i].guid;
392      break;
393    }
394  }
395
396  if (provisional_network_guid.empty())
397    return false;
398
399  scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password);
400
401  std::string error_string;
402  wifi_service_->SetProperties(
403      provisional_network_guid, properties.Pass(), &error_string);
404
405  if (!error_string.empty()) {
406    LOG(ERROR) << "Could not set properties on network: " << error_string;
407    return false;
408  }
409
410  *network_guid = provisional_network_guid;
411  return true;
412}
413
414void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure(
415    const base::Closure& closure) {
416  callback_runner_->PostTask(
417      FROM_HERE,
418      base::Bind(&WifiManagerNonChromeos::PostClosure, wifi_manager_, closure));
419}
420
421scoped_ptr<WifiManager> WifiManager::CreateDefault() {
422  return scoped_ptr<WifiManager>(new WifiManagerNonChromeos());
423}
424
425WifiManagerNonChromeos::WifiManagerNonChromeos()
426    : wifi_wrapper_(NULL), weak_factory_(this) {
427}
428
429WifiManagerNonChromeos::~WifiManagerNonChromeos() {
430  if (wifi_wrapper_) {
431    content::BrowserThread::DeleteSoon(
432        content::BrowserThread::FILE, FROM_HERE, wifi_wrapper_);
433  }
434}
435
436void WifiManagerNonChromeos::Start() {
437  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
438  task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread(
439      content::BrowserThread::FILE);
440
441  // Allocated on UI thread, but all initialization is done on file
442  // thread. Destroyed on file thread, which should be safe since all of the
443  // thread-unsafe members are created on the file thread.
444  wifi_wrapper_ = new WifiServiceWrapper(weak_factory_.GetWeakPtr());
445
446  task_runner_->PostTask(
447      FROM_HERE,
448      base::Bind(&WifiServiceWrapper::Start, wifi_wrapper_->AsWeakPtr()));
449}
450
451void WifiManagerNonChromeos::GetSSIDList(const SSIDListCallback& callback) {
452  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
453  task_runner_->PostTask(FROM_HERE,
454                         base::Bind(&WifiServiceWrapper::GetSSIDList,
455                                    wifi_wrapper_->AsWeakPtr(),
456                                    callback));
457}
458
459void WifiManagerNonChromeos::RequestScan() {
460  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
461  task_runner_->PostTask(
462      FROM_HERE,
463      base::Bind(&WifiServiceWrapper::RequestScan, wifi_wrapper_->AsWeakPtr()));
464}
465
466void WifiManagerNonChromeos::OnNetworkListChanged(
467    scoped_ptr<NetworkPropertiesList> ssid_list) {
468  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
469  FOR_EACH_OBSERVER(NetworkListObserver,
470                    network_list_observers_,
471                    OnNetworkListChanged(*ssid_list));
472}
473
474void WifiManagerNonChromeos::PostClosure(const base::Closure& callback) {
475  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
476  callback.Run();
477}
478
479void WifiManagerNonChromeos::PostSSIDListCallback(
480    const SSIDListCallback& callback,
481    scoped_ptr<NetworkPropertiesList> ssid_list) {
482  callback.Run(*ssid_list);
483}
484
485void WifiManagerNonChromeos::ConfigureAndConnectNetwork(
486    const std::string& ssid,
487    const WifiCredentials& credentials,
488    const SuccessCallback& callback) {
489  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
490  task_runner_->PostTask(
491      FROM_HERE,
492      base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork,
493                 wifi_wrapper_->AsWeakPtr(),
494                 ssid,
495                 credentials.psk,
496                 callback));
497}
498
499void WifiManagerNonChromeos::ConnectToNetworkByID(
500    const std::string& internal_id,
501    const SuccessCallback& callback) {
502  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
503  task_runner_->PostTask(FROM_HERE,
504                         base::Bind(&WifiServiceWrapper::ConnectToNetworkByID,
505                                    wifi_wrapper_->AsWeakPtr(),
506                                    internal_id,
507                                    callback));
508}
509
510void WifiManagerNonChromeos::RequestNetworkCredentials(
511    const std::string& ssid,
512    const CredentialsCallback& callback) {
513  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
514  task_runner_->PostTask(
515      FROM_HERE,
516      base::Bind(&WifiServiceWrapper::RequestNetworkCredentials,
517                 wifi_wrapper_->AsWeakPtr(),
518                 ssid,
519                 callback));
520}
521
522void WifiManagerNonChromeos::AddNetworkListObserver(
523    NetworkListObserver* observer) {
524  network_list_observers_.AddObserver(observer);
525}
526
527void WifiManagerNonChromeos::RemoveNetworkListObserver(
528    NetworkListObserver* observer) {
529  network_list_observers_.RemoveObserver(observer);
530}
531
532}  // namespace wifi
533
534}  // namespace local_discovery
535