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_name_obtained_(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 the ownership of the service name is obtained.
     58   return on_name_obtained_.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   on_name_obtained_.Signal();
    143 }
    144 
    145 void TestService::ReleaseOwnership(base::Closure callback) {
    146   bus_->GetDBusTaskRunner()->PostTask(
    147       FROM_HERE,
    148       base::Bind(&TestService::ReleaseOwnershipInternal,
    149                  base::Unretained(this),
    150                  callback));
    151 }
    152 
    153 void TestService::ReleaseOwnershipInternal(
    154     base::Closure callback) {
    155   bus_->ReleaseOwnership("org.chromium.TestService");
    156   has_ownership_ = false;
    157 
    158   bus_->GetOriginTaskRunner()->PostTask(
    159       FROM_HERE,
    160       callback);
    161 }
    162 
    163 void TestService::OnExported(const std::string& interface_name,
    164                              const std::string& method_name,
    165                              bool success) {
    166   if (!success) {
    167     LOG(ERROR) << "Failed to export: " << interface_name << "."
    168                << method_name;
    169     // Returning here will make WaitUntilServiceIsStarted() to time out
    170     // and return false.
    171     return;
    172   }
    173 
    174   ++num_exported_methods_;
    175   if (num_exported_methods_ == kNumMethodsToExport) {
    176     // As documented in exported_object.h, the service name should be
    177     // requested after all methods are exposed.
    178     bus_->RequestOwnership("org.chromium.TestService",
    179                            request_ownership_options_,
    180                            base::Bind(&TestService::OnOwnership,
    181                                       base::Unretained(this),
    182                                       base::Bind(&EmptyCallback)));
    183   }
    184 }
    185 
    186 void TestService::Run(base::MessageLoop* message_loop) {
    187   Bus::Options bus_options;
    188   bus_options.bus_type = Bus::SESSION;
    189   bus_options.connection_type = Bus::PRIVATE;
    190   bus_options.dbus_task_runner = dbus_task_runner_;
    191   bus_ = new Bus(bus_options);
    192 
    193   exported_object_ = bus_->GetExportedObject(
    194       ObjectPath("/org/chromium/TestObject"));
    195 
    196   int num_methods = 0;
    197   exported_object_->ExportMethod(
    198       "org.chromium.TestInterface",
    199       "Echo",
    200       base::Bind(&TestService::Echo,
    201                  base::Unretained(this)),
    202       base::Bind(&TestService::OnExported,
    203                  base::Unretained(this)));
    204   ++num_methods;
    205 
    206   exported_object_->ExportMethod(
    207       "org.chromium.TestInterface",
    208       "SlowEcho",
    209       base::Bind(&TestService::SlowEcho,
    210                  base::Unretained(this)),
    211       base::Bind(&TestService::OnExported,
    212                  base::Unretained(this)));
    213   ++num_methods;
    214 
    215   exported_object_->ExportMethod(
    216       "org.chromium.TestInterface",
    217       "AsyncEcho",
    218       base::Bind(&TestService::AsyncEcho,
    219                  base::Unretained(this)),
    220       base::Bind(&TestService::OnExported,
    221                  base::Unretained(this)));
    222   ++num_methods;
    223 
    224   exported_object_->ExportMethod(
    225       "org.chromium.TestInterface",
    226       "BrokenMethod",
    227       base::Bind(&TestService::BrokenMethod,
    228                  base::Unretained(this)),
    229       base::Bind(&TestService::OnExported,
    230                  base::Unretained(this)));
    231   ++num_methods;
    232 
    233   exported_object_->ExportMethod(
    234       "org.chromium.TestInterface",
    235       "PerformAction",
    236       base::Bind(&TestService::PerformAction,
    237                  base::Unretained(this)),
    238       base::Bind(&TestService::OnExported,
    239                  base::Unretained(this)));
    240   ++num_methods;
    241 
    242   exported_object_->ExportMethod(
    243        kPropertiesInterface,
    244        kPropertiesGetAll,
    245        base::Bind(&TestService::GetAllProperties,
    246                   base::Unretained(this)),
    247        base::Bind(&TestService::OnExported,
    248                   base::Unretained(this)));
    249   ++num_methods;
    250 
    251   exported_object_->ExportMethod(
    252        kPropertiesInterface,
    253        kPropertiesGet,
    254        base::Bind(&TestService::GetProperty,
    255                   base::Unretained(this)),
    256        base::Bind(&TestService::OnExported,
    257                   base::Unretained(this)));
    258   ++num_methods;
    259 
    260   exported_object_->ExportMethod(
    261        kPropertiesInterface,
    262        kPropertiesSet,
    263        base::Bind(&TestService::SetProperty,
    264                   base::Unretained(this)),
    265        base::Bind(&TestService::OnExported,
    266                   base::Unretained(this)));
    267   ++num_methods;
    268 
    269   exported_object_manager_ = bus_->GetExportedObject(
    270       ObjectPath("/org/chromium/TestService"));
    271 
    272   exported_object_manager_->ExportMethod(
    273        kObjectManagerInterface,
    274        kObjectManagerGetManagedObjects,
    275        base::Bind(&TestService::GetManagedObjects,
    276                   base::Unretained(this)),
    277        base::Bind(&TestService::OnExported,
    278                   base::Unretained(this)));
    279   ++num_methods;
    280 
    281   // Just print an error message as we don't want to crash tests.
    282   // Tests will fail at a call to WaitUntilServiceIsStarted().
    283   if (num_methods != kNumMethodsToExport) {
    284     LOG(ERROR) << "The number of methods does not match";
    285   }
    286   message_loop->Run();
    287 }
    288 
    289 void TestService::Echo(MethodCall* method_call,
    290                        ExportedObject::ResponseSender response_sender) {
    291   MessageReader reader(method_call);
    292   std::string text_message;
    293   if (!reader.PopString(&text_message)) {
    294     response_sender.Run(scoped_ptr<Response>());
    295     return;
    296   }
    297 
    298   scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    299   MessageWriter writer(response.get());
    300   writer.AppendString(text_message);
    301   response_sender.Run(response.Pass());
    302 }
    303 
    304 void TestService::SlowEcho(MethodCall* method_call,
    305                            ExportedObject::ResponseSender response_sender) {
    306   base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
    307   Echo(method_call, response_sender);
    308 }
    309 
    310 void TestService::AsyncEcho(MethodCall* method_call,
    311                             ExportedObject::ResponseSender response_sender) {
    312   // Schedule a call to Echo() to send an asynchronous response after we return.
    313   message_loop()->PostDelayedTask(FROM_HERE,
    314                                   base::Bind(&TestService::Echo,
    315                                              base::Unretained(this),
    316                                              method_call,
    317                                              response_sender),
    318                                   TestTimeouts::tiny_timeout());
    319 }
    320 
    321 void TestService::BrokenMethod(MethodCall* method_call,
    322                                ExportedObject::ResponseSender response_sender) {
    323   response_sender.Run(scoped_ptr<Response>());
    324 }
    325 
    326 
    327 void TestService::GetAllProperties(
    328     MethodCall* method_call,
    329     ExportedObject::ResponseSender response_sender) {
    330   MessageReader reader(method_call);
    331   std::string interface;
    332   if (!reader.PopString(&interface)) {
    333     response_sender.Run(scoped_ptr<Response>());
    334     return;
    335   }
    336 
    337   scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    338   MessageWriter writer(response.get());
    339 
    340   AddPropertiesToWriter(&writer);
    341 
    342   response_sender.Run(response.Pass());
    343 }
    344 
    345 void TestService::GetProperty(MethodCall* method_call,
    346                               ExportedObject::ResponseSender response_sender) {
    347   MessageReader reader(method_call);
    348   std::string interface;
    349   if (!reader.PopString(&interface)) {
    350     response_sender.Run(scoped_ptr<Response>());
    351     return;
    352   }
    353 
    354   std::string name;
    355   if (!reader.PopString(&name)) {
    356     response_sender.Run(scoped_ptr<Response>());
    357     return;
    358   }
    359 
    360   if (name == "Name") {
    361     // Return the previous value for the "Name" property:
    362     // Variant<"TestService">
    363     scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    364     MessageWriter writer(response.get());
    365 
    366     writer.AppendVariantOfString("TestService");
    367 
    368     response_sender.Run(response.Pass());
    369   } else if (name == "Version") {
    370     // Return a new value for the "Version" property:
    371     // Variant<20>
    372     scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    373     MessageWriter writer(response.get());
    374 
    375     writer.AppendVariantOfInt16(20);
    376 
    377     response_sender.Run(response.Pass());
    378   } else if (name == "Methods") {
    379     // Return the previous value for the "Methods" property:
    380     // Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>
    381     scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    382     MessageWriter writer(response.get());
    383     MessageWriter variant_writer(NULL);
    384     MessageWriter variant_array_writer(NULL);
    385 
    386     writer.OpenVariant("as", &variant_writer);
    387     variant_writer.OpenArray("s", &variant_array_writer);
    388     variant_array_writer.AppendString("Echo");
    389     variant_array_writer.AppendString("SlowEcho");
    390     variant_array_writer.AppendString("AsyncEcho");
    391     variant_array_writer.AppendString("BrokenMethod");
    392     variant_writer.CloseContainer(&variant_array_writer);
    393     writer.CloseContainer(&variant_writer);
    394 
    395     response_sender.Run(response.Pass());
    396   } else if (name == "Objects") {
    397     // Return the previous value for the "Objects" property:
    398     // Variant<[objectpath:"/TestObjectPath"]>
    399     scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    400     MessageWriter writer(response.get());
    401     MessageWriter variant_writer(NULL);
    402     MessageWriter variant_array_writer(NULL);
    403 
    404     writer.OpenVariant("ao", &variant_writer);
    405     variant_writer.OpenArray("o", &variant_array_writer);
    406     variant_array_writer.AppendObjectPath(ObjectPath("/TestObjectPath"));
    407     variant_writer.CloseContainer(&variant_array_writer);
    408     writer.CloseContainer(&variant_writer);
    409 
    410     response_sender.Run(response.Pass());
    411   } else if (name == "Bytes") {
    412     // Return the previous value for the "Bytes" property:
    413     // Variant<[0x54, 0x65, 0x73, 0x74]>
    414     scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    415     MessageWriter writer(response.get());
    416     MessageWriter variant_writer(NULL);
    417     MessageWriter variant_array_writer(NULL);
    418 
    419     writer.OpenVariant("ay", &variant_writer);
    420     const uint8 bytes[] = { 0x54, 0x65, 0x73, 0x74 };
    421     variant_writer.AppendArrayOfBytes(bytes, sizeof(bytes));
    422     writer.CloseContainer(&variant_writer);
    423 
    424     response_sender.Run(response.Pass());
    425   } else {
    426     // Return error.
    427     response_sender.Run(scoped_ptr<Response>());
    428     return;
    429   }
    430 }
    431 
    432 void TestService::SetProperty(MethodCall* method_call,
    433                               ExportedObject::ResponseSender response_sender) {
    434   MessageReader reader(method_call);
    435   std::string interface;
    436   if (!reader.PopString(&interface)) {
    437     response_sender.Run(scoped_ptr<Response>());
    438     return;
    439   }
    440 
    441   std::string name;
    442   if (!reader.PopString(&name)) {
    443     response_sender.Run(scoped_ptr<Response>());
    444     return;
    445   }
    446 
    447   if (name != "Name") {
    448     response_sender.Run(scoped_ptr<Response>());
    449     return;
    450   }
    451 
    452   std::string value;
    453   if (!reader.PopVariantOfString(&value)) {
    454     response_sender.Run(scoped_ptr<Response>());
    455     return;
    456   }
    457 
    458   SendPropertyChangedSignal(value);
    459 
    460   response_sender.Run(Response::FromMethodCall(method_call));
    461 }
    462 
    463 void TestService::PerformAction(
    464       MethodCall* method_call,
    465       ExportedObject::ResponseSender response_sender) {
    466   MessageReader reader(method_call);
    467   std::string action;
    468   ObjectPath object_path;
    469   if (!reader.PopString(&action) || !reader.PopObjectPath(&object_path)) {
    470     response_sender.Run(scoped_ptr<Response>());
    471     return;
    472   }
    473 
    474   if (action == "AddObject")
    475     AddObject(object_path);
    476   else if (action == "RemoveObject")
    477     RemoveObject(object_path);
    478   else if (action == "ReleaseOwnership") {
    479     ReleaseOwnership(base::Bind(&TestService::PerformActionResponse,
    480                                 base::Unretained(this),
    481                                 method_call, response_sender));
    482     return;
    483   } else if (action == "Ownership") {
    484     ReleaseOwnership(base::Bind(&TestService::OwnershipReleased,
    485                                 base::Unretained(this),
    486                                 method_call, response_sender));
    487     return;
    488   }
    489 
    490   scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    491   response_sender.Run(response.Pass());
    492 }
    493 
    494 void TestService::PerformActionResponse(
    495     MethodCall* method_call,
    496     ExportedObject::ResponseSender response_sender) {
    497   scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    498   response_sender.Run(response.Pass());
    499 }
    500 
    501 void TestService::OwnershipReleased(
    502     MethodCall* method_call,
    503     ExportedObject::ResponseSender response_sender) {
    504   RequestOwnership(base::Bind(&TestService::OwnershipRegained,
    505                               base::Unretained(this),
    506                               method_call, response_sender));
    507 }
    508 
    509 
    510 void TestService::OwnershipRegained(
    511     MethodCall* method_call,
    512     ExportedObject::ResponseSender response_sender,
    513     bool success) {
    514   PerformActionResponse(method_call, response_sender);
    515 }
    516 
    517 
    518 void TestService::GetManagedObjects(
    519     MethodCall* method_call,
    520     ExportedObject::ResponseSender response_sender) {
    521   scoped_ptr<Response> response = Response::FromMethodCall(method_call);
    522   MessageWriter writer(response.get());
    523 
    524   // The managed objects response is a dictionary of object paths identifying
    525   // the object(s) with a dictionary of strings identifying the interface(s)
    526   // they implement and then a dictionary of property values.
    527   //
    528   // Thus this looks something like:
    529   //
    530   // {
    531   //   "/org/chromium/TestObject": {
    532   //     "org.chromium.TestInterface": { /* Properties */ }
    533   //   }
    534   // }
    535 
    536 
    537   MessageWriter array_writer(NULL);
    538   MessageWriter dict_entry_writer(NULL);
    539   MessageWriter object_array_writer(NULL);
    540   MessageWriter object_dict_entry_writer(NULL);
    541 
    542   writer.OpenArray("{oa{sa{sv}}}", &array_writer);
    543 
    544   array_writer.OpenDictEntry(&dict_entry_writer);
    545   dict_entry_writer.AppendObjectPath(ObjectPath("/org/chromium/TestObject"));
    546   dict_entry_writer.OpenArray("{sa{sv}}", &object_array_writer);
    547 
    548   object_array_writer.OpenDictEntry(&object_dict_entry_writer);
    549   object_dict_entry_writer.AppendString("org.chromium.TestInterface");
    550   AddPropertiesToWriter(&object_dict_entry_writer);
    551   object_array_writer.CloseContainer(&object_dict_entry_writer);
    552 
    553   dict_entry_writer.CloseContainer(&object_array_writer);
    554 
    555   array_writer.CloseContainer(&dict_entry_writer);
    556   writer.CloseContainer(&array_writer);
    557 
    558   response_sender.Run(response.Pass());
    559 }
    560 
    561 void TestService::AddPropertiesToWriter(MessageWriter* writer) {
    562   // The properties response is a dictionary of strings identifying the
    563   // property and a variant containing the property value. We return all
    564   // of the properties, thus the response is:
    565   //
    566   // {
    567   //   "Name": Variant<"TestService">,
    568   //   "Version": Variant<10>,
    569   //   "Methods": Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>,
    570   //   "Objects": Variant<[objectpath:"/TestObjectPath"]>
    571   //   "Bytes": Variant<[0x54, 0x65, 0x73, 0x74]>
    572   // }
    573 
    574   MessageWriter array_writer(NULL);
    575   MessageWriter dict_entry_writer(NULL);
    576   MessageWriter variant_writer(NULL);
    577   MessageWriter variant_array_writer(NULL);
    578 
    579   writer->OpenArray("{sv}", &array_writer);
    580 
    581   array_writer.OpenDictEntry(&dict_entry_writer);
    582   dict_entry_writer.AppendString("Name");
    583   dict_entry_writer.AppendVariantOfString("TestService");
    584   array_writer.CloseContainer(&dict_entry_writer);
    585 
    586   array_writer.OpenDictEntry(&dict_entry_writer);
    587   dict_entry_writer.AppendString("Version");
    588   dict_entry_writer.AppendVariantOfInt16(10);
    589   array_writer.CloseContainer(&dict_entry_writer);
    590 
    591   array_writer.OpenDictEntry(&dict_entry_writer);
    592   dict_entry_writer.AppendString("Methods");
    593   dict_entry_writer.OpenVariant("as", &variant_writer);
    594   variant_writer.OpenArray("s", &variant_array_writer);
    595   variant_array_writer.AppendString("Echo");
    596   variant_array_writer.AppendString("SlowEcho");
    597   variant_array_writer.AppendString("AsyncEcho");
    598   variant_array_writer.AppendString("BrokenMethod");
    599   variant_writer.CloseContainer(&variant_array_writer);
    600   dict_entry_writer.CloseContainer(&variant_writer);
    601   array_writer.CloseContainer(&dict_entry_writer);
    602 
    603   array_writer.OpenDictEntry(&dict_entry_writer);
    604   dict_entry_writer.AppendString("Objects");
    605   dict_entry_writer.OpenVariant("ao", &variant_writer);
    606   variant_writer.OpenArray("o", &variant_array_writer);
    607   variant_array_writer.AppendObjectPath(ObjectPath("/TestObjectPath"));
    608   variant_writer.CloseContainer(&variant_array_writer);
    609   dict_entry_writer.CloseContainer(&variant_writer);
    610   array_writer.CloseContainer(&dict_entry_writer);
    611 
    612   array_writer.OpenDictEntry(&dict_entry_writer);
    613   dict_entry_writer.AppendString("Bytes");
    614   dict_entry_writer.OpenVariant("ay", &variant_writer);
    615   const uint8 bytes[] = { 0x54, 0x65, 0x73, 0x74 };
    616   variant_writer.AppendArrayOfBytes(bytes, sizeof(bytes));
    617   dict_entry_writer.CloseContainer(&variant_writer);
    618   array_writer.CloseContainer(&dict_entry_writer);
    619 
    620   writer->CloseContainer(&array_writer);
    621 }
    622 
    623 void TestService::AddObject(const ObjectPath& object_path) {
    624   message_loop()->PostTask(
    625       FROM_HERE,
    626       base::Bind(&TestService::AddObjectInternal,
    627                  base::Unretained(this),
    628                  object_path));
    629 }
    630 
    631 void TestService::AddObjectInternal(const ObjectPath& object_path) {
    632   Signal signal(kObjectManagerInterface, kObjectManagerInterfacesAdded);
    633   MessageWriter writer(&signal);
    634   writer.AppendObjectPath(object_path);
    635 
    636   MessageWriter array_writer(NULL);
    637   MessageWriter dict_entry_writer(NULL);
    638 
    639   writer.OpenArray("{sa{sv}}", &array_writer);
    640   array_writer.OpenDictEntry(&dict_entry_writer);
    641   dict_entry_writer.AppendString("org.chromium.TestInterface");
    642   AddPropertiesToWriter(&dict_entry_writer);
    643   array_writer.CloseContainer(&dict_entry_writer);
    644   writer.CloseContainer(&array_writer);
    645 
    646   exported_object_manager_->SendSignal(&signal);
    647 }
    648 
    649 void TestService::RemoveObject(const ObjectPath& object_path) {
    650   message_loop()->PostTask(FROM_HERE,
    651                            base::Bind(&TestService::RemoveObjectInternal,
    652                                       base::Unretained(this),
    653                                       object_path));
    654 }
    655 
    656 void TestService::RemoveObjectInternal(const ObjectPath& object_path) {
    657   Signal signal(kObjectManagerInterface, kObjectManagerInterfacesRemoved);
    658   MessageWriter writer(&signal);
    659 
    660   writer.AppendObjectPath(object_path);
    661 
    662   std::vector<std::string> interfaces;
    663   interfaces.push_back("org.chromium.TestInterface");
    664   writer.AppendArrayOfStrings(interfaces);
    665 
    666   exported_object_manager_->SendSignal(&signal);
    667 }
    668 
    669 void TestService::SendPropertyChangedSignal(const std::string& name) {
    670   message_loop()->PostTask(
    671       FROM_HERE,
    672       base::Bind(&TestService::SendPropertyChangedSignalInternal,
    673                  base::Unretained(this),
    674                  name));
    675 }
    676 
    677 void TestService::SendPropertyChangedSignalInternal(const std::string& name) {
    678   Signal signal(kPropertiesInterface, kPropertiesChanged);
    679   MessageWriter writer(&signal);
    680   writer.AppendString("org.chromium.TestInterface");
    681 
    682   MessageWriter array_writer(NULL);
    683   MessageWriter dict_entry_writer(NULL);
    684 
    685   writer.OpenArray("{sv}", &array_writer);
    686   array_writer.OpenDictEntry(&dict_entry_writer);
    687   dict_entry_writer.AppendString("Name");
    688   dict_entry_writer.AppendVariantOfString(name);
    689   array_writer.CloseContainer(&dict_entry_writer);
    690   writer.CloseContainer(&array_writer);
    691 
    692   exported_object_->SendSignal(&signal);
    693 }
    694 
    695 }  // namespace dbus
    696