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