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
17namespace {
18
19void EmptyCallback(bool /* success */) {
20}
21
22}  // namespace
23
24namespace dbus {
25
26// Echo, SlowEcho, AsyncEcho, BrokenMethod, GetAll, Get, Set, PerformAction,
27// GetManagedObjects.
28const int TestService::kNumMethodsToExport = 9;
29
30TestService::Options::Options()
31    : request_ownership_options(Bus::REQUIRE_PRIMARY) {
32}
33
34TestService::Options::~Options() {
35}
36
37TestService::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
45TestService::~TestService() {
46  Stop();
47}
48
49bool 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
55bool 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
61void TestService::ShutdownAndBlock() {
62  message_loop()->PostTask(
63      FROM_HERE,
64      base::Bind(&TestService::ShutdownAndBlockInternal,
65                 base::Unretained(this)));
66}
67
68bool TestService::HasDBusThread() {
69  return bus_->HasDBusThread();
70}
71
72void TestService::ShutdownAndBlockInternal() {
73  if (HasDBusThread())
74    bus_->ShutdownOnDBusThreadAndBlock();
75  else
76    bus_->ShutdownAndBlock();
77}
78
79void 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
87void 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
95void 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
102void 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
118void 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
126void 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
135void 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
143void 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
159void 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
268void 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
283void 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
289void 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
300void TestService::BrokenMethod(MethodCall* method_call,
301                               ExportedObject::ResponseSender response_sender) {
302  response_sender.Run(scoped_ptr<Response>());
303}
304
305
306void 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
324void 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
397void 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
428void 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
448void 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
491void 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
544void 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
552void 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
570void 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
577void 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
590void 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
598void 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