OLD | NEW |
(Empty) | |
| 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 using ::wifi::WiFiService; |
| 16 |
| 17 namespace local_discovery { |
| 18 |
| 19 namespace wifi { |
| 20 |
| 21 namespace { |
| 22 |
| 23 const int kConnectionTimeoutSeconds = 10; |
| 24 |
| 25 scoped_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 |
| 48 class 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 |
| 119 WifiManagerNonChromeos::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 |
| 126 WifiManagerNonChromeos::WifiServiceWrapper::~WifiServiceWrapper() { |
| 127 net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
| 128 } |
| 129 |
| 130 void 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, |
| 139 base::Unretained(this)), |
| 140 base::Bind(&WifiServiceWrapper::OnNetworkListChangedEvent, |
| 141 base::Unretained(this))); |
| 142 |
| 143 net::NetworkChangeNotifier::AddNetworkChangeObserver(this); |
| 144 } |
| 145 |
| 146 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDList( |
| 147 const WifiManager::SSIDListCallback& callback) { |
| 148 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 149 |
| 150 scoped_ptr<NetworkPropertiesList> ssid_list(new NetworkPropertiesList); |
| 151 GetSSIDListInternal(ssid_list.get()); |
| 152 |
| 153 callback_runner_->PostTask( |
| 154 FROM_HERE, |
| 155 base::Bind(&WifiManagerNonChromeos::PostSSIDListCallback, |
| 156 wifi_manager_, |
| 157 callback, |
| 158 base::Passed(&ssid_list))); |
| 159 } |
| 160 |
| 161 void WifiManagerNonChromeos::WifiServiceWrapper::ConfigureAndConnectPskNetwork( |
| 162 const std::string& ssid, |
| 163 const std::string& password, |
| 164 const WifiManager::SuccessCallback& callback) { |
| 165 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 166 scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password); |
| 167 |
| 168 std::string network_guid; |
| 169 std::string error_string; |
| 170 // Will fail without changing system state if network already exists. |
| 171 wifi_service_->CreateNetwork( |
| 172 false, properties.Pass(), &network_guid, &error_string); |
| 173 |
| 174 if (error_string.empty()) { |
| 175 ConnectToNetworkByID(network_guid, callback); |
| 176 return; |
| 177 } |
| 178 |
| 179 // If we cannot create the network, attempt to configure and connect to an |
| 180 // existing network. |
| 181 if (FindAndConfigureNetwork(ssid, password, &network_guid)) { |
| 182 ConnectToNetworkByID(network_guid, callback); |
| 183 } else { |
| 184 PostClosure(base::Bind(callback, false /* success */)); |
| 185 } |
| 186 } |
| 187 |
| 188 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkListChangedEvent( |
| 189 const std::vector<std::string>& network_guids) { |
| 190 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 191 scoped_ptr<NetworkPropertiesList> ssid_list(new NetworkPropertiesList); |
| 192 GetSSIDListInternal(ssid_list.get()); |
| 193 callback_runner_->PostTask( |
| 194 FROM_HERE, |
| 195 base::Bind(&WifiManagerNonChromeos::OnNetworkListChanged, |
| 196 wifi_manager_, |
| 197 base::Passed(&ssid_list))); |
| 198 } |
| 199 |
| 200 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworksChangedEvent( |
| 201 const std::vector<std::string>& network_guids) { |
| 202 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 203 if (connecting_network_guid_.empty() || |
| 204 !IsConnected(connecting_network_guid_)) { |
| 205 return; |
| 206 } |
| 207 |
| 208 connecting_network_guid_.clear(); |
| 209 connect_failure_callback_.Cancel(); |
| 210 |
| 211 PostClosure(base::Bind(connect_success_callback_, true)); |
| 212 |
| 213 connect_success_callback_.Reset(); |
| 214 } |
| 215 |
| 216 base::WeakPtr<WifiManagerNonChromeos::WifiServiceWrapper> |
| 217 WifiManagerNonChromeos::WifiServiceWrapper::AsWeakPtr() { |
| 218 return weak_factory_.GetWeakPtr(); |
| 219 } |
| 220 |
| 221 void WifiManagerNonChromeos::WifiServiceWrapper::RequestScan() { |
| 222 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 223 wifi_service_->RequestNetworkScan(); |
| 224 } |
| 225 |
| 226 void 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 PostClosure(base::Bind(callback, true /* success */)); |
| 244 return; |
| 245 } |
| 246 |
| 247 connect_success_callback_ = callback; |
| 248 connecting_network_guid_ = network_guid; |
| 249 connected_network_guid_ = connected_network_id; |
| 250 |
| 251 connect_failure_callback_.Reset(base::Bind( |
| 252 &WifiServiceWrapper::OnConnectToNetworkTimeout, base::Unretained(this))); |
| 253 |
| 254 base::MessageLoop::current()->PostDelayedTask( |
| 255 FROM_HERE, |
| 256 connect_failure_callback_.callback(), |
| 257 base::TimeDelta::FromSeconds(kConnectionTimeoutSeconds)); |
| 258 } |
| 259 |
| 260 void WifiManagerNonChromeos::WifiServiceWrapper::OnConnectToNetworkTimeout() { |
| 261 bool connected = IsConnected(connecting_network_guid_); |
| 262 std::string error_string; |
| 263 |
| 264 WifiManager::SuccessCallback connect_success_callback = |
| 265 connect_success_callback_; |
| 266 |
| 267 connect_success_callback_.Reset(); |
| 268 |
| 269 connecting_network_guid_.clear(); |
| 270 |
| 271 // If we did not connect, return to the network the user was originally |
| 272 // connected to. |
| 273 if (!connected) |
| 274 wifi_service_->StartConnect(connected_network_guid_, &error_string); |
| 275 |
| 276 PostClosure(base::Bind(connect_success_callback, connected /* success */)); |
| 277 } |
| 278 |
| 279 void WifiManagerNonChromeos::WifiServiceWrapper::RequestNetworkCredentials( |
| 280 const std::string& network_guid, |
| 281 const WifiManager::CredentialsCallback& callback) { |
| 282 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 283 |
| 284 bool success = true; |
| 285 std::string ssid; |
| 286 std::string key; |
| 287 |
| 288 #if defined(OS_WIN) |
| 289 NOTIMPLEMENTED(); |
| 290 success = false; |
| 291 #else |
| 292 base::DictionaryValue properties; |
| 293 std::string error_string; |
| 294 wifi_service_->GetProperties(network_guid, &properties, &error_string); |
| 295 |
| 296 if (!error_string.empty()) { |
| 297 LOG(ERROR) << "Could not get network properties: " << error_string; |
| 298 success = false; |
| 299 } |
| 300 |
| 301 if (!properties.GetString(onc::network_config::kName, &ssid)) { |
| 302 LOG(ERROR) << "Could not get network SSID"; |
| 303 success = false; |
| 304 } |
| 305 |
| 306 if (success) { |
| 307 wifi_service_->GetKeyFromSystem(network_guid, &key, &error_string); |
| 308 |
| 309 if (!error_string.empty()) { |
| 310 LOG(ERROR) << "Could not get key from system: " << error_string; |
| 311 success = false; |
| 312 } |
| 313 } |
| 314 #endif // OS_WIN |
| 315 PostClosure(base::Bind(callback, success, ssid, key)); |
| 316 } |
| 317 |
| 318 void WifiManagerNonChromeos::WifiServiceWrapper::OnNetworkChanged( |
| 319 net::NetworkChangeNotifier::ConnectionType type) { |
| 320 wifi_service_->RequestConnectedNetworkUpdate(); |
| 321 } |
| 322 |
| 323 void WifiManagerNonChromeos::WifiServiceWrapper::GetSSIDListInternal( |
| 324 NetworkPropertiesList* ssid_list) { |
| 325 base::ListValue visible_networks; |
| 326 |
| 327 wifi_service_->GetVisibleNetworks( |
| 328 onc::network_type::kWiFi, &visible_networks, true); |
| 329 |
| 330 for (size_t i = 0; i < visible_networks.GetSize(); i++) { |
| 331 const base::DictionaryValue* network_value = NULL; |
| 332 NetworkProperties network_properties; |
| 333 |
| 334 if (!visible_networks.GetDictionary(i, &network_value)) { |
| 335 NOTREACHED(); |
| 336 } |
| 337 |
| 338 network_properties.UpdateFromValue(*network_value); |
| 339 |
| 340 ssid_list->push_back(network_properties); |
| 341 } |
| 342 } |
| 343 |
| 344 std::string WifiManagerNonChromeos::WifiServiceWrapper::GetConnectedGUID() { |
| 345 NetworkPropertiesList ssid_list; |
| 346 GetSSIDListInternal(&ssid_list); |
| 347 |
| 348 for (NetworkPropertiesList::const_iterator it = ssid_list.begin(); |
| 349 it != ssid_list.end(); |
| 350 ++it) { |
| 351 if (it->connection_state == onc::connection_state::kConnected) |
| 352 return it->guid; |
| 353 } |
| 354 |
| 355 return ""; |
| 356 } |
| 357 |
| 358 bool WifiManagerNonChromeos::WifiServiceWrapper::IsConnected( |
| 359 const std::string& network_guid) { |
| 360 NetworkPropertiesList ssid_list; |
| 361 GetSSIDListInternal(&ssid_list); |
| 362 |
| 363 for (NetworkPropertiesList::const_iterator it = ssid_list.begin(); |
| 364 it != ssid_list.end(); |
| 365 ++it) { |
| 366 if (it->connection_state == onc::connection_state::kConnected && |
| 367 it->guid == network_guid) |
| 368 return true; |
| 369 } |
| 370 |
| 371 return false; |
| 372 } |
| 373 |
| 374 bool WifiManagerNonChromeos::WifiServiceWrapper::FindAndConfigureNetwork( |
| 375 const std::string& ssid, |
| 376 const std::string& password, |
| 377 std::string* network_guid) { |
| 378 std::string provisional_network_guid; |
| 379 NetworkPropertiesList network_property_list; |
| 380 GetSSIDListInternal(&network_property_list); |
| 381 |
| 382 for (size_t i = 0; i < network_property_list.size(); i++) { |
| 383 // TODO(noamsml): Handle case where there are multiple networks with the |
| 384 // same SSID but different security. |
| 385 if (network_property_list[i].ssid == ssid) { |
| 386 provisional_network_guid = network_property_list[i].guid; |
| 387 break; |
| 388 } |
| 389 } |
| 390 |
| 391 if (provisional_network_guid.empty()) |
| 392 return false; |
| 393 |
| 394 scoped_ptr<base::DictionaryValue> properties = MakeProperties(ssid, password); |
| 395 |
| 396 std::string error_string; |
| 397 wifi_service_->SetProperties( |
| 398 provisional_network_guid, properties.Pass(), &error_string); |
| 399 |
| 400 if (!error_string.empty()) { |
| 401 LOG(ERROR) << "Could not set properties on network: " << error_string; |
| 402 return false; |
| 403 } |
| 404 |
| 405 *network_guid = provisional_network_guid; |
| 406 return true; |
| 407 } |
| 408 |
| 409 void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure( |
| 410 const base::Closure& closure) { |
| 411 callback_runner_->PostTask( |
| 412 FROM_HERE, |
| 413 base::Bind(&WifiManagerNonChromeos::PostClosure, wifi_manager_, closure)); |
| 414 } |
| 415 |
| 416 scoped_ptr<WifiManager> WifiManager::Create() { |
| 417 return scoped_ptr<WifiManager>(new WifiManagerNonChromeos()); |
| 418 } |
| 419 |
| 420 WifiManagerNonChromeos::WifiManagerNonChromeos() |
| 421 : wifi_wrapper_(NULL), weak_factory_(this) { |
| 422 } |
| 423 |
| 424 WifiManagerNonChromeos::~WifiManagerNonChromeos() { |
| 425 if (wifi_wrapper_) { |
| 426 content::BrowserThread::DeleteSoon( |
| 427 content::BrowserThread::FILE, FROM_HERE, wifi_wrapper_); |
| 428 } |
| 429 } |
| 430 |
| 431 void WifiManagerNonChromeos::Start() { |
| 432 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 433 task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread( |
| 434 content::BrowserThread::FILE); |
| 435 |
| 436 // Allocated on UI thread, but all initialization is done on file |
| 437 // thread. Destroyed on file thread, which should be safe since all of the |
| 438 // thread-unsafe members are created on the file thread. |
| 439 wifi_wrapper_ = new WifiServiceWrapper(weak_factory_.GetWeakPtr()); |
| 440 |
| 441 task_runner_->PostTask( |
| 442 FROM_HERE, |
| 443 base::Bind(&WifiServiceWrapper::Start, wifi_wrapper_->AsWeakPtr())); |
| 444 } |
| 445 |
| 446 void WifiManagerNonChromeos::GetSSIDList(const SSIDListCallback& callback) { |
| 447 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 448 task_runner_->PostTask(FROM_HERE, |
| 449 base::Bind(&WifiServiceWrapper::GetSSIDList, |
| 450 wifi_wrapper_->AsWeakPtr(), |
| 451 callback)); |
| 452 } |
| 453 |
| 454 void WifiManagerNonChromeos::RequestScan() { |
| 455 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 456 task_runner_->PostTask( |
| 457 FROM_HERE, |
| 458 base::Bind(&WifiServiceWrapper::RequestScan, wifi_wrapper_->AsWeakPtr())); |
| 459 } |
| 460 |
| 461 void WifiManagerNonChromeos::OnNetworkListChanged( |
| 462 scoped_ptr<NetworkPropertiesList> ssid_list) { |
| 463 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 464 FOR_EACH_OBSERVER(NetworkListObserver, |
| 465 network_list_observers_, |
| 466 OnNetworkListChanged(*ssid_list)); |
| 467 } |
| 468 |
| 469 void WifiManagerNonChromeos::PostClosure(const base::Closure& callback) { |
| 470 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 471 callback.Run(); |
| 472 } |
| 473 |
| 474 void WifiManagerNonChromeos::PostSSIDListCallback( |
| 475 const SSIDListCallback& callback, |
| 476 scoped_ptr<NetworkPropertiesList> ssid_list) { |
| 477 callback.Run(*ssid_list); |
| 478 } |
| 479 |
| 480 void WifiManagerNonChromeos::ConfigureAndConnectNetwork( |
| 481 const std::string& ssid, |
| 482 const WifiCredentials& credentials, |
| 483 const SuccessCallback& callback) { |
| 484 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 485 task_runner_->PostTask( |
| 486 FROM_HERE, |
| 487 base::Bind(&WifiServiceWrapper::ConfigureAndConnectPskNetwork, |
| 488 wifi_wrapper_->AsWeakPtr(), |
| 489 ssid, |
| 490 credentials.psk, |
| 491 callback)); |
| 492 } |
| 493 |
| 494 void WifiManagerNonChromeos::ConnectToNetworkByID( |
| 495 const std::string& internal_id, |
| 496 const SuccessCallback& callback) { |
| 497 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 498 task_runner_->PostTask(FROM_HERE, |
| 499 base::Bind(&WifiServiceWrapper::ConnectToNetworkByID, |
| 500 wifi_wrapper_->AsWeakPtr(), |
| 501 internal_id, |
| 502 callback)); |
| 503 } |
| 504 |
| 505 void WifiManagerNonChromeos::RequestNetworkCredentials( |
| 506 const std::string& internal_id, |
| 507 const CredentialsCallback& callback) { |
| 508 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 509 task_runner_->PostTask( |
| 510 FROM_HERE, |
| 511 base::Bind(&WifiServiceWrapper::RequestNetworkCredentials, |
| 512 wifi_wrapper_->AsWeakPtr(), |
| 513 internal_id, |
| 514 callback)); |
| 515 } |
| 516 |
| 517 void WifiManagerNonChromeos::AddNetworkListObserver( |
| 518 NetworkListObserver* observer) { |
| 519 network_list_observers_.AddObserver(observer); |
| 520 } |
| 521 |
| 522 void WifiManagerNonChromeos::RemoveNetworkListObserver( |
| 523 NetworkListObserver* observer) { |
| 524 network_list_observers_.RemoveObserver(observer); |
| 525 } |
| 526 |
| 527 } // namespace wifi |
| 528 |
| 529 } // namespace local_discovery |
OLD | NEW |