wifi_manager_nonchromeos.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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::kNone); 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& network_guid, 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& network_guid, 282 const WifiManager::CredentialsCallback& callback) { 283 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 284 285 bool success = true; 286 std::string ssid; 287 std::string key; 288 289#if defined(OS_WIN) 290 NOTIMPLEMENTED(); 291 success = false; 292#else 293 base::DictionaryValue properties; 294 std::string error_string; 295 wifi_service_->GetProperties(network_guid, &properties, &error_string); 296 297 if (!error_string.empty()) { 298 LOG(ERROR) << "Could not get network properties: " << error_string; 299 success = false; 300 } 301 302 if (!properties.GetString(onc::network_config::kName, &ssid)) { 303 LOG(ERROR) << "Could not get network SSID"; 304 success = false; 305 } 306 307 if (success) { 308 wifi_service_->GetKeyFromSystem(network_guid, &key, &error_string); 309 310 if (!error_string.empty()) { 311 LOG(ERROR) << "Could not get key from system: " << error_string; 312 success = false; 313 } 314 } 315#endif // OS_WIN 316 PostClosure(base::Bind(callback, success, ssid, key)); 317} 318 319void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged( 320 net::NetworkChangeNotifier::ConnectionType type) { 321 wifi_service_->RequestConnectedNetworkUpdate(); 322} 323 324void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal( 325 NetworkPropertiesList* ssid_list) { 326 base::ListValue visible_networks; 327 328 wifi_service_->GetVisibleNetworks( 329 onc::network_type::kWiFi, &visible_networks, true); 330 331 for (size_t i = 0; i < visible_networks.GetSize(); i++) { 332 const base::DictionaryValue* network_value = NULL; 333 NetworkProperties network_properties; 334 335 if (!visible_networks.GetDictionary(i, &network_value)) { 336 NOTREACHED(); 337 } 338 339 network_properties.UpdateFromValue(*network_value); 340 341 ssid_list->push_back(network_properties); 342 } 343} 344 345std::string WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() { 346 NetworkPropertiesList ssid_list; 347 GetSSIDListInternal(&ssid_list); 348 349 for (NetworkPropertiesList::const_iterator it = ssid_list.begin(); 350 it != ssid_list.end(); 351 ++it) { 352 if (it->connection_state == onc::connection_state::kConnected) 353 return it->guid; 354 } 355 356 return ""; 357} 358 359bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected( 360 const std::string& network_guid) { 361 NetworkPropertiesList ssid_list; 362 GetSSIDListInternal(&ssid_list); 363 364 for (NetworkPropertiesList::const_iterator it = ssid_list.begin(); 365 it != ssid_list.end(); 366 ++it) { 367 if (it->connection_state == onc::connection_state::kConnected && 368 it->guid == network_guid) 369 return true; 370 } 371 372 return false; 373} 374 375bool WifiManagerNonChromeos::WifiServiceWrapper::FindAndConfigureNetwork( 376 const std::string& ssid, 377 const std::string& password, 378 std::string* network_guid) { 379 std::string provisional_network_guid; 380 NetworkPropertiesList network_property_list; 381 GetSSIDListInternal(&network_property_list); 382 383 for (size_t i = 0; i < network_property_list.size(); i++) { 384 // TODO(noamsml): Handle case where there are multiple networks with the 385 // same SSID but different security. 386 if (network_property_list[i].ssid == ssid) { 387 provisional_network_guid = network_property_list[i].guid; 388 break; 389 } 390 } 391 392 if (provisional_network_guid.empty()) 393 return false; 394 395 scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password); 396 397 std::string error_string; 398 wifi_service_->SetProperties( 399 provisional_network_guid, properties.Pass(), &error_string); 400 401 if (!error_string.empty()) { 402 LOG(ERROR) << "Could not set properties on network: " << error_string; 403 return false; 404 } 405 406 *network_guid = provisional_network_guid; 407 return true; 408} 409 410void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure( 411 const base::Closure& closure) { 412 callback_runner_->PostTask( 413 FROM_HERE, 414 base::Bind(&WifiManagerNonChromeos::PostClosure, wifi_manager_, closure)); 415} 416 417scoped_ptr<WifiManager> WifiManager::CreateDefault() { 418 return scoped_ptr<WifiManager>(new WifiManagerNonChromeos()); 419} 420 421WifiManagerNonChromeos::WifiManagerNonChromeos() 422 : wifi_wrapper_(NULL), weak_factory_(this) { 423} 424 425WifiManagerNonChromeos::~WifiManagerNonChromeos() { 426 if (wifi_wrapper_) { 427 content::BrowserThread::DeleteSoon( 428 content::BrowserThread::FILE, FROM_HERE, wifi_wrapper_); 429 } 430} 431 432void WifiManagerNonChromeos::Start() { 433 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 434 task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread( 435 content::BrowserThread::FILE); 436 437 // Allocated on UI thread, but all initialization is done on file 438 // thread. Destroyed on file thread, which should be safe since all of the 439 // thread-unsafe members are created on the file thread. 440 wifi_wrapper_ = new WifiServiceWrapper(weak_factory_.GetWeakPtr()); 441 442 task_runner_->PostTask( 443 FROM_HERE, 444 base::Bind(&WifiServiceWrapper::Start, wifi_wrapper_->AsWeakPtr())); 445} 446 447void WifiManagerNonChromeos::GetSSIDList(const SSIDListCallback& callback) { 448 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 449 task_runner_->PostTask(FROM_HERE, 450 base::Bind(&WifiServiceWrapper::GetSSIDList, 451 wifi_wrapper_->AsWeakPtr(), 452 callback)); 453} 454 455void WifiManagerNonChromeos::RequestScan() { 456 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 457 task_runner_->PostTask( 458 FROM_HERE, 459 base::Bind(&WifiServiceWrapper::RequestScan, wifi_wrapper_->AsWeakPtr())); 460} 461 462void WifiManagerNonChromeos::OnNetworkListChanged( 463 scoped_ptr<NetworkPropertiesList> ssid_list) { 464 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 465 FOR_EACH_OBSERVER(NetworkListObserver, 466 network_list_observers_, 467 OnNetworkListChanged(*ssid_list)); 468} 469 470void WifiManagerNonChromeos::PostClosure(const base::Closure& callback) { 471 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 472 callback.Run(); 473} 474 475void WifiManagerNonChromeos::PostSSIDListCallback( 476 const SSIDListCallback& callback, 477 scoped_ptr<NetworkPropertiesList> ssid_list) { 478 callback.Run(*ssid_list); 479} 480 481void WifiManagerNonChromeos::ConfigureAndConnectNetwork( 482 const std::string& ssid, 483 const WifiCredentials& credentials, 484 const SuccessCallback& callback) { 485 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 486 task_runner_->PostTask( 487 FROM_HERE, 488 base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork, 489 wifi_wrapper_->AsWeakPtr(), 490 ssid, 491 credentials.psk, 492 callback)); 493} 494 495void WifiManagerNonChromeos::ConnectToNetworkByID( 496 const std::string& internal_id, 497 const SuccessCallback& callback) { 498 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 499 task_runner_->PostTask(FROM_HERE, 500 base::Bind(&WifiServiceWrapper::ConnectToNetworkByID, 501 wifi_wrapper_->AsWeakPtr(), 502 internal_id, 503 callback)); 504} 505 506void WifiManagerNonChromeos::RequestNetworkCredentials( 507 const std::string& internal_id, 508 const CredentialsCallback& callback) { 509 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 510 task_runner_->PostTask( 511 FROM_HERE, 512 base::Bind(&WifiServiceWrapper::RequestNetworkCredentials, 513 wifi_wrapper_->AsWeakPtr(), 514 internal_id, 515 callback)); 516} 517 518void WifiManagerNonChromeos::AddNetworkListObserver( 519 NetworkListObserver* observer) { 520 network_list_observers_.AddObserver(observer); 521} 522 523void WifiManagerNonChromeos::RemoveNetworkListObserver( 524 NetworkListObserver* observer) { 525 network_list_observers_.RemoveObserver(observer); 526} 527 528} // namespace wifi 529 530} // namespace local_discovery 531