1 // Copyright 2013 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 "chromeos/dbus/fake_shill_service_client.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/command_line.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/stl_util.h" 12 #include "base/strings/string_util.h" 13 #include "base/values.h" 14 #include "chromeos/chromeos_switches.h" 15 #include "chromeos/dbus/dbus_thread_manager.h" 16 #include "chromeos/dbus/shill_manager_client.h" 17 #include "chromeos/dbus/shill_property_changed_observer.h" 18 #include "chromeos/dbus/shill_stub_helper.h" 19 #include "chromeos/network/shill_property_util.h" 20 #include "dbus/bus.h" 21 #include "dbus/message.h" 22 #include "dbus/object_path.h" 23 #include "third_party/cros_system_api/dbus/service_constants.h" 24 25 namespace chromeos { 26 27 namespace { 28 29 void PassStubListValue(const ShillServiceClient::ListValueCallback& callback, 30 base::ListValue* value) { 31 callback.Run(*value); 32 } 33 34 void PassStubServiceProperties( 35 const ShillServiceClient::DictionaryValueCallback& callback, 36 DBusMethodCallStatus call_status, 37 const base::DictionaryValue* properties) { 38 callback.Run(call_status, *properties); 39 } 40 41 } // namespace 42 43 FakeShillServiceClient::FakeShillServiceClient() : weak_ptr_factory_(this) { 44 } 45 46 FakeShillServiceClient::~FakeShillServiceClient() { 47 STLDeleteContainerPairSecondPointers( 48 observer_list_.begin(), observer_list_.end()); 49 } 50 51 52 // ShillServiceClient overrides. 53 54 void FakeShillServiceClient::Init(dbus::Bus* bus) { 55 } 56 57 void FakeShillServiceClient::AddPropertyChangedObserver( 58 const dbus::ObjectPath& service_path, 59 ShillPropertyChangedObserver* observer) { 60 GetObserverList(service_path).AddObserver(observer); 61 } 62 63 void FakeShillServiceClient::RemovePropertyChangedObserver( 64 const dbus::ObjectPath& service_path, 65 ShillPropertyChangedObserver* observer) { 66 GetObserverList(service_path).RemoveObserver(observer); 67 } 68 69 void FakeShillServiceClient::GetProperties( 70 const dbus::ObjectPath& service_path, 71 const DictionaryValueCallback& callback) { 72 base::DictionaryValue* nested_dict = NULL; 73 scoped_ptr<base::DictionaryValue> result_properties; 74 DBusMethodCallStatus call_status; 75 stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(), 76 &nested_dict); 77 if (nested_dict) { 78 result_properties.reset(nested_dict->DeepCopy()); 79 // Remove credentials that Shill wouldn't send. 80 result_properties->RemoveWithoutPathExpansion(shill::kPassphraseProperty, 81 NULL); 82 call_status = DBUS_METHOD_CALL_SUCCESS; 83 } else { 84 LOG(ERROR) << "Properties not found for: " << service_path.value(); 85 result_properties.reset(new base::DictionaryValue); 86 call_status = DBUS_METHOD_CALL_FAILURE; 87 } 88 89 base::MessageLoop::current()->PostTask( 90 FROM_HERE, 91 base::Bind(&PassStubServiceProperties, 92 callback, 93 call_status, 94 base::Owned(result_properties.release()))); 95 } 96 97 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path, 98 const std::string& name, 99 const base::Value& value, 100 const base::Closure& callback, 101 const ErrorCallback& error_callback) { 102 if (!SetServiceProperty(service_path.value(), name, value)) { 103 LOG(ERROR) << "Service not found: " << service_path.value(); 104 error_callback.Run("Error.InvalidService", "Invalid Service"); 105 return; 106 } 107 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 108 } 109 110 void FakeShillServiceClient::SetProperties( 111 const dbus::ObjectPath& service_path, 112 const base::DictionaryValue& properties, 113 const base::Closure& callback, 114 const ErrorCallback& error_callback) { 115 for (base::DictionaryValue::Iterator iter(properties); 116 !iter.IsAtEnd(); iter.Advance()) { 117 if (!SetServiceProperty(service_path.value(), iter.key(), iter.value())) { 118 LOG(ERROR) << "Service not found: " << service_path.value(); 119 error_callback.Run("Error.InvalidService", "Invalid Service"); 120 return; 121 } 122 } 123 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 124 } 125 126 void FakeShillServiceClient::ClearProperty( 127 const dbus::ObjectPath& service_path, 128 const std::string& name, 129 const base::Closure& callback, 130 const ErrorCallback& error_callback) { 131 base::DictionaryValue* dict = NULL; 132 if (!stub_services_.GetDictionaryWithoutPathExpansion( 133 service_path.value(), &dict)) { 134 error_callback.Run("Error.InvalidService", "Invalid Service"); 135 return; 136 } 137 dict->RemoveWithoutPathExpansion(name, NULL); 138 // Note: Shill does not send notifications when properties are cleared. 139 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 140 } 141 142 void FakeShillServiceClient::ClearProperties( 143 const dbus::ObjectPath& service_path, 144 const std::vector<std::string>& names, 145 const ListValueCallback& callback, 146 const ErrorCallback& error_callback) { 147 base::DictionaryValue* dict = NULL; 148 if (!stub_services_.GetDictionaryWithoutPathExpansion( 149 service_path.value(), &dict)) { 150 error_callback.Run("Error.InvalidService", "Invalid Service"); 151 return; 152 } 153 scoped_ptr<base::ListValue> results(new base::ListValue); 154 for (std::vector<std::string>::const_iterator iter = names.begin(); 155 iter != names.end(); ++iter) { 156 dict->RemoveWithoutPathExpansion(*iter, NULL); 157 // Note: Shill does not send notifications when properties are cleared. 158 results->AppendBoolean(true); 159 } 160 base::MessageLoop::current()->PostTask( 161 FROM_HERE, 162 base::Bind(&PassStubListValue, 163 callback, base::Owned(results.release()))); 164 } 165 166 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path, 167 const base::Closure& callback, 168 const ErrorCallback& error_callback) { 169 VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value(); 170 base::DictionaryValue* service_properties = NULL; 171 if (!stub_services_.GetDictionary( 172 service_path.value(), &service_properties)) { 173 LOG(ERROR) << "Service not found: " << service_path.value(); 174 error_callback.Run("Error.InvalidService", "Invalid Service"); 175 return; 176 } 177 178 // Set any other services of the same Type to 'offline' first, before setting 179 // State to Association which will trigger sorting Manager.Services and 180 // sending an update. 181 SetOtherServicesOffline(service_path.value()); 182 183 // Set Associating. 184 base::StringValue associating_value(shill::kStateAssociation); 185 SetServiceProperty(service_path.value(), 186 shill::kStateProperty, 187 associating_value); 188 189 // Stay Associating until the state is changed again after a delay. 190 base::TimeDelta delay; 191 if (CommandLine::ForCurrentProcess()->HasSwitch( 192 chromeos::switches::kEnableStubInteractive)) { 193 const int kConnectDelaySeconds = 5; 194 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds); 195 } 196 base::MessageLoop::current()->PostDelayedTask( 197 FROM_HERE, 198 base::Bind(&FakeShillServiceClient::ContinueConnect, 199 weak_ptr_factory_.GetWeakPtr(), 200 service_path.value()), 201 delay); 202 203 callback.Run(); 204 } 205 206 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path, 207 const base::Closure& callback, 208 const ErrorCallback& error_callback) { 209 base::Value* service; 210 if (!stub_services_.Get(service_path.value(), &service)) { 211 error_callback.Run("Error.InvalidService", "Invalid Service"); 212 return; 213 } 214 base::TimeDelta delay; 215 if (CommandLine::ForCurrentProcess()->HasSwitch( 216 chromeos::switches::kEnableStubInteractive)) { 217 const int kConnectDelaySeconds = 2; 218 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds); 219 } 220 // Set Idle after a delay 221 base::StringValue idle_value(shill::kStateIdle); 222 base::MessageLoop::current()->PostDelayedTask( 223 FROM_HERE, 224 base::Bind(&FakeShillServiceClient::SetProperty, 225 weak_ptr_factory_.GetWeakPtr(), 226 service_path, 227 shill::kStateProperty, 228 idle_value, 229 base::Bind(&base::DoNothing), 230 error_callback), 231 delay); 232 callback.Run(); 233 } 234 235 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path, 236 const base::Closure& callback, 237 const ErrorCallback& error_callback) { 238 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 239 } 240 241 void FakeShillServiceClient::ActivateCellularModem( 242 const dbus::ObjectPath& service_path, 243 const std::string& carrier, 244 const base::Closure& callback, 245 const ErrorCallback& error_callback) { 246 base::DictionaryValue* service_properties = 247 GetModifiableServiceProperties(service_path.value(), false); 248 if (!service_properties) { 249 LOG(ERROR) << "Service not found: " << service_path.value(); 250 error_callback.Run("Error.InvalidService", "Invalid Service"); 251 } 252 SetServiceProperty(service_path.value(), 253 shill::kActivationStateProperty, 254 base::StringValue(shill::kActivationStateActivating)); 255 base::TimeDelta delay; 256 if (CommandLine::ForCurrentProcess()->HasSwitch( 257 chromeos::switches::kEnableStubInteractive)) { 258 const int kConnectDelaySeconds = 2; 259 delay = base::TimeDelta::FromSeconds(kConnectDelaySeconds); 260 } 261 // Set Activated after a delay 262 base::MessageLoop::current()->PostDelayedTask( 263 FROM_HERE, 264 base::Bind(&FakeShillServiceClient::SetCellularActivated, 265 weak_ptr_factory_.GetWeakPtr(), 266 service_path, 267 error_callback), 268 delay); 269 270 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 271 } 272 273 void FakeShillServiceClient::CompleteCellularActivation( 274 const dbus::ObjectPath& service_path, 275 const base::Closure& callback, 276 const ErrorCallback& error_callback) { 277 base::MessageLoop::current()->PostTask(FROM_HERE, callback); 278 } 279 280 void FakeShillServiceClient::GetLoadableProfileEntries( 281 const dbus::ObjectPath& service_path, 282 const DictionaryValueCallback& callback) { 283 // Provide a dictionary with a single { profile_path, service_path } entry 284 // if the Profile property is set, or an empty dictionary. 285 scoped_ptr<base::DictionaryValue> result_properties( 286 new base::DictionaryValue); 287 base::DictionaryValue* service_properties = 288 GetModifiableServiceProperties(service_path.value(), false); 289 if (service_properties) { 290 std::string profile_path; 291 if (service_properties->GetStringWithoutPathExpansion( 292 shill::kProfileProperty, &profile_path)) { 293 result_properties->SetStringWithoutPathExpansion( 294 profile_path, service_path.value()); 295 } 296 } else { 297 LOG(WARNING) << "Service not in profile: " << service_path.value(); 298 } 299 300 DBusMethodCallStatus call_status = DBUS_METHOD_CALL_SUCCESS; 301 base::MessageLoop::current()->PostTask( 302 FROM_HERE, 303 base::Bind(&PassStubServiceProperties, 304 callback, 305 call_status, 306 base::Owned(result_properties.release()))); 307 } 308 309 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() { 310 return this; 311 } 312 313 // ShillServiceClient::TestInterface overrides. 314 315 void FakeShillServiceClient::AddService(const std::string& service_path, 316 const std::string& name, 317 const std::string& type, 318 const std::string& state, 319 bool add_to_visible_list, 320 bool add_to_watch_list) { 321 std::string nstate = state; 322 if (CommandLine::ForCurrentProcess()->HasSwitch( 323 chromeos::switches::kDefaultStubNetworkStateIdle)) { 324 nstate = shill::kStateIdle; 325 } 326 AddServiceWithIPConfig(service_path, name, type, nstate, "", 327 add_to_visible_list, add_to_watch_list); 328 } 329 330 void FakeShillServiceClient::AddServiceWithIPConfig( 331 const std::string& service_path, 332 const std::string& name, 333 const std::string& type, 334 const std::string& state, 335 const std::string& ipconfig_path, 336 bool add_to_visible_list, 337 bool add_to_watch_list) { 338 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> 339 AddManagerService(service_path, add_to_visible_list, add_to_watch_list); 340 341 base::DictionaryValue* properties = 342 GetModifiableServiceProperties(service_path, true); 343 connect_behavior_.erase(service_path); 344 shill_property_util::SetSSID(name, properties); 345 properties->SetWithoutPathExpansion( 346 shill::kNameProperty, 347 base::Value::CreateStringValue(name)); 348 properties->SetWithoutPathExpansion( 349 shill::kDeviceProperty, 350 base::Value::CreateStringValue( 351 shill_stub_helper::DevicePathForType(type))); 352 properties->SetWithoutPathExpansion( 353 shill::kTypeProperty, 354 base::Value::CreateStringValue(type)); 355 properties->SetWithoutPathExpansion( 356 shill::kStateProperty, 357 base::Value::CreateStringValue(state)); 358 if (!ipconfig_path.empty()) 359 properties->SetWithoutPathExpansion( 360 shill::kIPConfigProperty, 361 base::Value::CreateStringValue(ipconfig_path)); 362 } 363 364 void FakeShillServiceClient::RemoveService(const std::string& service_path) { 365 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> 366 RemoveManagerService(service_path); 367 368 stub_services_.RemoveWithoutPathExpansion(service_path, NULL); 369 connect_behavior_.erase(service_path); 370 } 371 372 bool FakeShillServiceClient::SetServiceProperty(const std::string& service_path, 373 const std::string& property, 374 const base::Value& value) { 375 base::DictionaryValue* dict = NULL; 376 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, &dict)) 377 return false; 378 379 VLOG(1) << "Service.SetProperty: " << property << " = " << value 380 << " For: " << service_path; 381 382 base::DictionaryValue new_properties; 383 std::string changed_property; 384 bool case_sensitive = true; 385 if (StartsWithASCII(property, "Provider.", case_sensitive) || 386 StartsWithASCII(property, "OpenVPN.", case_sensitive) || 387 StartsWithASCII(property, "L2TPIPsec.", case_sensitive)) { 388 // These properties are only nested within the Provider dictionary if read 389 // from Shill. 390 base::DictionaryValue* provider = new base::DictionaryValue; 391 provider->SetWithoutPathExpansion(property, value.DeepCopy()); 392 new_properties.SetWithoutPathExpansion(shill::kProviderProperty, provider); 393 changed_property = shill::kProviderProperty; 394 } else { 395 new_properties.SetWithoutPathExpansion(property, value.DeepCopy()); 396 changed_property = property; 397 } 398 399 dict->MergeDictionary(&new_properties); 400 401 if (property == shill::kStateProperty) { 402 // When State changes the sort order of Services may change. 403 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> 404 SortManagerServices(); 405 } 406 407 base::MessageLoop::current()->PostTask( 408 FROM_HERE, 409 base::Bind(&FakeShillServiceClient::NotifyObserversPropertyChanged, 410 weak_ptr_factory_.GetWeakPtr(), 411 dbus::ObjectPath(service_path), changed_property)); 412 return true; 413 } 414 415 const base::DictionaryValue* FakeShillServiceClient::GetServiceProperties( 416 const std::string& service_path) const { 417 const base::DictionaryValue* properties = NULL; 418 stub_services_.GetDictionaryWithoutPathExpansion(service_path, &properties); 419 return properties; 420 } 421 422 void FakeShillServiceClient::ClearServices() { 423 DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> 424 ClearManagerServices(); 425 426 stub_services_.Clear(); 427 connect_behavior_.clear(); 428 } 429 430 void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path, 431 const base::Closure& behavior) { 432 connect_behavior_[service_path] = behavior; 433 } 434 435 void FakeShillServiceClient::NotifyObserversPropertyChanged( 436 const dbus::ObjectPath& service_path, 437 const std::string& property) { 438 base::DictionaryValue* dict = NULL; 439 std::string path = service_path.value(); 440 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &dict)) { 441 LOG(ERROR) << "Notify for unknown service: " << path; 442 return; 443 } 444 base::Value* value = NULL; 445 if (!dict->GetWithoutPathExpansion(property, &value)) { 446 LOG(ERROR) << "Notify for unknown property: " 447 << path << " : " << property; 448 return; 449 } 450 FOR_EACH_OBSERVER(ShillPropertyChangedObserver, 451 GetObserverList(service_path), 452 OnPropertyChanged(property, *value)); 453 } 454 455 base::DictionaryValue* FakeShillServiceClient::GetModifiableServiceProperties( 456 const std::string& service_path, bool create_if_missing) { 457 base::DictionaryValue* properties = NULL; 458 if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path, 459 &properties) && 460 create_if_missing) { 461 properties = new base::DictionaryValue; 462 stub_services_.Set(service_path, properties); 463 } 464 return properties; 465 } 466 467 FakeShillServiceClient::PropertyObserverList& 468 FakeShillServiceClient::GetObserverList(const dbus::ObjectPath& device_path) { 469 std::map<dbus::ObjectPath, PropertyObserverList*>::iterator iter = 470 observer_list_.find(device_path); 471 if (iter != observer_list_.end()) 472 return *(iter->second); 473 PropertyObserverList* observer_list = new PropertyObserverList(); 474 observer_list_[device_path] = observer_list; 475 return *observer_list; 476 } 477 478 void FakeShillServiceClient::SetOtherServicesOffline( 479 const std::string& service_path) { 480 const base::DictionaryValue* service_properties = GetServiceProperties( 481 service_path); 482 if (!service_properties) { 483 LOG(ERROR) << "Missing service: " << service_path; 484 return; 485 } 486 std::string service_type; 487 service_properties->GetString(shill::kTypeProperty, &service_type); 488 // Set all other services of the same type to offline (Idle). 489 for (base::DictionaryValue::Iterator iter(stub_services_); 490 !iter.IsAtEnd(); iter.Advance()) { 491 std::string path = iter.key(); 492 if (path == service_path) 493 continue; 494 base::DictionaryValue* properties; 495 if (!stub_services_.GetDictionaryWithoutPathExpansion(path, &properties)) 496 NOTREACHED(); 497 498 std::string type; 499 properties->GetString(shill::kTypeProperty, &type); 500 if (type != service_type) 501 continue; 502 properties->SetWithoutPathExpansion( 503 shill::kStateProperty, 504 base::Value::CreateStringValue(shill::kStateIdle)); 505 } 506 } 507 508 void FakeShillServiceClient::SetCellularActivated( 509 const dbus::ObjectPath& service_path, 510 const ErrorCallback& error_callback) { 511 SetProperty(service_path, 512 shill::kActivationStateProperty, 513 base::StringValue(shill::kActivationStateActivated), 514 base::Bind(&base::DoNothing), 515 error_callback); 516 SetProperty(service_path, 517 shill::kConnectableProperty, 518 base::FundamentalValue(true), 519 base::Bind(&base::DoNothing), 520 error_callback); 521 } 522 523 void FakeShillServiceClient::ContinueConnect( 524 const std::string& service_path) { 525 VLOG(1) << "FakeShillServiceClient::ContinueConnect: " << service_path; 526 base::DictionaryValue* service_properties = NULL; 527 if (!stub_services_.GetDictionary(service_path, &service_properties)) { 528 LOG(ERROR) << "Service not found: " << service_path; 529 return; 530 } 531 532 if (ContainsKey(connect_behavior_, service_path)) { 533 const base::Closure& custom_connect_behavior = 534 connect_behavior_[service_path]; 535 custom_connect_behavior.Run(); 536 return; 537 } 538 539 // No custom connect behavior set, continue with the default connect behavior. 540 std::string passphrase; 541 service_properties->GetStringWithoutPathExpansion( 542 shill::kPassphraseProperty, &passphrase); 543 if (passphrase == "failure") { 544 // Simulate a password failure. 545 SetServiceProperty(service_path, 546 shill::kStateProperty, 547 base::StringValue(shill::kStateFailure)); 548 base::MessageLoop::current()->PostTask( 549 FROM_HERE, 550 base::Bind( 551 base::IgnoreResult(&FakeShillServiceClient::SetServiceProperty), 552 weak_ptr_factory_.GetWeakPtr(), 553 service_path, 554 shill::kErrorProperty, 555 base::StringValue(shill::kErrorBadPassphrase))); 556 } else { 557 // Set Online. 558 SetServiceProperty(service_path, 559 shill::kStateProperty, 560 base::StringValue(shill::kStateOnline)); 561 } 562 } 563 564 } // namespace chromeos 565