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