• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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