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.
5 #include "dbus/test_service.h"
10 #include "base/bind.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/threading/platform_thread.h"
14 #include "dbus/exported_object.h"
15 #include "dbus/message.h"
16 #include "dbus/object_manager.h"
17 #include "dbus/object_path.h"
18 #include "dbus/property.h"
22 void EmptyCallback(bool /* success */) {
29 // Echo, SlowEcho, AsyncEcho, BrokenMethod, GetAll, Get, Set, PerformAction,
31 const int TestService::kNumMethodsToExport
= 9;
33 TestService::Options::Options()
34 : request_ownership_options(Bus::REQUIRE_PRIMARY
) {
37 TestService::Options::~Options() {
40 TestService::TestService(const Options
& options
)
41 : base::Thread("TestService"),
42 request_ownership_options_(options
.request_ownership_options
),
43 dbus_task_runner_(options
.dbus_task_runner
),
44 on_name_obtained_(false, false),
45 num_exported_methods_(0),
46 send_immediate_properties_changed_(false),
47 has_ownership_(false),
48 exported_object_(NULL
),
49 exported_object_manager_(NULL
) {
52 TestService::~TestService() {
56 bool TestService::StartService() {
57 base::Thread::Options thread_options
;
58 thread_options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
59 return StartWithOptions(thread_options
);
62 bool TestService::WaitUntilServiceIsStarted() {
63 const base::TimeDelta
timeout(TestTimeouts::action_max_timeout());
64 // Wait until the ownership of the service name is obtained.
65 return on_name_obtained_
.TimedWait(timeout
);
68 void TestService::ShutdownAndBlock() {
69 message_loop()->PostTask(
71 base::Bind(&TestService::ShutdownAndBlockInternal
,
72 base::Unretained(this)));
75 bool TestService::HasDBusThread() {
76 return bus_
->HasDBusThread();
79 void TestService::ShutdownAndBlockInternal() {
81 bus_
->ShutdownOnDBusThreadAndBlock();
83 bus_
->ShutdownAndBlock();
86 void TestService::SendTestSignal(const std::string
& message
) {
87 message_loop()->PostTask(
89 base::Bind(&TestService::SendTestSignalInternal
,
90 base::Unretained(this),
94 void TestService::SendTestSignalFromRoot(const std::string
& message
) {
95 message_loop()->PostTask(
97 base::Bind(&TestService::SendTestSignalFromRootInternal
,
98 base::Unretained(this),
102 void TestService::SendTestSignalInternal(const std::string
& message
) {
103 Signal
signal("org.chromium.TestInterface", "Test");
104 MessageWriter
writer(&signal
);
105 writer
.AppendString(message
);
106 exported_object_
->SendSignal(&signal
);
109 void TestService::SendTestSignalFromRootInternal(const std::string
& message
) {
110 Signal
signal("org.chromium.TestInterface", "Test");
111 MessageWriter
writer(&signal
);
112 writer
.AppendString(message
);
114 bus_
->RequestOwnership("org.chromium.TestService",
115 request_ownership_options_
,
116 base::Bind(&TestService::OnOwnership
,
117 base::Unretained(this),
118 base::Bind(&EmptyCallback
)));
120 // Use "/" just like dbus-send does.
121 ExportedObject
* root_object
= bus_
->GetExportedObject(ObjectPath("/"));
122 root_object
->SendSignal(&signal
);
125 void TestService::RequestOwnership(base::Callback
<void(bool)> callback
) {
126 message_loop()->PostTask(
128 base::Bind(&TestService::RequestOwnershipInternal
,
129 base::Unretained(this),
133 void TestService::RequestOwnershipInternal(
134 base::Callback
<void(bool)> callback
) {
135 bus_
->RequestOwnership("org.chromium.TestService",
136 request_ownership_options_
,
137 base::Bind(&TestService::OnOwnership
,
138 base::Unretained(this),
142 void TestService::OnOwnership(base::Callback
<void(bool)> callback
,
143 const std::string
& service_name
,
145 has_ownership_
= success
;
146 LOG_IF(ERROR
, !success
) << "Failed to own: " << service_name
;
147 callback
.Run(success
);
149 on_name_obtained_
.Signal();
152 void TestService::ReleaseOwnership(base::Closure callback
) {
153 bus_
->GetDBusTaskRunner()->PostTask(
155 base::Bind(&TestService::ReleaseOwnershipInternal
,
156 base::Unretained(this),
160 void TestService::ReleaseOwnershipInternal(
161 base::Closure callback
) {
162 bus_
->ReleaseOwnership("org.chromium.TestService");
163 has_ownership_
= false;
165 bus_
->GetOriginTaskRunner()->PostTask(
170 void TestService::SetSendImmediatePropertiesChanged() {
171 send_immediate_properties_changed_
= true;
174 void TestService::OnExported(const std::string
& interface_name
,
175 const std::string
& method_name
,
178 LOG(ERROR
) << "Failed to export: " << interface_name
<< "."
180 // Returning here will make WaitUntilServiceIsStarted() to time out
185 ++num_exported_methods_
;
186 if (num_exported_methods_
== kNumMethodsToExport
) {
187 // As documented in exported_object.h, the service name should be
188 // requested after all methods are exposed.
189 bus_
->RequestOwnership("org.chromium.TestService",
190 request_ownership_options_
,
191 base::Bind(&TestService::OnOwnership
,
192 base::Unretained(this),
193 base::Bind(&EmptyCallback
)));
197 void TestService::Run(base::MessageLoop
* message_loop
) {
198 Bus::Options bus_options
;
199 bus_options
.bus_type
= Bus::SESSION
;
200 bus_options
.connection_type
= Bus::PRIVATE
;
201 bus_options
.dbus_task_runner
= dbus_task_runner_
;
202 bus_
= new Bus(bus_options
);
204 exported_object_
= bus_
->GetExportedObject(
205 ObjectPath("/org/chromium/TestObject"));
208 exported_object_
->ExportMethod(
209 "org.chromium.TestInterface",
211 base::Bind(&TestService::Echo
,
212 base::Unretained(this)),
213 base::Bind(&TestService::OnExported
,
214 base::Unretained(this)));
217 exported_object_
->ExportMethod(
218 "org.chromium.TestInterface",
220 base::Bind(&TestService::SlowEcho
,
221 base::Unretained(this)),
222 base::Bind(&TestService::OnExported
,
223 base::Unretained(this)));
226 exported_object_
->ExportMethod(
227 "org.chromium.TestInterface",
229 base::Bind(&TestService::AsyncEcho
,
230 base::Unretained(this)),
231 base::Bind(&TestService::OnExported
,
232 base::Unretained(this)));
235 exported_object_
->ExportMethod(
236 "org.chromium.TestInterface",
238 base::Bind(&TestService::BrokenMethod
,
239 base::Unretained(this)),
240 base::Bind(&TestService::OnExported
,
241 base::Unretained(this)));
244 exported_object_
->ExportMethod(
245 "org.chromium.TestInterface",
247 base::Bind(&TestService::PerformAction
,
248 base::Unretained(this)),
249 base::Bind(&TestService::OnExported
,
250 base::Unretained(this)));
253 exported_object_
->ExportMethod(
254 kPropertiesInterface
,
256 base::Bind(&TestService::GetAllProperties
,
257 base::Unretained(this)),
258 base::Bind(&TestService::OnExported
,
259 base::Unretained(this)));
262 exported_object_
->ExportMethod(
263 kPropertiesInterface
,
265 base::Bind(&TestService::GetProperty
,
266 base::Unretained(this)),
267 base::Bind(&TestService::OnExported
,
268 base::Unretained(this)));
271 exported_object_
->ExportMethod(
272 kPropertiesInterface
,
274 base::Bind(&TestService::SetProperty
,
275 base::Unretained(this)),
276 base::Bind(&TestService::OnExported
,
277 base::Unretained(this)));
280 exported_object_manager_
= bus_
->GetExportedObject(
281 ObjectPath("/org/chromium/TestService"));
283 exported_object_manager_
->ExportMethod(
284 kObjectManagerInterface
,
285 kObjectManagerGetManagedObjects
,
286 base::Bind(&TestService::GetManagedObjects
,
287 base::Unretained(this)),
288 base::Bind(&TestService::OnExported
,
289 base::Unretained(this)));
292 // Just print an error message as we don't want to crash tests.
293 // Tests will fail at a call to WaitUntilServiceIsStarted().
294 if (num_methods
!= kNumMethodsToExport
) {
295 LOG(ERROR
) << "The number of methods does not match";
300 void TestService::Echo(MethodCall
* method_call
,
301 ExportedObject::ResponseSender response_sender
) {
302 MessageReader
reader(method_call
);
303 std::string text_message
;
304 if (!reader
.PopString(&text_message
)) {
305 response_sender
.Run(scoped_ptr
<Response
>());
309 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
310 MessageWriter
writer(response
.get());
311 writer
.AppendString(text_message
);
312 response_sender
.Run(response
.Pass());
315 void TestService::SlowEcho(MethodCall
* method_call
,
316 ExportedObject::ResponseSender response_sender
) {
317 base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
318 Echo(method_call
, response_sender
);
321 void TestService::AsyncEcho(MethodCall
* method_call
,
322 ExportedObject::ResponseSender response_sender
) {
323 // Schedule a call to Echo() to send an asynchronous response after we return.
324 message_loop()->PostDelayedTask(FROM_HERE
,
325 base::Bind(&TestService::Echo
,
326 base::Unretained(this),
329 TestTimeouts::tiny_timeout());
332 void TestService::BrokenMethod(MethodCall
* method_call
,
333 ExportedObject::ResponseSender response_sender
) {
334 response_sender
.Run(scoped_ptr
<Response
>());
338 void TestService::GetAllProperties(
339 MethodCall
* method_call
,
340 ExportedObject::ResponseSender response_sender
) {
341 MessageReader
reader(method_call
);
342 std::string interface
;
343 if (!reader
.PopString(&interface
)) {
344 response_sender
.Run(scoped_ptr
<Response
>());
348 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
349 MessageWriter
writer(response
.get());
351 AddPropertiesToWriter(&writer
);
353 response_sender
.Run(response
.Pass());
356 void TestService::GetProperty(MethodCall
* method_call
,
357 ExportedObject::ResponseSender response_sender
) {
358 MessageReader
reader(method_call
);
359 std::string interface
;
360 if (!reader
.PopString(&interface
)) {
361 response_sender
.Run(scoped_ptr
<Response
>());
366 if (!reader
.PopString(&name
)) {
367 response_sender
.Run(scoped_ptr
<Response
>());
371 if (name
== "Name") {
372 // Return the previous value for the "Name" property:
373 // Variant<"TestService">
374 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
375 MessageWriter
writer(response
.get());
377 writer
.AppendVariantOfString("TestService");
379 response_sender
.Run(response
.Pass());
380 } else if (name
== "Version") {
381 // Return a new value for the "Version" property:
383 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
384 MessageWriter
writer(response
.get());
386 writer
.AppendVariantOfInt16(20);
388 response_sender
.Run(response
.Pass());
389 } else if (name
== "Methods") {
390 // Return the previous value for the "Methods" property:
391 // Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>
392 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
393 MessageWriter
writer(response
.get());
394 MessageWriter
variant_writer(NULL
);
395 MessageWriter
variant_array_writer(NULL
);
397 writer
.OpenVariant("as", &variant_writer
);
398 variant_writer
.OpenArray("s", &variant_array_writer
);
399 variant_array_writer
.AppendString("Echo");
400 variant_array_writer
.AppendString("SlowEcho");
401 variant_array_writer
.AppendString("AsyncEcho");
402 variant_array_writer
.AppendString("BrokenMethod");
403 variant_writer
.CloseContainer(&variant_array_writer
);
404 writer
.CloseContainer(&variant_writer
);
406 response_sender
.Run(response
.Pass());
407 } else if (name
== "Objects") {
408 // Return the previous value for the "Objects" property:
409 // Variant<[objectpath:"/TestObjectPath"]>
410 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
411 MessageWriter
writer(response
.get());
412 MessageWriter
variant_writer(NULL
);
413 MessageWriter
variant_array_writer(NULL
);
415 writer
.OpenVariant("ao", &variant_writer
);
416 variant_writer
.OpenArray("o", &variant_array_writer
);
417 variant_array_writer
.AppendObjectPath(ObjectPath("/TestObjectPath"));
418 variant_writer
.CloseContainer(&variant_array_writer
);
419 writer
.CloseContainer(&variant_writer
);
421 response_sender
.Run(response
.Pass());
422 } else if (name
== "Bytes") {
423 // Return the previous value for the "Bytes" property:
424 // Variant<[0x54, 0x65, 0x73, 0x74]>
425 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
426 MessageWriter
writer(response
.get());
427 MessageWriter
variant_writer(NULL
);
428 MessageWriter
variant_array_writer(NULL
);
430 writer
.OpenVariant("ay", &variant_writer
);
431 const uint8 bytes
[] = { 0x54, 0x65, 0x73, 0x74 };
432 variant_writer
.AppendArrayOfBytes(bytes
, sizeof(bytes
));
433 writer
.CloseContainer(&variant_writer
);
435 response_sender
.Run(response
.Pass());
438 response_sender
.Run(scoped_ptr
<Response
>());
443 void TestService::SetProperty(MethodCall
* method_call
,
444 ExportedObject::ResponseSender response_sender
) {
445 MessageReader
reader(method_call
);
446 std::string interface
;
447 if (!reader
.PopString(&interface
)) {
448 response_sender
.Run(scoped_ptr
<Response
>());
453 if (!reader
.PopString(&name
)) {
454 response_sender
.Run(scoped_ptr
<Response
>());
458 if (name
!= "Name") {
459 response_sender
.Run(scoped_ptr
<Response
>());
464 if (!reader
.PopVariantOfString(&value
)) {
465 response_sender
.Run(scoped_ptr
<Response
>());
469 SendPropertyChangedSignal(value
);
471 response_sender
.Run(Response::FromMethodCall(method_call
));
474 void TestService::PerformAction(
475 MethodCall
* method_call
,
476 ExportedObject::ResponseSender response_sender
) {
477 MessageReader
reader(method_call
);
479 ObjectPath object_path
;
480 if (!reader
.PopString(&action
) || !reader
.PopObjectPath(&object_path
)) {
481 response_sender
.Run(scoped_ptr
<Response
>());
485 if (action
== "AddObject") {
486 AddObject(object_path
);
487 } else if (action
== "RemoveObject") {
488 RemoveObject(object_path
);
489 } else if (action
== "SetSendImmediatePropertiesChanged") {
490 SetSendImmediatePropertiesChanged();
491 } else if (action
== "ReleaseOwnership") {
492 ReleaseOwnership(base::Bind(&TestService::PerformActionResponse
,
493 base::Unretained(this),
494 method_call
, response_sender
));
496 } else if (action
== "Ownership") {
497 ReleaseOwnership(base::Bind(&TestService::OwnershipReleased
,
498 base::Unretained(this),
499 method_call
, response_sender
));
501 } else if (action
== "InvalidateProperty") {
502 SendPropertyInvalidatedSignal();
505 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
506 response_sender
.Run(response
.Pass());
509 void TestService::PerformActionResponse(
510 MethodCall
* method_call
,
511 ExportedObject::ResponseSender response_sender
) {
512 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
513 response_sender
.Run(response
.Pass());
516 void TestService::OwnershipReleased(
517 MethodCall
* method_call
,
518 ExportedObject::ResponseSender response_sender
) {
519 RequestOwnership(base::Bind(&TestService::OwnershipRegained
,
520 base::Unretained(this),
521 method_call
, response_sender
));
525 void TestService::OwnershipRegained(
526 MethodCall
* method_call
,
527 ExportedObject::ResponseSender response_sender
,
529 PerformActionResponse(method_call
, response_sender
);
533 void TestService::GetManagedObjects(
534 MethodCall
* method_call
,
535 ExportedObject::ResponseSender response_sender
) {
536 scoped_ptr
<Response
> response
= Response::FromMethodCall(method_call
);
537 MessageWriter
writer(response
.get());
539 // The managed objects response is a dictionary of object paths identifying
540 // the object(s) with a dictionary of strings identifying the interface(s)
541 // they implement and then a dictionary of property values.
543 // Thus this looks something like:
546 // "/org/chromium/TestObject": {
547 // "org.chromium.TestInterface": { /* Properties */ }
552 MessageWriter
array_writer(NULL
);
553 MessageWriter
dict_entry_writer(NULL
);
554 MessageWriter
object_array_writer(NULL
);
555 MessageWriter
object_dict_entry_writer(NULL
);
557 writer
.OpenArray("{oa{sa{sv}}}", &array_writer
);
559 array_writer
.OpenDictEntry(&dict_entry_writer
);
560 dict_entry_writer
.AppendObjectPath(ObjectPath("/org/chromium/TestObject"));
561 dict_entry_writer
.OpenArray("{sa{sv}}", &object_array_writer
);
563 object_array_writer
.OpenDictEntry(&object_dict_entry_writer
);
564 object_dict_entry_writer
.AppendString("org.chromium.TestInterface");
565 AddPropertiesToWriter(&object_dict_entry_writer
);
566 object_array_writer
.CloseContainer(&object_dict_entry_writer
);
568 dict_entry_writer
.CloseContainer(&object_array_writer
);
570 array_writer
.CloseContainer(&dict_entry_writer
);
571 writer
.CloseContainer(&array_writer
);
573 response_sender
.Run(response
.Pass());
575 if (send_immediate_properties_changed_
)
576 SendPropertyChangedSignal("ChangedTestServiceName");
579 void TestService::AddPropertiesToWriter(MessageWriter
* writer
) {
580 // The properties response is a dictionary of strings identifying the
581 // property and a variant containing the property value. We return all
582 // of the properties, thus the response is:
585 // "Name": Variant<"TestService">,
586 // "Version": Variant<10>,
587 // "Methods": Variant<["Echo", "SlowEcho", "AsyncEcho", "BrokenMethod"]>,
588 // "Objects": Variant<[objectpath:"/TestObjectPath"]>
589 // "Bytes": Variant<[0x54, 0x65, 0x73, 0x74]>
592 MessageWriter
array_writer(NULL
);
593 MessageWriter
dict_entry_writer(NULL
);
594 MessageWriter
variant_writer(NULL
);
595 MessageWriter
variant_array_writer(NULL
);
597 writer
->OpenArray("{sv}", &array_writer
);
599 array_writer
.OpenDictEntry(&dict_entry_writer
);
600 dict_entry_writer
.AppendString("Name");
601 dict_entry_writer
.AppendVariantOfString("TestService");
602 array_writer
.CloseContainer(&dict_entry_writer
);
604 array_writer
.OpenDictEntry(&dict_entry_writer
);
605 dict_entry_writer
.AppendString("Version");
606 dict_entry_writer
.AppendVariantOfInt16(10);
607 array_writer
.CloseContainer(&dict_entry_writer
);
609 array_writer
.OpenDictEntry(&dict_entry_writer
);
610 dict_entry_writer
.AppendString("Methods");
611 dict_entry_writer
.OpenVariant("as", &variant_writer
);
612 variant_writer
.OpenArray("s", &variant_array_writer
);
613 variant_array_writer
.AppendString("Echo");
614 variant_array_writer
.AppendString("SlowEcho");
615 variant_array_writer
.AppendString("AsyncEcho");
616 variant_array_writer
.AppendString("BrokenMethod");
617 variant_writer
.CloseContainer(&variant_array_writer
);
618 dict_entry_writer
.CloseContainer(&variant_writer
);
619 array_writer
.CloseContainer(&dict_entry_writer
);
621 array_writer
.OpenDictEntry(&dict_entry_writer
);
622 dict_entry_writer
.AppendString("Objects");
623 dict_entry_writer
.OpenVariant("ao", &variant_writer
);
624 variant_writer
.OpenArray("o", &variant_array_writer
);
625 variant_array_writer
.AppendObjectPath(ObjectPath("/TestObjectPath"));
626 variant_writer
.CloseContainer(&variant_array_writer
);
627 dict_entry_writer
.CloseContainer(&variant_writer
);
628 array_writer
.CloseContainer(&dict_entry_writer
);
630 array_writer
.OpenDictEntry(&dict_entry_writer
);
631 dict_entry_writer
.AppendString("Bytes");
632 dict_entry_writer
.OpenVariant("ay", &variant_writer
);
633 const uint8 bytes
[] = { 0x54, 0x65, 0x73, 0x74 };
634 variant_writer
.AppendArrayOfBytes(bytes
, sizeof(bytes
));
635 dict_entry_writer
.CloseContainer(&variant_writer
);
636 array_writer
.CloseContainer(&dict_entry_writer
);
638 writer
->CloseContainer(&array_writer
);
641 void TestService::AddObject(const ObjectPath
& object_path
) {
642 message_loop()->PostTask(
644 base::Bind(&TestService::AddObjectInternal
,
645 base::Unretained(this),
649 void TestService::AddObjectInternal(const ObjectPath
& object_path
) {
650 Signal
signal(kObjectManagerInterface
, kObjectManagerInterfacesAdded
);
651 MessageWriter
writer(&signal
);
652 writer
.AppendObjectPath(object_path
);
654 MessageWriter
array_writer(NULL
);
655 MessageWriter
dict_entry_writer(NULL
);
657 writer
.OpenArray("{sa{sv}}", &array_writer
);
658 array_writer
.OpenDictEntry(&dict_entry_writer
);
659 dict_entry_writer
.AppendString("org.chromium.TestInterface");
660 AddPropertiesToWriter(&dict_entry_writer
);
661 array_writer
.CloseContainer(&dict_entry_writer
);
662 writer
.CloseContainer(&array_writer
);
664 exported_object_manager_
->SendSignal(&signal
);
667 void TestService::RemoveObject(const ObjectPath
& object_path
) {
668 message_loop()->PostTask(FROM_HERE
,
669 base::Bind(&TestService::RemoveObjectInternal
,
670 base::Unretained(this),
674 void TestService::RemoveObjectInternal(const ObjectPath
& object_path
) {
675 Signal
signal(kObjectManagerInterface
, kObjectManagerInterfacesRemoved
);
676 MessageWriter
writer(&signal
);
678 writer
.AppendObjectPath(object_path
);
680 std::vector
<std::string
> interfaces
;
681 interfaces
.push_back("org.chromium.TestInterface");
682 writer
.AppendArrayOfStrings(interfaces
);
684 exported_object_manager_
->SendSignal(&signal
);
687 void TestService::SendPropertyChangedSignal(const std::string
& name
) {
688 message_loop()->PostTask(
690 base::Bind(&TestService::SendPropertyChangedSignalInternal
,
691 base::Unretained(this),
695 void TestService::SendPropertyChangedSignalInternal(const std::string
& name
) {
696 Signal
signal(kPropertiesInterface
, kPropertiesChanged
);
697 MessageWriter
writer(&signal
);
698 writer
.AppendString("org.chromium.TestInterface");
700 MessageWriter
array_writer(NULL
);
701 MessageWriter
dict_entry_writer(NULL
);
703 writer
.OpenArray("{sv}", &array_writer
);
704 array_writer
.OpenDictEntry(&dict_entry_writer
);
705 dict_entry_writer
.AppendString("Name");
706 dict_entry_writer
.AppendVariantOfString(name
);
707 array_writer
.CloseContainer(&dict_entry_writer
);
708 writer
.CloseContainer(&array_writer
);
710 MessageWriter
invalidated_array_writer(NULL
);
712 writer
.OpenArray("s", &invalidated_array_writer
);
713 writer
.CloseContainer(&invalidated_array_writer
);
715 exported_object_
->SendSignal(&signal
);
718 void TestService::SendPropertyInvalidatedSignal() {
719 message_loop()->PostTask(
720 FROM_HERE
, base::Bind(&TestService::SendPropertyInvalidatedSignalInternal
,
721 base::Unretained(this)));
724 void TestService::SendPropertyInvalidatedSignalInternal() {
725 Signal
signal(kPropertiesInterface
, kPropertiesChanged
);
726 MessageWriter
writer(&signal
);
727 writer
.AppendString("org.chromium.TestInterface");
729 MessageWriter
array_writer(NULL
);
730 MessageWriter
dict_entry_writer(NULL
);
732 writer
.OpenArray("{sv}", &array_writer
);
733 writer
.CloseContainer(&array_writer
);
735 MessageWriter
invalidated_array_writer(NULL
);
737 writer
.OpenArray("s", &invalidated_array_writer
);
738 invalidated_array_writer
.AppendString("Name");
739 writer
.CloseContainer(&invalidated_array_writer
);
741 exported_object_
->SendSignal(&signal
);