Home | History | Annotate | Download | only in dbus
      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