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