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 "chromeos/dbus/shill_client_helper.h" 6 7 #include "base/bind.h" 8 #include "base/values.h" 9 #include "chromeos/dbus/blocking_method_caller.h" 10 #include "dbus/message.h" 11 #include "dbus/object_proxy.h" 12 #include "dbus/values_util.h" 13 #include "third_party/cros_system_api/dbus/service_constants.h" 14 15 namespace chromeos { 16 17 namespace { 18 19 const char kInvalidResponseErrorName[] = ""; // No error name. 20 const char kInvalidResponseErrorMessage[] = "Invalid response."; 21 22 void OnBooleanMethodWithErrorCallback( 23 const ShillClientHelper::BooleanCallback& callback, 24 const ShillClientHelper::ErrorCallback& error_callback, 25 dbus::Response* response) { 26 if (!response) { 27 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 28 return; 29 } 30 dbus::MessageReader reader(response); 31 bool result; 32 if (!reader.PopBool(&result)) { 33 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 34 return; 35 } 36 callback.Run(result); 37 } 38 39 void OnStringMethodWithErrorCallback( 40 const ShillClientHelper::StringCallback& callback, 41 const ShillClientHelper::ErrorCallback& error_callback, 42 dbus::Response* response) { 43 if (!response) { 44 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 45 return; 46 } 47 dbus::MessageReader reader(response); 48 std::string result; 49 if (!reader.PopString(&result)) { 50 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 51 return; 52 } 53 callback.Run(result); 54 } 55 56 // Handles responses for methods without results. 57 void OnVoidMethod(const VoidDBusMethodCallback& callback, 58 dbus::Response* response) { 59 if (!response) { 60 callback.Run(DBUS_METHOD_CALL_FAILURE); 61 return; 62 } 63 callback.Run(DBUS_METHOD_CALL_SUCCESS); 64 } 65 66 // Handles responses for methods with ObjectPath results. 67 void OnObjectPathMethod( 68 const ObjectPathDBusMethodCallback& callback, 69 dbus::Response* response) { 70 if (!response) { 71 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath()); 72 return; 73 } 74 dbus::MessageReader reader(response); 75 dbus::ObjectPath result; 76 if (!reader.PopObjectPath(&result)) { 77 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath()); 78 return; 79 } 80 callback.Run(DBUS_METHOD_CALL_SUCCESS, result); 81 } 82 83 // Handles responses for methods with ObjectPath results and no status. 84 void OnObjectPathMethodWithoutStatus( 85 const ObjectPathCallback& callback, 86 const ShillClientHelper::ErrorCallback& error_callback, 87 dbus::Response* response) { 88 if (!response) { 89 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 90 return; 91 } 92 dbus::MessageReader reader(response); 93 dbus::ObjectPath result; 94 if (!reader.PopObjectPath(&result)) { 95 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 96 return; 97 } 98 callback.Run(result); 99 } 100 101 // Handles responses for methods with DictionaryValue results. 102 void OnDictionaryValueMethod( 103 const ShillClientHelper::DictionaryValueCallback& callback, 104 dbus::Response* response) { 105 if (!response) { 106 base::DictionaryValue result; 107 callback.Run(DBUS_METHOD_CALL_FAILURE, result); 108 return; 109 } 110 dbus::MessageReader reader(response); 111 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 112 base::DictionaryValue* result = NULL; 113 if (!value.get() || !value->GetAsDictionary(&result)) { 114 base::DictionaryValue result; 115 callback.Run(DBUS_METHOD_CALL_FAILURE, result); 116 return; 117 } 118 callback.Run(DBUS_METHOD_CALL_SUCCESS, *result); 119 } 120 121 // Handles responses for methods without results. 122 void OnVoidMethodWithErrorCallback( 123 const base::Closure& callback, 124 dbus::Response* response) { 125 callback.Run(); 126 } 127 128 // Handles responses for methods with DictionaryValue results. 129 // Used by CallDictionaryValueMethodWithErrorCallback(). 130 void OnDictionaryValueMethodWithErrorCallback( 131 const ShillClientHelper::DictionaryValueCallbackWithoutStatus& callback, 132 const ShillClientHelper::ErrorCallback& error_callback, 133 dbus::Response* response) { 134 dbus::MessageReader reader(response); 135 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 136 base::DictionaryValue* result = NULL; 137 if (!value.get() || !value->GetAsDictionary(&result)) { 138 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 139 return; 140 } 141 callback.Run(*result); 142 } 143 144 // Handles responses for methods with ListValue results. 145 void OnListValueMethodWithErrorCallback( 146 const ShillClientHelper::ListValueCallback& callback, 147 const ShillClientHelper::ErrorCallback& error_callback, 148 dbus::Response* response) { 149 dbus::MessageReader reader(response); 150 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 151 base::ListValue* result = NULL; 152 if (!value.get() || !value->GetAsList(&result)) { 153 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage); 154 return; 155 } 156 callback.Run(*result); 157 } 158 159 // Handles running appropriate error callbacks. 160 void OnError(const ShillClientHelper::ErrorCallback& error_callback, 161 dbus::ErrorResponse* response) { 162 std::string error_name; 163 std::string error_message; 164 if (response) { 165 // Error message may contain the error message as string. 166 dbus::MessageReader reader(response); 167 error_name = response->GetErrorName(); 168 reader.PopString(&error_message); 169 } 170 error_callback.Run(error_name, error_message); 171 } 172 173 } // namespace 174 175 ShillClientHelper::ShillClientHelper(dbus::Bus* bus, 176 dbus::ObjectProxy* proxy) 177 : blocking_method_caller_(new BlockingMethodCaller(bus, proxy)), 178 proxy_(proxy), 179 weak_ptr_factory_(this) { 180 } 181 182 ShillClientHelper::~ShillClientHelper() { 183 LOG_IF(ERROR, observer_list_.size() != 0u) 184 << "ShillClientHelper destroyed with active observers: " 185 << observer_list_.size(); 186 } 187 188 void ShillClientHelper::AddPropertyChangedObserver( 189 ShillPropertyChangedObserver* observer) { 190 // Excecute all the pending MonitorPropertyChanged calls. 191 for (size_t i = 0; i < interfaces_to_be_monitored_.size(); ++i) { 192 MonitorPropertyChangedInternal(interfaces_to_be_monitored_[i]); 193 } 194 interfaces_to_be_monitored_.clear(); 195 196 observer_list_.AddObserver(observer); 197 } 198 199 void ShillClientHelper::RemovePropertyChangedObserver( 200 ShillPropertyChangedObserver* observer) { 201 observer_list_.RemoveObserver(observer); 202 } 203 204 void ShillClientHelper::MonitorPropertyChanged( 205 const std::string& interface_name) { 206 if (observer_list_.size() > 0) { 207 // Effectively monitor the PropertyChanged now. 208 MonitorPropertyChangedInternal(interface_name); 209 } else { 210 // Delay the ConnectToSignal until an observer is added. 211 interfaces_to_be_monitored_.push_back(interface_name); 212 } 213 } 214 215 void ShillClientHelper::MonitorPropertyChangedInternal( 216 const std::string& interface_name) { 217 // We are not using dbus::PropertySet to monitor PropertyChanged signal 218 // because the interface is not "org.freedesktop.DBus.Properties". 219 proxy_->ConnectToSignal(interface_name, 220 flimflam::kMonitorPropertyChanged, 221 base::Bind(&ShillClientHelper::OnPropertyChanged, 222 weak_ptr_factory_.GetWeakPtr()), 223 base::Bind(&ShillClientHelper::OnSignalConnected, 224 weak_ptr_factory_.GetWeakPtr())); 225 } 226 227 void ShillClientHelper::CallVoidMethod( 228 dbus::MethodCall* method_call, 229 const VoidDBusMethodCallback& callback) { 230 DCHECK(!callback.is_null()); 231 proxy_->CallMethod(method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 232 base::Bind(&OnVoidMethod, 233 callback)); 234 } 235 236 void ShillClientHelper::CallObjectPathMethod( 237 dbus::MethodCall* method_call, 238 const ObjectPathDBusMethodCallback& callback) { 239 DCHECK(!callback.is_null()); 240 proxy_->CallMethod(method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 241 base::Bind(&OnObjectPathMethod, 242 callback)); 243 } 244 245 void ShillClientHelper::CallObjectPathMethodWithErrorCallback( 246 dbus::MethodCall* method_call, 247 const ObjectPathCallback& callback, 248 const ErrorCallback& error_callback) { 249 DCHECK(!callback.is_null()); 250 DCHECK(!error_callback.is_null()); 251 proxy_->CallMethodWithErrorCallback( 252 method_call, 253 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 254 base::Bind(&OnObjectPathMethodWithoutStatus, 255 callback, 256 error_callback), 257 base::Bind(&OnError, 258 error_callback)); 259 } 260 261 void ShillClientHelper::CallDictionaryValueMethod( 262 dbus::MethodCall* method_call, 263 const DictionaryValueCallback& callback) { 264 DCHECK(!callback.is_null()); 265 proxy_->CallMethod(method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 266 base::Bind(&OnDictionaryValueMethod, 267 callback)); 268 } 269 270 void ShillClientHelper::CallVoidMethodWithErrorCallback( 271 dbus::MethodCall* method_call, 272 const base::Closure& callback, 273 const ErrorCallback& error_callback) { 274 DCHECK(!callback.is_null()); 275 DCHECK(!error_callback.is_null()); 276 proxy_->CallMethodWithErrorCallback( 277 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 278 base::Bind(&OnVoidMethodWithErrorCallback, 279 callback), 280 base::Bind(&OnError, 281 error_callback)); 282 } 283 284 void ShillClientHelper::CallBooleanMethodWithErrorCallback( 285 dbus::MethodCall* method_call, 286 const BooleanCallback& callback, 287 const ErrorCallback& error_callback) { 288 DCHECK(!callback.is_null()); 289 DCHECK(!error_callback.is_null()); 290 proxy_->CallMethodWithErrorCallback( 291 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 292 base::Bind(&OnBooleanMethodWithErrorCallback, 293 callback, 294 error_callback), 295 base::Bind(&OnError, 296 error_callback)); 297 } 298 299 void ShillClientHelper::CallStringMethodWithErrorCallback( 300 dbus::MethodCall* method_call, 301 const StringCallback& callback, 302 const ErrorCallback& error_callback) { 303 DCHECK(!callback.is_null()); 304 DCHECK(!error_callback.is_null()); 305 proxy_->CallMethodWithErrorCallback( 306 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 307 base::Bind(&OnStringMethodWithErrorCallback, 308 callback, 309 error_callback), 310 base::Bind(&OnError, 311 error_callback)); 312 } 313 314 void ShillClientHelper::CallDictionaryValueMethodWithErrorCallback( 315 dbus::MethodCall* method_call, 316 const DictionaryValueCallbackWithoutStatus& callback, 317 const ErrorCallback& error_callback) { 318 DCHECK(!callback.is_null()); 319 DCHECK(!error_callback.is_null()); 320 proxy_->CallMethodWithErrorCallback( 321 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 322 base::Bind( 323 &OnDictionaryValueMethodWithErrorCallback, 324 callback, 325 error_callback), 326 base::Bind(&OnError, 327 error_callback)); 328 } 329 330 void ShillClientHelper::CallListValueMethodWithErrorCallback( 331 dbus::MethodCall* method_call, 332 const ListValueCallback& callback, 333 const ErrorCallback& error_callback) { 334 DCHECK(!callback.is_null()); 335 DCHECK(!error_callback.is_null()); 336 proxy_->CallMethodWithErrorCallback( 337 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 338 base::Bind( 339 &OnListValueMethodWithErrorCallback, 340 callback, 341 error_callback), 342 base::Bind(&OnError, 343 error_callback)); 344 } 345 346 bool ShillClientHelper::CallVoidMethodAndBlock( 347 dbus::MethodCall* method_call) { 348 scoped_ptr<dbus::Response> response( 349 blocking_method_caller_->CallMethodAndBlock(method_call)); 350 if (!response.get()) 351 return false; 352 return true; 353 } 354 355 base::DictionaryValue* ShillClientHelper::CallDictionaryValueMethodAndBlock( 356 dbus::MethodCall* method_call) { 357 scoped_ptr<dbus::Response> response( 358 blocking_method_caller_->CallMethodAndBlock(method_call)); 359 if (!response.get()) 360 return NULL; 361 362 dbus::MessageReader reader(response.get()); 363 base::Value* value = dbus::PopDataAsValue(&reader); 364 base::DictionaryValue* result = NULL; 365 if (!value || !value->GetAsDictionary(&result)) { 366 delete value; 367 return NULL; 368 } 369 return result; 370 } 371 372 // static 373 void ShillClientHelper::AppendValueDataAsVariant(dbus::MessageWriter* writer, 374 const base::Value& value) { 375 // Support basic types and string-to-string dictionary. 376 switch (value.GetType()) { 377 case base::Value::TYPE_DICTIONARY: { 378 const base::DictionaryValue* dictionary = NULL; 379 value.GetAsDictionary(&dictionary); 380 dbus::MessageWriter variant_writer(NULL); 381 writer->OpenVariant("a{ss}", &variant_writer); 382 dbus::MessageWriter array_writer(NULL); 383 variant_writer.OpenArray("{ss}", &array_writer); 384 for (base::DictionaryValue::Iterator it(*dictionary); 385 !it.IsAtEnd(); 386 it.Advance()) { 387 dbus::MessageWriter entry_writer(NULL); 388 array_writer.OpenDictEntry(&entry_writer); 389 entry_writer.AppendString(it.key()); 390 const base::Value& value = it.value(); 391 std::string value_string; 392 DLOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING) 393 << "Unexpected type " << value.GetType(); 394 value.GetAsString(&value_string); 395 entry_writer.AppendString(value_string); 396 array_writer.CloseContainer(&entry_writer); 397 } 398 variant_writer.CloseContainer(&array_writer); 399 writer->CloseContainer(&variant_writer); 400 break; 401 } 402 case base::Value::TYPE_LIST: { 403 const base::ListValue* list = NULL; 404 value.GetAsList(&list); 405 dbus::MessageWriter variant_writer(NULL); 406 writer->OpenVariant("as", &variant_writer); 407 dbus::MessageWriter array_writer(NULL); 408 variant_writer.OpenArray("s", &array_writer); 409 for (base::ListValue::const_iterator it = list->begin(); 410 it != list->end(); ++it) { 411 const base::Value& value = **it; 412 LOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING) 413 << "Unexpected type " << value.GetType(); 414 std::string value_string; 415 value.GetAsString(&value_string); 416 array_writer.AppendString(value_string); 417 } 418 variant_writer.CloseContainer(&array_writer); 419 writer->CloseContainer(&variant_writer); 420 break; 421 } 422 case base::Value::TYPE_BOOLEAN: 423 case base::Value::TYPE_INTEGER: 424 case base::Value::TYPE_DOUBLE: 425 case base::Value::TYPE_STRING: 426 dbus::AppendBasicTypeValueDataAsVariant(writer, value); 427 break; 428 default: 429 DLOG(ERROR) << "Unexpected type " << value.GetType(); 430 } 431 432 } 433 434 // static 435 void ShillClientHelper::AppendServicePropertiesDictionary( 436 dbus::MessageWriter* writer, 437 const base::DictionaryValue& dictionary) { 438 dbus::MessageWriter array_writer(NULL); 439 writer->OpenArray("{sv}", &array_writer); 440 for (base::DictionaryValue::Iterator it(dictionary); 441 !it.IsAtEnd(); 442 it.Advance()) { 443 dbus::MessageWriter entry_writer(NULL); 444 array_writer.OpenDictEntry(&entry_writer); 445 entry_writer.AppendString(it.key()); 446 ShillClientHelper::AppendValueDataAsVariant(&entry_writer, it.value()); 447 array_writer.CloseContainer(&entry_writer); 448 } 449 writer->CloseContainer(&array_writer); 450 } 451 452 void ShillClientHelper::OnSignalConnected(const std::string& interface, 453 const std::string& signal, 454 bool success) { 455 LOG_IF(ERROR, !success) << "Connect to " << interface << " " << signal 456 << " failed."; 457 } 458 459 void ShillClientHelper::OnPropertyChanged(dbus::Signal* signal) { 460 if (!observer_list_.might_have_observers()) 461 return; 462 463 dbus::MessageReader reader(signal); 464 std::string name; 465 if (!reader.PopString(&name)) 466 return; 467 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 468 if (!value.get()) 469 return; 470 471 FOR_EACH_OBSERVER(ShillPropertyChangedObserver, observer_list_, 472 OnPropertyChanged(name, *value)); 473 } 474 475 476 } // namespace chromeos 477