Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / dbus / test_service.cc
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