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