Home | History | Annotate | Download | only in wifi
      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 
     15 #if defined(OS_WIN)
     16 #include "chrome/browser/local_discovery/wifi/credential_getter_win.h"
     17 #endif  // OS_WIN
     18 
     19 using ::wifi::WiFiService;
     20 
     21 namespace local_discovery {
     22 
     23 namespace wifi {
     24 
     25 namespace {
     26 
     27 const int kConnectionTimeoutSeconds = 10;
     28 
     29 scoped_ptr<base::DictionaryValue> MakeProperties(const std::string& ssid,
     30                                                  const std::string& password) {
     31   scoped_ptr<base::DictionaryValue> properties(new base::DictionaryValue);
     32 
     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);
     36 
     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
     41     // manner.
     42     wifi->SetString(onc::wifi::kSecurity, onc::wifi::kWPA2_PSK);
     43   } else {
     44     wifi->SetString(onc::wifi::kSecurity, onc::wifi::kSecurityNone);
     45   }
     46 
     47   return properties.Pass();
     48 }
     49 
     50 }  // namespace
     51 
     52 class WifiManagerNonChromeos::WifiServiceWrapper
     53     : public net::NetworkChangeNotifier::NetworkChangeObserver {
     54  public:
     55   explicit WifiServiceWrapper(
     56       base::WeakPtr<WifiManagerNonChromeos> wifi_manager);
     57 
     58   virtual ~WifiServiceWrapper();
     59 
     60   void Start();
     61 
     62   void GetSSIDList(const WifiManager::SSIDListCallback& callback);
     63 
     64   void ConfigureAndConnectPskNetwork(
     65       const std::string& ssid,
     66       const std::string& password,
     67       const WifiManager::SuccessCallback& callback);
     68 
     69   base::WeakPtr<WifiManagerNonChromeos::WifiServiceWrapper> AsWeakPtr();
     70 
     71   void RequestScan();
     72 
     73   void ConnectToNetworkByID(const std::string& network_guid,
     74                             const WifiManager::SuccessCallback& callback);
     75 
     76   void RequestNetworkCredentials(
     77       const std::string& ssid,
     78       const WifiManager::CredentialsCallback& callback);
     79 
     80  private:
     81   // net::NetworkChangeNotifier::NetworkChangeObserver implementation.
     82   virtual void OnNetworkChanged(
     83       net::NetworkChangeNotifier::ConnectionType type) OVERRIDE;
     84 
     85   void GetSSIDListInternal(NetworkPropertiesList* ssid_list);
     86 
     87   void OnNetworkListChangedEvent(const std::vector<std::string>& network_guids);
     88 
     89   void OnNetworksChangedEvent(const std::vector<std::string>& network_guids);
     90 
     91   std::string GetConnectedGUID();
     92 
     93   bool IsConnected(const std::string& network_guid);
     94 
     95   void OnConnectToNetworkTimeout();
     96 
     97   void PostClosure(const base::Closure& closure);
     98 
     99   bool FindAndConfigureNetwork(const std::string& ssid,
    100                                const std::string& password,
    101                                std::string* network_guid);
    102 
    103 #if defined(OS_WIN)
    104   void PostCredentialsCallback(const WifiManager::CredentialsCallback& callback,
    105                                const std::string& ssid,
    106                                bool success,
    107                                const std::string& password);
    108 #endif  // OS_WIN
    109 
    110   scoped_ptr<WiFiService> wifi_service_;
    111 
    112   base::WeakPtr<WifiManagerNonChromeos> wifi_manager_;
    113 
    114   WifiManager::SuccessCallback connect_success_callback_;
    115   base::CancelableClosure connect_failure_callback_;
    116 
    117   // SSID of previously connected network.
    118   std::string connected_network_guid_;
    119 
    120   // SSID of network we are connecting to.
    121   std::string connecting_network_guid_;
    122 
    123   scoped_refptr<base::MessageLoopProxy> callback_runner_;
    124 
    125   base::WeakPtrFactory<WifiServiceWrapper> weak_factory_;
    126 
    127 #if defined(OS_WIN)
    128   scoped_refptr<CredentialGetterWin> credential_getter_;
    129 #endif  // OS_WIN
    130 
    131   DISALLOW_COPY_AND_ASSIGN(WifiServiceWrapper);
    132 };
    133 
    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) {
    139 }
    140 
    141 WifiManagerNonChromeos::WifiServiceWrapper::~WifiServiceWrapper() {
    142   net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
    143 }
    144 
    145 void WifiManagerNonChromeos::WifiServiceWrapper::Start() {
    146   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    147   wifi_service_.reset(WiFiService::Create());
    148 
    149   wifi_service_->Initialize(base::MessageLoopProxy::current());
    150 
    151   wifi_service_->SetEventObservers(
    152       base::MessageLoopProxy::current(),
    153       base::Bind(&WifiServiceWrapper::OnNetworksChangedEvent, AsWeakPtr()),
    154       base::Bind(&WifiServiceWrapper::OnNetworkListChangedEvent, AsWeakPtr()));
    155 
    156   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
    157 }
    158 
    159 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDList(
    160     const WifiManager::SSIDListCallback& callback) {
    161   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    162 
    163   scoped_ptr<NetworkPropertiesList> ssid_list(new NetworkPropertiesList);
    164   GetSSIDListInternal(ssid_list.get());
    165 
    166   callback_runner_->PostTask(
    167       FROM_HERE,
    168       base::Bind(&WifiManagerNonChromeos::PostSSIDListCallback,
    169                  wifi_manager_,
    170                  callback,
    171                  base::Passed(&ssid_list)));
    172 }
    173 
    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);
    180 
    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);
    186 
    187   if (error_string.empty()) {
    188     ConnectToNetworkByID(network_guid, callback);
    189     return;
    190   }
    191 
    192   // If we cannot create the network, attempt to configure and connect to an
    193   // existing network.
    194   if (FindAndConfigureNetwork(ssid, password, &network_guid)) {
    195     ConnectToNetworkByID(network_guid, callback);
    196   } else {
    197     if (!callback.is_null())
    198       PostClosure(base::Bind(callback, false /* success */));
    199   }
    200 }
    201 
    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(
    208       FROM_HERE,
    209       base::Bind(&WifiManagerNonChromeos::OnNetworkListChanged,
    210                  wifi_manager_,
    211                  base::Passed(&ssid_list)));
    212 }
    213 
    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_)) {
    219     return;
    220   }
    221 
    222   connecting_network_guid_.clear();
    223   connect_failure_callback_.Cancel();
    224 
    225   if (!connect_success_callback_.is_null())
    226     PostClosure(base::Bind(connect_success_callback_, true));
    227 
    228   connect_success_callback_.Reset();
    229 }
    230 
    231 base::WeakPtr<WifiManagerNonChromeos::WifiServiceWrapper>
    232 WifiManagerNonChromeos::WifiServiceWrapper::AsWeakPtr() {
    233   return weak_factory_.GetWeakPtr();
    234 }
    235 
    236 void WifiManagerNonChromeos::WifiServiceWrapper::RequestScan() {
    237   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    238   wifi_service_->RequestNetworkScan();
    239 }
    240 
    241 void WifiManagerNonChromeos::WifiServiceWrapper::ConnectToNetworkByID(
    242     const std::string& network_guid,
    243     const WifiManager::SuccessCallback& callback) {
    244   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    245 
    246   std::string connected_network_id = GetConnectedGUID();
    247   std::string error_string;
    248   wifi_service_->StartConnect(network_guid, &error_string);
    249 
    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);
    254     return;
    255   }
    256 
    257   if (IsConnected(network_guid)) {
    258     if (!callback.is_null())
    259       PostClosure(base::Bind(callback, true /* success */));
    260     return;
    261   }
    262 
    263   connect_success_callback_ = callback;
    264   connecting_network_guid_ = network_guid;
    265   connected_network_guid_ = connected_network_id;
    266 
    267   connect_failure_callback_.Reset(base::Bind(
    268       &WifiServiceWrapper::OnConnectToNetworkTimeout, base::Unretained(this)));
    269 
    270   base::MessageLoop::current()->PostDelayedTask(
    271       FROM_HERE,
    272       connect_failure_callback_.callback(),
    273       base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds));
    274 }
    275 
    276 void WifiManagerNonChromeos::WifiServiceWrapper::OnConnectToNetworkTimeout() {
    277   bool connected = IsConnected(connecting_network_guid_);
    278   std::string error_string;
    279 
    280   WifiManager::SuccessCallback connect_success_callback =
    281       connect_success_callback_;
    282 
    283   connect_success_callback_.Reset();
    284 
    285   connecting_network_guid_.clear();
    286 
    287   // If we did not connect, return to the network the user was originally
    288   // connected to.
    289   if (!connected)
    290     wifi_service_->StartConnect(connected_network_guid_, &error_string);
    291 
    292   PostClosure(base::Bind(connect_success_callback, connected /* success */));
    293 }
    294 
    295 void WifiManagerNonChromeos::WifiServiceWrapper::RequestNetworkCredentials(
    296     const std::string& ssid,
    297     const WifiManager::CredentialsCallback& callback) {
    298   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
    299 
    300   bool success = true;
    301   std::string guid;
    302   std::string key;
    303 
    304   NetworkPropertiesList network_list;
    305 
    306   GetSSIDListInternal(&network_list);
    307 
    308   for (NetworkPropertiesList::iterator i = network_list.begin();
    309        i != network_list.end();
    310        i++) {
    311     if (i->ssid == ssid) {
    312       guid = i->guid;
    313       break;
    314     }
    315   }
    316 
    317   if (guid.empty()) {
    318     success = false;
    319   }
    320 
    321   if (!success) {
    322     PostClosure(base::Bind(callback, success, "", ""));
    323     return;
    324   }
    325 
    326 #if defined(OS_WIN)
    327   credential_getter_ = new CredentialGetterWin();
    328   credential_getter_->StartGetCredentials(
    329       guid,
    330       base::Bind(&WifiServiceWrapper::PostCredentialsCallback,
    331                  AsWeakPtr(),
    332                  callback,
    333                  ssid));
    334 #else
    335   if (success) {
    336     std::string error_string;
    337     wifi_service_->GetKeyFromSystem(guid, &key, &error_string);
    338 
    339     if (!error_string.empty()) {
    340       LOG(ERROR) << "Could not get key from system: " << error_string;
    341       success = false;
    342     }
    343 
    344     PostClosure(base::Bind(callback, success, ssid, key));
    345   }
    346 #endif  // OS_WIN
    347 }
    348 
    349 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged(
    350     net::NetworkChangeNotifier::ConnectionType type) {
    351   wifi_service_->RequestConnectedNetworkUpdate();
    352 }
    353 
    354 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal(
    355     NetworkPropertiesList* ssid_list) {
    356   base::ListValue visible_networks;
    357 
    358   wifi_service_->GetVisibleNetworks(
    359       onc::network_type::kWiFi, &visible_networks, true);
    360 
    361   for (size_t i = 0; i < visible_networks.GetSize(); i++) {
    362     const base::DictionaryValue* network_value = NULL;
    363     NetworkProperties network_properties;
    364 
    365     if (!visible_networks.GetDictionary(i, &network_value)) {
    366       NOTREACHED();
    367     }
    368 
    369     network_properties.UpdateFromValue(*network_value);
    370 
    371     ssid_list->push_back(network_properties);
    372   }
    373 }
    374 
    375 std::string WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() {
    376   NetworkPropertiesList ssid_list;
    377   GetSSIDListInternal(&ssid_list);
    378 
    379   for (NetworkPropertiesList::const_iterator it = ssid_list.begin();
    380        it != ssid_list.end();
    381        ++it) {
    382     if (it->connection_state == onc::connection_state::kConnected)
    383       return it->guid;
    384   }
    385 
    386   return "";
    387 }
    388 
    389 bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected(
    390     const std::string& network_guid) {
    391   NetworkPropertiesList ssid_list;
    392   GetSSIDListInternal(&ssid_list);
    393 
    394   for (NetworkPropertiesList::const_iterator it = ssid_list.begin();
    395        it != ssid_list.end();
    396        ++it) {
    397     if (it->connection_state == onc::connection_state::kConnected &&
    398         it->guid == network_guid)
    399       return true;
    400   }
    401 
    402   return false;
    403 }
    404 
    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);
    412 
    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;
    418       break;
    419     }
    420   }
    421 
    422   if (provisional_network_guid.empty())
    423     return false;
    424 
    425   scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password);
    426 
    427   std::string error_string;
    428   wifi_service_->SetProperties(
    429       provisional_network_guid, properties.Pass(), &error_string);
    430 
    431   if (!error_string.empty()) {
    432     LOG(ERROR) << "Could not set properties on network: " << error_string;
    433     return false;
    434   }
    435 
    436   *network_guid = provisional_network_guid;
    437   return true;
    438 }
    439 
    440 void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure(
    441     const base::Closure& closure) {
    442   callback_runner_->PostTask(
    443       FROM_HERE,
    444       base::Bind(&WifiManagerNonChromeos::PostClosure, wifi_manager_, closure));
    445 }
    446 
    447 #if defined(OS_WIN)
    448 void WifiManagerNonChromeos::WifiServiceWrapper::PostCredentialsCallback(
    449     const WifiManager::CredentialsCallback& callback,
    450     const std::string& ssid,
    451     bool success,
    452     const std::string& password) {
    453   PostClosure(base::Bind(callback, success, ssid, password));
    454 }
    455 
    456 #endif  // OS_WIN
    457 
    458 scoped_ptr<WifiManager> WifiManager::CreateDefault() {
    459   return scoped_ptr<WifiManager>(new WifiManagerNonChromeos());
    460 }
    461 
    462 WifiManagerNonChromeos::WifiManagerNonChromeos()
    463     : wifi_wrapper_(NULL), weak_factory_(this) {
    464 }
    465 
    466 WifiManagerNonChromeos::~WifiManagerNonChromeos() {
    467   if (wifi_wrapper_) {
    468     content::BrowserThread::DeleteSoon(
    469         content::BrowserThread::FILE, FROM_HERE, wifi_wrapper_);
    470   }
    471 }
    472 
    473 void WifiManagerNonChromeos::Start() {
    474   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    475   task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread(
    476       content::BrowserThread::FILE);
    477 
    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());
    482 
    483   task_runner_->PostTask(
    484       FROM_HERE,
    485       base::Bind(&WifiServiceWrapper::Start, wifi_wrapper_->AsWeakPtr()));
    486 }
    487 
    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(),
    493                                     callback));
    494 }
    495 
    496 void WifiManagerNonChromeos::RequestScan() {
    497   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    498   task_runner_->PostTask(
    499       FROM_HERE,
    500       base::Bind(&WifiServiceWrapper::RequestScan, wifi_wrapper_->AsWeakPtr()));
    501 }
    502 
    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));
    509 }
    510 
    511 void WifiManagerNonChromeos::PostClosure(const base::Closure& callback) {
    512   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    513   callback.Run();
    514 }
    515 
    516 void WifiManagerNonChromeos::PostSSIDListCallback(
    517     const SSIDListCallback& callback,
    518     scoped_ptr<NetworkPropertiesList> ssid_list) {
    519   callback.Run(*ssid_list);
    520 }
    521 
    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(
    528       FROM_HERE,
    529       base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork,
    530                  wifi_wrapper_->AsWeakPtr(),
    531                  ssid,
    532                  credentials.psk,
    533                  callback));
    534 }
    535 
    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(),
    543                                     internal_id,
    544                                     callback));
    545 }
    546 
    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(
    552       FROM_HERE,
    553       base::Bind(&WifiServiceWrapper::RequestNetworkCredentials,
    554                  wifi_wrapper_->AsWeakPtr(),
    555                  ssid,
    556                  callback));
    557 }
    558 
    559 void WifiManagerNonChromeos::AddNetworkListObserver(
    560     NetworkListObserver* observer) {
    561   network_list_observers_.AddObserver(observer);
    562 }
    563 
    564 void WifiManagerNonChromeos::RemoveNetworkListObserver(
    565     NetworkListObserver* observer) {
    566   network_list_observers_.RemoveObserver(observer);
    567 }
    568 
    569 }  // namespace wifi
    570 
    571 }  // namespace local_discovery
    572