exported_object.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. #include "dbus/exported_object.h"
  5. #include <stdint.h>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/logging.h"
  9. #include "base/memory/ref_counted.h"
  10. #include "base/metrics/histogram_macros.h"
  11. #include "base/task/sequenced_task_runner.h"
  12. #include "base/task/task_runner.h"
  13. #include "base/threading/thread_restrictions.h"
  14. #include "base/time/time.h"
  15. #include "dbus/bus.h"
  16. #include "dbus/message.h"
  17. #include "dbus/object_path.h"
  18. #include "dbus/scoped_dbus_error.h"
  19. #include "dbus/util.h"
  20. namespace dbus {
  21. namespace {
  22. // Used for success ratio histograms. 1 for success, 0 for failure.
  23. const int kSuccessRatioHistogramMaxValue = 2;
  24. } // namespace
  25. ExportedObject::ExportedObject(Bus* bus,
  26. const ObjectPath& object_path)
  27. : bus_(bus),
  28. object_path_(object_path),
  29. object_is_registered_(false) {
  30. LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
  31. }
  32. ExportedObject::~ExportedObject() {
  33. DCHECK(!object_is_registered_);
  34. }
  35. bool ExportedObject::ExportMethodAndBlock(
  36. const std::string& interface_name,
  37. const std::string& method_name,
  38. const MethodCallCallback& method_call_callback) {
  39. bus_->AssertOnDBusThread();
  40. // Check if the method is already exported.
  41. const std::string absolute_method_name =
  42. GetAbsoluteMemberName(interface_name, method_name);
  43. if (method_table_.find(absolute_method_name) != method_table_.end()) {
  44. LOG(ERROR) << absolute_method_name << " is already exported";
  45. return false;
  46. }
  47. if (!bus_->Connect())
  48. return false;
  49. if (!bus_->SetUpAsyncOperations())
  50. return false;
  51. if (!Register())
  52. return false;
  53. // Add the method callback to the method table.
  54. method_table_[absolute_method_name] = method_call_callback;
  55. return true;
  56. }
  57. bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
  58. const std::string& method_name) {
  59. bus_->AssertOnDBusThread();
  60. const std::string absolute_method_name =
  61. GetAbsoluteMemberName(interface_name, method_name);
  62. MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
  63. if (iter == method_table_.end()) {
  64. LOG(ERROR) << absolute_method_name << " is not exported";
  65. return false;
  66. }
  67. method_table_.erase(iter);
  68. return true;
  69. }
  70. void ExportedObject::ExportMethod(
  71. const std::string& interface_name,
  72. const std::string& method_name,
  73. const MethodCallCallback& method_call_callback,
  74. OnExportedCallback on_exported_callback) {
  75. bus_->AssertOnOriginThread();
  76. base::OnceClosure task = base::BindOnce(
  77. &ExportedObject::ExportMethodInternal, this, interface_name, method_name,
  78. method_call_callback, std::move(on_exported_callback));
  79. bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
  80. }
  81. void ExportedObject::UnexportMethod(
  82. const std::string& interface_name,
  83. const std::string& method_name,
  84. OnUnexportedCallback on_unexported_callback) {
  85. bus_->AssertOnOriginThread();
  86. base::OnceClosure task = base::BindOnce(
  87. &ExportedObject::UnexportMethodInternal, this, interface_name,
  88. method_name, std::move(on_unexported_callback));
  89. bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
  90. }
  91. void ExportedObject::SendSignal(Signal* signal) {
  92. // For signals, the object path should be set to the path to the sender
  93. // object, which is this exported object here.
  94. CHECK(signal->SetPath(object_path_));
  95. // Increment the reference count so we can safely reference the
  96. // underlying signal message until the signal sending is complete. This
  97. // will be unref'ed in SendSignalInternal().
  98. DBusMessage* signal_message = signal->raw_message();
  99. dbus_message_ref(signal_message);
  100. const base::TimeTicks start_time = base::TimeTicks::Now();
  101. if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
  102. // The Chrome OS power manager doesn't use a dedicated TaskRunner for
  103. // sending DBus messages. Sending signals asynchronously can cause an
  104. // inversion in the message order if the power manager calls
  105. // ObjectProxy::CallMethodAndBlock() before going back to the top level of
  106. // the MessageLoop: crbug.com/472361.
  107. SendSignalInternal(start_time, signal_message);
  108. } else {
  109. bus_->GetDBusTaskRunner()->PostTask(
  110. FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
  111. start_time, signal_message));
  112. }
  113. }
  114. void ExportedObject::Unregister() {
  115. bus_->AssertOnDBusThread();
  116. if (!object_is_registered_)
  117. return;
  118. bus_->UnregisterObjectPath(object_path_);
  119. object_is_registered_ = false;
  120. }
  121. void ExportedObject::ExportMethodInternal(
  122. const std::string& interface_name,
  123. const std::string& method_name,
  124. const MethodCallCallback& method_call_callback,
  125. OnExportedCallback on_exported_callback) {
  126. bus_->AssertOnDBusThread();
  127. const bool success = ExportMethodAndBlock(interface_name,
  128. method_name,
  129. method_call_callback);
  130. bus_->GetOriginTaskRunner()->PostTask(
  131. FROM_HERE, base::BindOnce(&ExportedObject::OnExported, this,
  132. std::move(on_exported_callback), interface_name,
  133. method_name, success));
  134. }
  135. void ExportedObject::UnexportMethodInternal(
  136. const std::string& interface_name,
  137. const std::string& method_name,
  138. OnUnexportedCallback on_unexported_callback) {
  139. bus_->AssertOnDBusThread();
  140. const bool success = UnexportMethodAndBlock(interface_name, method_name);
  141. bus_->GetOriginTaskRunner()->PostTask(
  142. FROM_HERE, base::BindOnce(&ExportedObject::OnUnexported, this,
  143. std::move(on_unexported_callback),
  144. interface_name, method_name, success));
  145. }
  146. void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
  147. const std::string& interface_name,
  148. const std::string& method_name,
  149. bool success) {
  150. bus_->AssertOnOriginThread();
  151. std::move(on_exported_callback).Run(interface_name, method_name, success);
  152. }
  153. void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
  154. const std::string& interface_name,
  155. const std::string& method_name,
  156. bool success) {
  157. bus_->AssertOnOriginThread();
  158. std::move(on_unexported_callback).Run(interface_name, method_name, success);
  159. }
  160. void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
  161. DBusMessage* signal_message) {
  162. uint32_t serial = 0;
  163. bus_->Send(signal_message, &serial);
  164. dbus_message_unref(signal_message);
  165. // Record time spent to send the the signal. This is not accurate as the
  166. // signal will actually be sent from the next run of the message loop,
  167. // but we can at least tell the number of signals sent.
  168. UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
  169. base::TimeTicks::Now() - start_time);
  170. }
  171. bool ExportedObject::Register() {
  172. bus_->AssertOnDBusThread();
  173. if (object_is_registered_)
  174. return true;
  175. ScopedDBusError error;
  176. DBusObjectPathVTable vtable = {};
  177. vtable.message_function = &ExportedObject::HandleMessageThunk;
  178. vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
  179. const bool success = bus_->TryRegisterObjectPath(object_path_,
  180. &vtable,
  181. this,
  182. error.get());
  183. if (!success) {
  184. LOG(ERROR) << "Failed to register the object: " << object_path_.value()
  185. << ": " << (error.is_set() ? error.message() : "");
  186. return false;
  187. }
  188. object_is_registered_ = true;
  189. return true;
  190. }
  191. DBusHandlerResult ExportedObject::HandleMessage(
  192. DBusConnection* connection,
  193. DBusMessage* raw_message) {
  194. bus_->AssertOnDBusThread();
  195. // ExportedObject only handles method calls. Ignore other message types (e.g.
  196. // signal).
  197. if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
  198. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  199. // raw_message will be unrefed on exit of the function. Increment the
  200. // reference so we can use it in MethodCall.
  201. dbus_message_ref(raw_message);
  202. std::unique_ptr<MethodCall> method_call(
  203. MethodCall::FromRawMessage(raw_message));
  204. const std::string interface = method_call->GetInterface();
  205. const std::string member = method_call->GetMember();
  206. if (interface.empty()) {
  207. // We don't support method calls without interface.
  208. LOG(WARNING) << "Interface is missing: " << method_call->ToString();
  209. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  210. }
  211. // Check if we know about the method.
  212. const std::string absolute_method_name = GetAbsoluteMemberName(
  213. interface, member);
  214. MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
  215. if (iter == method_table_.end()) {
  216. // Don't know about the method.
  217. LOG(WARNING) << "Unknown method: " << method_call->ToString();
  218. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  219. }
  220. const base::TimeTicks start_time = base::TimeTicks::Now();
  221. if (bus_->HasDBusThread()) {
  222. // Post a task to run the method in the origin thread.
  223. bus_->GetOriginTaskRunner()->PostTask(
  224. FROM_HERE,
  225. base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
  226. std::move(method_call), start_time));
  227. } else {
  228. // If the D-Bus thread is not used, just call the method directly.
  229. MethodCall* method = method_call.get();
  230. iter->second.Run(
  231. method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
  232. std::move(method_call)));
  233. }
  234. // It's valid to say HANDLED here, and send a method response at a later
  235. // time from OnMethodCompleted() asynchronously.
  236. return DBUS_HANDLER_RESULT_HANDLED;
  237. }
  238. void ExportedObject::RunMethod(const MethodCallCallback& method_call_callback,
  239. std::unique_ptr<MethodCall> method_call,
  240. base::TimeTicks start_time) {
  241. bus_->AssertOnOriginThread();
  242. MethodCall* method = method_call.get();
  243. method_call_callback.Run(
  244. method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
  245. std::move(method_call)));
  246. }
  247. void ExportedObject::SendResponse(base::TimeTicks start_time,
  248. std::unique_ptr<MethodCall> method_call,
  249. std::unique_ptr<Response> response) {
  250. DCHECK(method_call);
  251. if (bus_->HasDBusThread()) {
  252. bus_->GetDBusTaskRunner()->PostTask(
  253. FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
  254. std::move(method_call), std::move(response),
  255. start_time));
  256. } else {
  257. OnMethodCompleted(std::move(method_call), std::move(response), start_time);
  258. }
  259. }
  260. void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
  261. std::unique_ptr<Response> response,
  262. base::TimeTicks start_time) {
  263. bus_->AssertOnDBusThread();
  264. // Record if the method call is successful, or not. 1 if successful.
  265. UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
  266. response ? 1 : 0,
  267. kSuccessRatioHistogramMaxValue);
  268. // Check if the bus is still connected. If the method takes long to
  269. // complete, the bus may be shut down meanwhile.
  270. if (!bus_->IsConnected())
  271. return;
  272. if (!response) {
  273. // Something bad happened in the method call.
  274. std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
  275. method_call.get(), DBUS_ERROR_FAILED,
  276. "error occurred in " + method_call->GetMember()));
  277. bus_->Send(error_response->raw_message(), nullptr);
  278. return;
  279. }
  280. // The method call was successful.
  281. bus_->Send(response->raw_message(), nullptr);
  282. // Record time spent to handle the the method call. Don't include failures.
  283. UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
  284. base::TimeTicks::Now() - start_time);
  285. }
  286. void ExportedObject::OnUnregistered(DBusConnection* connection) {
  287. }
  288. DBusHandlerResult ExportedObject::HandleMessageThunk(
  289. DBusConnection* connection,
  290. DBusMessage* raw_message,
  291. void* user_data) {
  292. ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
  293. return self->HandleMessage(connection, raw_message);
  294. }
  295. void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
  296. void* user_data) {
  297. ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
  298. return self->OnUnregistered(connection);
  299. }
  300. } // namespace dbus