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
EmptyCallback(bool)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
Options()30 TestService::Options::Options()
31 : request_ownership_options(Bus::REQUIRE_PRIMARY) {
32 }
33
~Options()34 TestService::Options::~Options() {
35 }
36
TestService(const Options & options)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
~TestService()45 TestService::~TestService() {
46 Stop();
47 }
48
StartService()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
WaitUntilServiceIsStarted()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
ShutdownAndBlock()61 void TestService::ShutdownAndBlock() {
62 message_loop()->PostTask(
63 FROM_HERE,
64 base::Bind(&TestService::ShutdownAndBlockInternal,
65 base::Unretained(this)));
66 }
67
HasDBusThread()68 bool TestService::HasDBusThread() {
69 return bus_->HasDBusThread();
70 }
71
ShutdownAndBlockInternal()72 void TestService::ShutdownAndBlockInternal() {
73 if (HasDBusThread())
74 bus_->ShutdownOnDBusThreadAndBlock();
75 else
76 bus_->ShutdownAndBlock();
77 }
78
SendTestSignal(const std::string & message)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
SendTestSignalFromRoot(const std::string & message)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
SendTestSignalInternal(const std::string & message)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
SendTestSignalFromRootInternal(const std::string & message)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
RequestOwnership(base::Callback<void (bool)> callback)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
RequestOwnershipInternal(base::Callback<void (bool)> callback)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
OnOwnership(base::Callback<void (bool)> callback,const std::string & service_name,bool success)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
OnExported(const std::string & interface_name,const std::string & method_name,bool success)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
Run(base::MessageLoop * message_loop)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
Echo(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
SlowEcho(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
AsyncEcho(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
BrokenMethod(MethodCall * method_call,ExportedObject::ResponseSender response_sender)300 void TestService::BrokenMethod(MethodCall* method_call,
301 ExportedObject::ResponseSender response_sender) {
302 response_sender.Run(scoped_ptr<Response>());
303 }
304
305
GetAllProperties(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
GetProperty(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
SetProperty(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
PerformAction(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
GetManagedObjects(MethodCall * method_call,ExportedObject::ResponseSender response_sender)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
AddPropertiesToWriter(MessageWriter * writer)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
AddObject(const ObjectPath & object_path)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
AddObjectInternal(const ObjectPath & object_path)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
RemoveObject(const ObjectPath & object_path)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
RemoveObjectInternal(const ObjectPath & object_path)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
SendPropertyChangedSignal(const std::string & name)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
SendPropertyChangedSignalInternal(const std::string & name)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