1 // Copyright (c) 2012 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 "dbus/test_service.h" 6 7 #include "base/bind.h" 8 #include "base/test/test_timeouts.h" 9 #include "base/threading/platform_thread.h" 10 #include "dbus/bus.h" 11 #include "dbus/exported_object.h" 12 #include "dbus/message.h" 13 #include "dbus/object_manager.h" 14 #include "dbus/object_path.h" 15 #include "dbus/property.h" 16 17 namespace { 18 19 void EmptyCallback(bool /* success */) { 20 } 21 22 } // namespace 23 24 namespace dbus { 25 26 // Echo, SlowEcho, AsyncEcho, BrokenMethod, GetAll, Get, Set, PerformAction, 27 // GetManagedObjects. 28 const int TestService::kNumMethodsToExport = 9; 29 30 TestService::Options::Options() 31 : request_ownership_options(Bus::REQUIRE_PRIMARY) { 32 } 33 34 TestService::Options::~Options() { 35 } 36 37 TestService::TestService(const Options& options) 38 : base::Thread("TestService"), 39 request_ownership_options_(options.request_ownership_options), 40 dbus_task_runner_(options.dbus_task_runner), 41 on_all_methods_exported_(false, false), 42 num_exported_methods_(0) { 43 } 44 45 TestService::~TestService() { 46 Stop(); 47 } 48 49 bool TestService::StartService() { 50 base::Thread::Options thread_options; 51 thread_options.message_loop_type = base::MessageLoop::TYPE_IO; 52 return StartWithOptions(thread_options); 53 } 54 55 bool TestService::WaitUntilServiceIsStarted() { 56 const base::TimeDelta timeout(TestTimeouts::action_max_timeout()); 57 // Wait until all methods are exported. 58 return on_all_methods_exported_.TimedWait(timeout); 59 } 60 61 void TestService::ShutdownAndBlock() { 62 message_loop()->PostTask( 63 FROM_HERE, 64 base::Bind(&TestService::ShutdownAndBlockInternal, 65 base::Unretained(this))); 66 } 67 68 bool TestService::HasDBusThread() { 69 return bus_->HasDBusThread(); 70 } 71 72 void TestService::ShutdownAndBlockInternal() { 73 if (HasDBusThread()) 74 bus_->ShutdownOnDBusThreadAndBlock(); 75 else 76 bus_->ShutdownAndBlock(); 77 } 78 79 void TestService::SendTestSignal(const std::string& message) { 80 message_loop()->PostTask( 81 FROM_HERE, 82 base::Bind(&TestService::SendTestSignalInternal, 83 base::Unretained(this), 84 message)); 85 } 86 87 void TestService::SendTestSignalFromRoot(const std::string& message) { 88 message_loop()->PostTask( 89 FROM_HERE, 90 base::Bind(&TestService::SendTestSignalFromRootInternal, 91 base::Unretained(this), 92 message)); 93 } 94 95 void TestService::SendTestSignalInternal(const std::string& message) { 96 Signal signal("org.chromium.TestInterface", "Test"); 97 MessageWriter writer(&signal); 98 writer.AppendString(message); 99 exported_object_->SendSignal(&signal); 100 } 101 102 void TestService::SendTestSignalFromRootInternal(const std::string& message) { 103 Signal signal("org.chromium.TestInterface", "Test"); 104 MessageWriter writer(&signal); 105 writer.AppendString(message); 106 107 bus_->RequestOwnership("org.chromium.TestService", 108 request_ownership_options_, 109 base::Bind(&TestService::OnOwnership, 110 base::Unretained(this), 111 base::Bind(&EmptyCallback))); 112 113 // Use "/" just like dbus-send does. 114 ExportedObject* root_object = bus_->GetExportedObject(ObjectPath("/")); 115 root_object->SendSignal(&signal); 116 } 117 118 void TestService::RequestOwnership(base::Callback<void(bool)> callback) { 119 message_loop()->PostTask( 120 FROM_HERE, 121 base::Bind(&TestService::RequestOwnershipInternal, 122 base::Unretained(this), 123 callback)); 124 } 125 126 void TestService::RequestOwnershipInternal( 127 base::Callback<void(bool)> callback) { 128 bus_->RequestOwnership("org.chromium.TestService", 129 request_ownership_options_, 130 base::Bind(&TestService::OnOwnership, 131 base::Unretained(this), 132 callback)); 133 } 134 135 void TestService::OnOwnership(base::Callback<void(bool)> callback, 136 const std::string& service_name, 137 bool success) { 138 has_ownership_ = success; 139 LOG_IF(ERROR, !success) << "Failed to own: " << service_name; 140 callback.Run(success); 141 } 142 143 void TestService::OnExported(const std::string& interface_name, 144 const std::string& method_name, 145 bool success) { 146 if (!success) { 147 LOG(ERROR) << "Failed to export: " << interface_name << "." 148 << method_name; 149 // Returning here will make WaitUntilServiceIsStarted() to time out 150 // and return false. 151 return; 152 } 153 154 ++num_exported_methods_; 155 if (num_exported_methods_ == kNumMethodsToExport) 156 on_all_methods_exported_.Signal(); 157 } 158 159 void TestService::Run(base::MessageLoop* message_loop) { 160 Bus::Options bus_options; 161 bus_options.bus_type = Bus::SESSION; 162 bus_options.connection_type = Bus::PRIVATE; 163 bus_options.dbus_task_runner = dbus_task_runner_; 164 bus_ = new Bus(bus_options); 165 166 bus_->RequestOwnership("org.chromium.TestService", 167 request_ownership_options_, 168 base::Bind(&TestService::OnOwnership, 169 base::Unretained(this), 170 base::Bind(&EmptyCallback))); 171 172 exported_object_ = bus_->GetExportedObject( 173 ObjectPath("/org/chromium/TestObject")); 174 175 int num_methods = 0; 176 exported_object_->ExportMethod( 177 "org.chromium.TestInterface", 178 "Echo", 179 base::Bind(&TestService::Echo, 180 base::Unretained(this)), 181 base::Bind(&TestService::OnExported, 182 base::Unretained(this))); 183 ++num_methods; 184 185 exported_object_->ExportMethod( 186 "org.chromium.TestInterface", 187 "SlowEcho", 188 base::Bind(&TestService::SlowEcho, 189 base::Unretained(this)), 190 base::Bind(&TestService::OnExported, 191 base::Unretained(this))); 192 ++num_methods; 193 194 exported_object_->ExportMethod( 195 "org.chromium.TestInterface", 196 "AsyncEcho", 197 base::Bind(&TestService::AsyncEcho, 198 base::Unretained(this)), 199 base::Bind(&TestService::OnExported, 200 base::Unretained(this))); 201 ++num_methods; 202 203 exported_object_->ExportMethod( 204 "org.chromium.TestInterface", 205 "BrokenMethod", 206 base::Bind(&TestService::BrokenMethod, 207 base::Unretained(this)), 208 base::Bind(&TestService::OnExported, 209 base::Unretained(this))); 210 ++num_methods; 211 212 exported_object_->ExportMethod( 213 "org.chromium.TestInterface", 214 "PerformAction", 215 base::Bind(&TestService::PerformAction, 216 base::Unretained(this)), 217 base::Bind(&TestService::OnExported, 218 base::Unretained(this))); 219 ++num_methods; 220 221 exported_object_->ExportMethod( 222 kPropertiesInterface, 223 kPropertiesGetAll, 224 base::Bind(&TestService::GetAllProperties, 225 base::Unretained(this)), 226 base::Bind(&TestService::OnExported, 227 base::Unretained(this))); 228 ++num_methods; 229 230 exported_object_->ExportMethod( 231 kPropertiesInterface, 232 kPropertiesGet, 233 base::Bind(&TestService::GetProperty, 234 base::Unretained(this)), 235 base::Bind(&TestService::OnExported, 236 base::Unretained(this))); 237 ++num_methods; 238 239 exported_object_->ExportMethod( 240 kPropertiesInterface, 241 kPropertiesSet, 242 base::Bind(&TestService::SetProperty, 243 base::Unretained(this)), 244 base::Bind(&TestService::OnExported, 245 base::Unretained(this))); 246 ++num_methods; 247 248 exported_object_manager_ = bus_->GetExportedObject( 249 ObjectPath("/org/chromium/TestService")); 250 251 exported_object_manager_->ExportMethod( 252 kObjectManagerInterface, 253 kObjectManagerGetManagedObjects, 254 base::Bind(&TestService::GetManagedObjects, 255 base::Unretained(this)), 256 base::Bind(&TestService::OnExported, 257 base::Unretained(this))); 258 ++num_methods; 259 260 // Just print an error message as we don't want to crash tests. 261 // Tests will fail at a call to WaitUntilServiceIsStarted(). 262 if (num_methods != kNumMethodsToExport) { 263 LOG(ERROR) << "The number of methods does not match"; 264 } 265 message_loop->Run(); 266 } 267 268 void TestService::Echo(MethodCall* method_call, 269 ExportedObject::ResponseSender response_sender) { 270 MessageReader reader(method_call); 271 std::string text_message; 272 if (!reader.PopString(&text_message)) { 273 response_sender.Run(scoped_ptr<Response>()); 274 return; 275 } 276 277 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 278 MessageWriter writer(response.get()); 279 writer.AppendString(text_message); 280 response_sender.Run(response.Pass()); 281 } 282 283 void TestService::SlowEcho(MethodCall* method_call, 284 ExportedObject::ResponseSender response_sender) { 285 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); 286 Echo(method_call, response_sender); 287 } 288 289 void TestService::AsyncEcho(MethodCall* method_call, 290 ExportedObject::ResponseSender response_sender) { 291 // Schedule a call to Echo() to send an asynchronous response after we return. 292 message_loop()->PostDelayedTask(FROM_HERE, 293 base::Bind(&TestService::Echo, 294 base::Unretained(this), 295 method_call, 296 response_sender), 297 TestTimeouts::tiny_timeout()); 298 } 299 300 void TestService::BrokenMethod(MethodCall* method_call, 301 ExportedObject::ResponseSender response_sender) { 302 response_sender.Run(scoped_ptr<Response>()); 303 } 304 305 306 void TestService::GetAllProperties( 307 MethodCall* method_call, 308 ExportedObject::ResponseSender response_sender) { 309 MessageReader reader(method_call); 310 std::string interface; 311 if (!reader.PopString(&interface)) { 312 response_sender.Run(scoped_ptr<Response>()); 313 return; 314 } 315 316 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 317 MessageWriter writer(response.get()); 318 319 AddPropertiesToWriter(&writer); 320 321 response_sender.Run(response.Pass()); 322 } 323 324 void TestService::GetProperty(MethodCall* method_call, 325 ExportedObject::ResponseSender response_sender) { 326 MessageReader reader(method_call); 327 std::string interface; 328 if (!reader.PopString(&interface)) { 329 response_sender.Run(scoped_ptr<Response>()); 330 return; 331 } 332 333 std::string name; 334 if (!reader.PopString(&name)) { 335 response_sender.Run(scoped_ptr<Response>()); 336 return; 337 } 338 339 if (name == "Name") { 340 // Return the previous value for the "Name" property: 341 // Variant<"TestService"> 342 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 343 MessageWriter writer(response.get()); 344 345 writer.AppendVariantOfString("TestService"); 346 347 response_sender.Run(response.Pass()); 348 } else if (name == "Version") { 349 // Return a new value for the "Version" property: 350 // Variant<20> 351 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 352 MessageWriter writer(response.get()); 353 354 writer.AppendVariantOfInt16(20); 355 356 response_sender.Run(response.Pass()); 357 } else if (name == "Methods") { 358 // Return the previous value for the "Methods" property: 359 // Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]> 360 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 361 MessageWriter writer(response.get()); 362 MessageWriter variant_writer(NULL); 363 MessageWriter variant_array_writer(NULL); 364 365 writer.OpenVariant("as", &variant_writer); 366 variant_writer.OpenArray("s", &variant_array_writer); 367 variant_array_writer.AppendString("Echo"); 368 variant_array_writer.AppendString("SlowEcho"); 369 variant_array_writer.AppendString("AsyncEcho"); 370 variant_array_writer.AppendString("BrokenMethod"); 371 variant_writer.CloseContainer(&variant_array_writer); 372 writer.CloseContainer(&variant_writer); 373 374 response_sender.Run(response.Pass()); 375 } else if (name == "Objects") { 376 // Return the previous value for the "Objects" property: 377 // Variant<[objectpath:"/TestObjectPath"]> 378 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 379 MessageWriter writer(response.get()); 380 MessageWriter variant_writer(NULL); 381 MessageWriter variant_array_writer(NULL); 382 383 writer.OpenVariant("ao", &variant_writer); 384 variant_writer.OpenArray("o", &variant_array_writer); 385 variant_array_writer.AppendObjectPath(ObjectPath("/TestObjectPath")); 386 variant_writer.CloseContainer(&variant_array_writer); 387 writer.CloseContainer(&variant_writer); 388 389 response_sender.Run(response.Pass()); 390 } else { 391 // Return error. 392 response_sender.Run(scoped_ptr<Response>()); 393 return; 394 } 395 } 396 397 void TestService::SetProperty(MethodCall* method_call, 398 ExportedObject::ResponseSender response_sender) { 399 MessageReader reader(method_call); 400 std::string interface; 401 if (!reader.PopString(&interface)) { 402 response_sender.Run(scoped_ptr<Response>()); 403 return; 404 } 405 406 std::string name; 407 if (!reader.PopString(&name)) { 408 response_sender.Run(scoped_ptr<Response>()); 409 return; 410 } 411 412 if (name != "Name") { 413 response_sender.Run(scoped_ptr<Response>()); 414 return; 415 } 416 417 std::string value; 418 if (!reader.PopVariantOfString(&value)) { 419 response_sender.Run(scoped_ptr<Response>()); 420 return; 421 } 422 423 SendPropertyChangedSignal(value); 424 425 response_sender.Run(Response::FromMethodCall(method_call)); 426 } 427 428 void TestService::PerformAction( 429 MethodCall* method_call, 430 ExportedObject::ResponseSender response_sender) { 431 MessageReader reader(method_call); 432 std::string action; 433 ObjectPath object_path; 434 if (!reader.PopString(&action) || !reader.PopObjectPath(&object_path)) { 435 response_sender.Run(scoped_ptr<Response>()); 436 return; 437 } 438 439 if (action == "AddObject") 440 AddObject(object_path); 441 else if (action == "RemoveObject") 442 RemoveObject(object_path); 443 444 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 445 response_sender.Run(response.Pass()); 446 } 447 448 void TestService::GetManagedObjects( 449 MethodCall* method_call, 450 ExportedObject::ResponseSender response_sender) { 451 scoped_ptr<Response> response = Response::FromMethodCall(method_call); 452 MessageWriter writer(response.get()); 453 454 // The managed objects response is a dictionary of object paths identifying 455 // the object(s) with a dictionary of strings identifying the interface(s) 456 // they implement and then a dictionary of property values. 457 // 458 // Thus this looks something like: 459 // 460 // { 461 // "/org/chromium/TestObject": { 462 // "org.chromium.TestInterface": { /* Properties */ } 463 // } 464 // } 465 466 467 MessageWriter array_writer(NULL); 468 MessageWriter dict_entry_writer(NULL); 469 MessageWriter object_array_writer(NULL); 470 MessageWriter object_dict_entry_writer(NULL); 471 472 writer.OpenArray("{oa{sa{sv}}}", &array_writer); 473 474 array_writer.OpenDictEntry(&dict_entry_writer); 475 dict_entry_writer.AppendObjectPath(ObjectPath("/org/chromium/TestObject")); 476 dict_entry_writer.OpenArray("{sa{sv}}", &object_array_writer); 477 478 object_array_writer.OpenDictEntry(&object_dict_entry_writer); 479 object_dict_entry_writer.AppendString("org.chromium.TestInterface"); 480 AddPropertiesToWriter(&object_dict_entry_writer); 481 object_array_writer.CloseContainer(&object_dict_entry_writer); 482 483 dict_entry_writer.CloseContainer(&object_array_writer); 484 485 array_writer.CloseContainer(&dict_entry_writer); 486 writer.CloseContainer(&array_writer); 487 488 response_sender.Run(response.Pass()); 489 } 490 491 void TestService::AddPropertiesToWriter(MessageWriter* writer) { 492 // The properties response is a dictionary of strings identifying the 493 // property and a variant containing the property value. We return all 494 // of the properties, thus the response is: 495 // 496 // { 497 // "Name": Variant<"TestService">, 498 // "Version": Variant<10>, 499 // "Methods": Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>, 500 // "Objects": Variant<[objectpath:"/TestObjectPath"]> 501 // } 502 503 MessageWriter array_writer(NULL); 504 MessageWriter dict_entry_writer(NULL); 505 MessageWriter variant_writer(NULL); 506 MessageWriter variant_array_writer(NULL); 507 508 writer->OpenArray("{sv}", &array_writer); 509 510 array_writer.OpenDictEntry(&dict_entry_writer); 511 dict_entry_writer.AppendString("Name"); 512 dict_entry_writer.AppendVariantOfString("TestService"); 513 array_writer.CloseContainer(&dict_entry_writer); 514 515 array_writer.OpenDictEntry(&dict_entry_writer); 516 dict_entry_writer.AppendString("Version"); 517 dict_entry_writer.AppendVariantOfInt16(10); 518 array_writer.CloseContainer(&dict_entry_writer); 519 520 array_writer.OpenDictEntry(&dict_entry_writer); 521 dict_entry_writer.AppendString("Methods"); 522 dict_entry_writer.OpenVariant("as", &variant_writer); 523 variant_writer.OpenArray("s", &variant_array_writer); 524 variant_array_writer.AppendString("Echo"); 525 variant_array_writer.AppendString("SlowEcho"); 526 variant_array_writer.AppendString("AsyncEcho"); 527 variant_array_writer.AppendString("BrokenMethod"); 528 variant_writer.CloseContainer(&variant_array_writer); 529 dict_entry_writer.CloseContainer(&variant_writer); 530 array_writer.CloseContainer(&dict_entry_writer); 531 532 array_writer.OpenDictEntry(&dict_entry_writer); 533 dict_entry_writer.AppendString("Objects"); 534 dict_entry_writer.OpenVariant("ao", &variant_writer); 535 variant_writer.OpenArray("o", &variant_array_writer); 536 variant_array_writer.AppendObjectPath(ObjectPath("/TestObjectPath")); 537 variant_writer.CloseContainer(&variant_array_writer); 538 dict_entry_writer.CloseContainer(&variant_writer); 539 array_writer.CloseContainer(&dict_entry_writer); 540 541 writer->CloseContainer(&array_writer); 542 } 543 544 void TestService::AddObject(const ObjectPath& object_path) { 545 message_loop()->PostTask( 546 FROM_HERE, 547 base::Bind(&TestService::AddObjectInternal, 548 base::Unretained(this), 549 object_path)); 550 } 551 552 void TestService::AddObjectInternal(const ObjectPath& object_path) { 553 Signal signal(kObjectManagerInterface, kObjectManagerInterfacesAdded); 554 MessageWriter writer(&signal); 555 writer.AppendObjectPath(object_path); 556 557 MessageWriter array_writer(NULL); 558 MessageWriter dict_entry_writer(NULL); 559 560 writer.OpenArray("{sa{sv}}", &array_writer); 561 array_writer.OpenDictEntry(&dict_entry_writer); 562 dict_entry_writer.AppendString("org.chromium.TestInterface"); 563 AddPropertiesToWriter(&dict_entry_writer); 564 array_writer.CloseContainer(&dict_entry_writer); 565 writer.CloseContainer(&array_writer); 566 567 exported_object_manager_->SendSignal(&signal); 568 } 569 570 void TestService::RemoveObject(const ObjectPath& object_path) { 571 message_loop()->PostTask(FROM_HERE, 572 base::Bind(&TestService::RemoveObjectInternal, 573 base::Unretained(this), 574 object_path)); 575 } 576 577 void TestService::RemoveObjectInternal(const ObjectPath& object_path) { 578 Signal signal(kObjectManagerInterface, kObjectManagerInterfacesRemoved); 579 MessageWriter writer(&signal); 580 581 writer.AppendObjectPath(object_path); 582 583 std::vector<std::string> interfaces; 584 interfaces.push_back("org.chromium.TestInterface"); 585 writer.AppendArrayOfStrings(interfaces); 586 587 exported_object_manager_->SendSignal(&signal); 588 } 589 590 void TestService::SendPropertyChangedSignal(const std::string& name) { 591 message_loop()->PostTask( 592 FROM_HERE, 593 base::Bind(&TestService::SendPropertyChangedSignalInternal, 594 base::Unretained(this), 595 name)); 596 } 597 598 void TestService::SendPropertyChangedSignalInternal(const std::string& name) { 599 Signal signal(kPropertiesInterface, kPropertiesChanged); 600 MessageWriter writer(&signal); 601 writer.AppendString("org.chromium.TestInterface"); 602 603 MessageWriter array_writer(NULL); 604 MessageWriter dict_entry_writer(NULL); 605 606 writer.OpenArray("{sv}", &array_writer); 607 array_writer.OpenDictEntry(&dict_entry_writer); 608 dict_entry_writer.AppendString("Name"); 609 dict_entry_writer.AppendVariantOfString(name); 610 array_writer.CloseContainer(&dict_entry_writer); 611 writer.CloseContainer(&array_writer); 612 613 exported_object_->SendSignal(&signal); 614 } 615 616 } // namespace dbus 617