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.
5 #include "chromeos/dbus/shill_client_helper.h"
8 #include "base/callback_helpers.h"
9 #include "components/device_event_log/device_event_log.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"
17 // Class to hold onto a reference to a ShillClientHelper. This calss
18 // is owned by callbacks and released once the callback completes.
19 // Note: Only success callbacks hold the reference. If an error callback is
20 // invoked instead, the success callback will still be destroyed and the
21 // RefHolder with it, once the callback chain completes.
22 class ShillClientHelper::RefHolder
{
24 explicit RefHolder(base::WeakPtr
<ShillClientHelper
> helper
)
34 base::WeakPtr
<ShillClientHelper
> helper_
;
39 const char kInvalidResponseErrorName
[] = ""; // No error name.
40 const char kInvalidResponseErrorMessage
[] = "Invalid response.";
42 // Note: here and below, |ref_holder| is unused in the function body. It only
43 // exists so that it will be destroyed (and the reference released) with the
44 // Callback object once completed.
45 void OnBooleanMethodWithErrorCallback(
46 ShillClientHelper::RefHolder
* ref_holder
,
47 const ShillClientHelper::BooleanCallback
& callback
,
48 const ShillClientHelper::ErrorCallback
& error_callback
,
49 dbus::Response
* response
) {
51 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
54 dbus::MessageReader
reader(response
);
56 if (!reader
.PopBool(&result
)) {
57 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
63 void OnStringMethodWithErrorCallback(
64 ShillClientHelper::RefHolder
* ref_holder
,
65 const ShillClientHelper::StringCallback
& callback
,
66 const ShillClientHelper::ErrorCallback
& error_callback
,
67 dbus::Response
* response
) {
69 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
72 dbus::MessageReader
reader(response
);
74 if (!reader
.PopString(&result
)) {
75 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
81 // Handles responses for methods without results.
82 void OnVoidMethod(ShillClientHelper::RefHolder
* ref_holder
,
83 const VoidDBusMethodCallback
& callback
,
84 dbus::Response
* response
) {
86 callback
.Run(DBUS_METHOD_CALL_FAILURE
);
89 callback
.Run(DBUS_METHOD_CALL_SUCCESS
);
92 // Handles responses for methods with ObjectPath results.
93 void OnObjectPathMethod(
94 ShillClientHelper::RefHolder
* ref_holder
,
95 const ObjectPathDBusMethodCallback
& callback
,
96 dbus::Response
* response
) {
98 callback
.Run(DBUS_METHOD_CALL_FAILURE
, dbus::ObjectPath());
101 dbus::MessageReader
reader(response
);
102 dbus::ObjectPath result
;
103 if (!reader
.PopObjectPath(&result
)) {
104 callback
.Run(DBUS_METHOD_CALL_FAILURE
, dbus::ObjectPath());
107 callback
.Run(DBUS_METHOD_CALL_SUCCESS
, result
);
110 // Handles responses for methods with ObjectPath results and no status.
111 void OnObjectPathMethodWithoutStatus(
112 ShillClientHelper::RefHolder
* ref_holder
,
113 const ObjectPathCallback
& callback
,
114 const ShillClientHelper::ErrorCallback
& error_callback
,
115 dbus::Response
* response
) {
117 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
120 dbus::MessageReader
reader(response
);
121 dbus::ObjectPath result
;
122 if (!reader
.PopObjectPath(&result
)) {
123 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
126 callback
.Run(result
);
129 // Handles responses for methods with DictionaryValue results.
130 void OnDictionaryValueMethod(
131 ShillClientHelper::RefHolder
* ref_holder
,
132 const ShillClientHelper::DictionaryValueCallback
& callback
,
133 dbus::Response
* response
) {
135 base::DictionaryValue result
;
136 callback
.Run(DBUS_METHOD_CALL_FAILURE
, result
);
139 dbus::MessageReader
reader(response
);
140 scoped_ptr
<base::Value
> value(dbus::PopDataAsValue(&reader
));
141 base::DictionaryValue
* result
= NULL
;
142 if (!value
.get() || !value
->GetAsDictionary(&result
)) {
143 base::DictionaryValue result
;
144 callback
.Run(DBUS_METHOD_CALL_FAILURE
, result
);
147 callback
.Run(DBUS_METHOD_CALL_SUCCESS
, *result
);
150 // Handles responses for methods without results.
151 void OnVoidMethodWithErrorCallback(
152 ShillClientHelper::RefHolder
* ref_holder
,
153 const base::Closure
& callback
,
154 dbus::Response
* response
) {
158 // Handles responses for methods with DictionaryValue results.
159 // Used by CallDictionaryValueMethodWithErrorCallback().
160 void OnDictionaryValueMethodWithErrorCallback(
161 ShillClientHelper::RefHolder
* ref_holder
,
162 const ShillClientHelper::DictionaryValueCallbackWithoutStatus
& callback
,
163 const ShillClientHelper::ErrorCallback
& error_callback
,
164 dbus::Response
* response
) {
165 dbus::MessageReader
reader(response
);
166 scoped_ptr
<base::Value
> value(dbus::PopDataAsValue(&reader
));
167 base::DictionaryValue
* result
= NULL
;
168 if (!value
.get() || !value
->GetAsDictionary(&result
)) {
169 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
172 callback
.Run(*result
);
175 // Handles responses for methods with ListValue results.
176 void OnListValueMethodWithErrorCallback(
177 ShillClientHelper::RefHolder
* ref_holder
,
178 const ShillClientHelper::ListValueCallback
& callback
,
179 const ShillClientHelper::ErrorCallback
& error_callback
,
180 dbus::Response
* response
) {
181 dbus::MessageReader
reader(response
);
182 scoped_ptr
<base::Value
> value(dbus::PopDataAsValue(&reader
));
183 base::ListValue
* result
= NULL
;
184 if (!value
.get() || !value
->GetAsList(&result
)) {
185 error_callback
.Run(kInvalidResponseErrorName
, kInvalidResponseErrorMessage
);
188 callback
.Run(*result
);
191 // Handles running appropriate error callbacks.
192 void OnError(const ShillClientHelper::ErrorCallback
& error_callback
,
193 dbus::ErrorResponse
* response
) {
194 std::string error_name
;
195 std::string error_message
;
197 // Error message may contain the error message as string.
198 dbus::MessageReader
reader(response
);
199 error_name
= response
->GetErrorName();
200 reader
.PopString(&error_message
);
202 error_callback
.Run(error_name
, error_message
);
207 ShillClientHelper::ShillClientHelper(dbus::ObjectProxy
* proxy
)
210 weak_ptr_factory_(this) {
213 ShillClientHelper::~ShillClientHelper() {
214 if (observer_list_
.might_have_observers())
215 NET_LOG(ERROR
) << "ShillClientHelper destroyed with active observers";
218 void ShillClientHelper::SetReleasedCallback(ReleasedCallback callback
) {
219 CHECK(released_callback_
.is_null());
220 released_callback_
= callback
;
223 void ShillClientHelper::AddPropertyChangedObserver(
224 ShillPropertyChangedObserver
* observer
) {
225 if (observer_list_
.HasObserver(observer
))
228 // Excecute all the pending MonitorPropertyChanged calls.
229 for (size_t i
= 0; i
< interfaces_to_be_monitored_
.size(); ++i
) {
230 MonitorPropertyChangedInternal(interfaces_to_be_monitored_
[i
]);
232 interfaces_to_be_monitored_
.clear();
234 observer_list_
.AddObserver(observer
);
237 void ShillClientHelper::RemovePropertyChangedObserver(
238 ShillPropertyChangedObserver
* observer
) {
239 if (!observer_list_
.HasObserver(observer
))
241 observer_list_
.RemoveObserver(observer
);
245 void ShillClientHelper::MonitorPropertyChanged(
246 const std::string
& interface_name
) {
247 if (observer_list_
.might_have_observers()) {
248 // Effectively monitor the PropertyChanged now.
249 MonitorPropertyChangedInternal(interface_name
);
251 // Delay the ConnectToSignal until an observer is added.
252 interfaces_to_be_monitored_
.push_back(interface_name
);
256 void ShillClientHelper::MonitorPropertyChangedInternal(
257 const std::string
& interface_name
) {
258 // We are not using dbus::PropertySet to monitor PropertyChanged signal
259 // because the interface is not "org.freedesktop.DBus.Properties".
260 proxy_
->ConnectToSignal(interface_name
,
261 shill::kMonitorPropertyChanged
,
262 base::Bind(&ShillClientHelper::OnPropertyChanged
,
263 weak_ptr_factory_
.GetWeakPtr()),
264 base::Bind(&ShillClientHelper::OnSignalConnected
,
265 weak_ptr_factory_
.GetWeakPtr()));
268 void ShillClientHelper::CallVoidMethod(
269 dbus::MethodCall
* method_call
,
270 const VoidDBusMethodCallback
& callback
) {
271 DCHECK(!callback
.is_null());
273 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
274 base::Bind(&OnVoidMethod
,
275 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
279 void ShillClientHelper::CallObjectPathMethod(
280 dbus::MethodCall
* method_call
,
281 const ObjectPathDBusMethodCallback
& callback
) {
282 DCHECK(!callback
.is_null());
284 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
285 base::Bind(&OnObjectPathMethod
,
286 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
290 void ShillClientHelper::CallObjectPathMethodWithErrorCallback(
291 dbus::MethodCall
* method_call
,
292 const ObjectPathCallback
& callback
,
293 const ErrorCallback
& error_callback
) {
294 DCHECK(!callback
.is_null());
295 DCHECK(!error_callback
.is_null());
296 proxy_
->CallMethodWithErrorCallback(
298 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
299 base::Bind(&OnObjectPathMethodWithoutStatus
,
300 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
307 void ShillClientHelper::CallDictionaryValueMethod(
308 dbus::MethodCall
* method_call
,
309 const DictionaryValueCallback
& callback
) {
310 DCHECK(!callback
.is_null());
312 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
313 base::Bind(&OnDictionaryValueMethod
,
314 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
318 void ShillClientHelper::CallVoidMethodWithErrorCallback(
319 dbus::MethodCall
* method_call
,
320 const base::Closure
& callback
,
321 const ErrorCallback
& error_callback
) {
322 DCHECK(!callback
.is_null());
323 DCHECK(!error_callback
.is_null());
324 proxy_
->CallMethodWithErrorCallback(
325 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
326 base::Bind(&OnVoidMethodWithErrorCallback
,
327 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
333 void ShillClientHelper::CallBooleanMethodWithErrorCallback(
334 dbus::MethodCall
* method_call
,
335 const BooleanCallback
& callback
,
336 const ErrorCallback
& error_callback
) {
337 DCHECK(!callback
.is_null());
338 DCHECK(!error_callback
.is_null());
339 proxy_
->CallMethodWithErrorCallback(
340 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
341 base::Bind(&OnBooleanMethodWithErrorCallback
,
342 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
349 void ShillClientHelper::CallStringMethodWithErrorCallback(
350 dbus::MethodCall
* method_call
,
351 const StringCallback
& callback
,
352 const ErrorCallback
& error_callback
) {
353 DCHECK(!callback
.is_null());
354 DCHECK(!error_callback
.is_null());
355 proxy_
->CallMethodWithErrorCallback(
356 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
357 base::Bind(&OnStringMethodWithErrorCallback
,
358 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
365 void ShillClientHelper::CallDictionaryValueMethodWithErrorCallback(
366 dbus::MethodCall
* method_call
,
367 const DictionaryValueCallbackWithoutStatus
& callback
,
368 const ErrorCallback
& error_callback
) {
369 DCHECK(!callback
.is_null());
370 DCHECK(!error_callback
.is_null());
371 proxy_
->CallMethodWithErrorCallback(
372 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
373 base::Bind(&OnDictionaryValueMethodWithErrorCallback
,
374 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
381 void ShillClientHelper::CallListValueMethodWithErrorCallback(
382 dbus::MethodCall
* method_call
,
383 const ListValueCallback
& callback
,
384 const ErrorCallback
& error_callback
) {
385 DCHECK(!callback
.is_null());
386 DCHECK(!error_callback
.is_null());
387 proxy_
->CallMethodWithErrorCallback(
388 method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
389 base::Bind(&OnListValueMethodWithErrorCallback
,
390 base::Owned(new RefHolder(weak_ptr_factory_
.GetWeakPtr())),
399 enum DictionaryType
{ DICTIONARY_TYPE_VARIANT
, DICTIONARY_TYPE_STRING
};
401 // Appends an a{ss} dictionary to |writer|. |dictionary| must only contain
403 void AppendStringDictionary(const base::DictionaryValue
& dictionary
,
404 dbus::MessageWriter
* writer
) {
405 dbus::MessageWriter
variant_writer(NULL
);
406 writer
->OpenVariant("a{ss}", &variant_writer
);
407 dbus::MessageWriter
array_writer(NULL
);
408 variant_writer
.OpenArray("{ss}", &array_writer
);
409 for (base::DictionaryValue::Iterator
it(dictionary
); !it
.IsAtEnd();
411 dbus::MessageWriter
entry_writer(NULL
);
412 array_writer
.OpenDictEntry(&entry_writer
);
413 entry_writer
.AppendString(it
.key());
414 const base::Value
& value
= it
.value();
415 std::string value_string
;
416 if (!value
.GetAsString(&value_string
))
417 NET_LOG(ERROR
) << "Dictionary value not a string: " << it
.key();
418 entry_writer
.AppendString(value_string
);
419 array_writer
.CloseContainer(&entry_writer
);
421 variant_writer
.CloseContainer(&array_writer
);
422 writer
->CloseContainer(&variant_writer
);
425 // Implements AppendValueDataAsVariant. If |dictionary_type| is
426 // DICTIONARY_TYPE_VARIANT and |value| is a Dictionary then it will be written
427 // as type 'a{ss}'. Otherwise dictionaries are written as type a{sv}. (This is
428 // to support Cellular.APN which expects a string -> string dictionary).
429 void AppendValueDataAsVariantInternal(dbus::MessageWriter
* writer
,
430 const base::Value
& value
,
431 DictionaryType dictionary_type
) {
432 // Support basic types and string-to-string dictionary.
433 switch (value
.GetType()) {
434 case base::Value::TYPE_DICTIONARY
: {
435 const base::DictionaryValue
* dictionary
= NULL
;
436 value
.GetAsDictionary(&dictionary
);
437 if (dictionary_type
== DICTIONARY_TYPE_STRING
) {
438 AppendStringDictionary(*dictionary
, writer
);
440 dbus::MessageWriter
variant_writer(NULL
);
441 writer
->OpenVariant("a{sv}", &variant_writer
);
442 ShillClientHelper::AppendServicePropertiesDictionary(&variant_writer
,
444 writer
->CloseContainer(&variant_writer
);
448 case base::Value::TYPE_LIST
: {
449 const base::ListValue
* list
= NULL
;
450 value
.GetAsList(&list
);
451 dbus::MessageWriter
variant_writer(NULL
);
452 writer
->OpenVariant("as", &variant_writer
);
453 dbus::MessageWriter
array_writer(NULL
);
454 variant_writer
.OpenArray("s", &array_writer
);
455 for (base::ListValue::const_iterator it
= list
->begin();
456 it
!= list
->end(); ++it
) {
457 const base::Value
& value
= **it
;
458 std::string value_string
;
459 if (!value
.GetAsString(&value_string
))
460 NET_LOG(ERROR
) << "List value not a string: " << value
;
461 array_writer
.AppendString(value_string
);
463 variant_writer
.CloseContainer(&array_writer
);
464 writer
->CloseContainer(&variant_writer
);
467 case base::Value::TYPE_BOOLEAN
:
468 case base::Value::TYPE_INTEGER
:
469 case base::Value::TYPE_DOUBLE
:
470 case base::Value::TYPE_STRING
:
471 dbus::AppendBasicTypeValueDataAsVariant(writer
, value
);
474 NET_LOG(ERROR
) << "Unexpected value type: " << value
.GetType();
481 void ShillClientHelper::AppendValueDataAsVariant(dbus::MessageWriter
* writer
,
482 const base::Value
& value
) {
483 AppendValueDataAsVariantInternal(writer
, value
, DICTIONARY_TYPE_VARIANT
);
487 void ShillClientHelper::AppendServicePropertiesDictionary(
488 dbus::MessageWriter
* writer
,
489 const base::DictionaryValue
& dictionary
) {
490 dbus::MessageWriter
array_writer(NULL
);
491 writer
->OpenArray("{sv}", &array_writer
);
492 for (base::DictionaryValue::Iterator
it(dictionary
); !it
.IsAtEnd();
494 dbus::MessageWriter
entry_writer(NULL
);
495 array_writer
.OpenDictEntry(&entry_writer
);
496 entry_writer
.AppendString(it
.key());
497 // Shill expects Cellular.APN to be a string dictionary, a{ss}. All other
498 // properties use a varient dictionary, a{sv}. TODO(stevenjb): Remove this
499 // hack if/when we change Shill to accept a{sv} for Cellular.APN.
500 DictionaryType dictionary_type
= (it
.key() == shill::kCellularApnProperty
)
501 ? DICTIONARY_TYPE_STRING
502 : DICTIONARY_TYPE_VARIANT
;
503 AppendValueDataAsVariantInternal(&entry_writer
, it
.value(),
505 array_writer
.CloseContainer(&entry_writer
);
507 writer
->CloseContainer(&array_writer
);
510 void ShillClientHelper::AddRef() {
514 void ShillClientHelper::Release() {
516 if (active_refs_
== 0 && !released_callback_
.is_null())
517 base::ResetAndReturn(&released_callback_
).Run(this); // May delete this
520 void ShillClientHelper::OnSignalConnected(const std::string
& interface
,
521 const std::string
& signal
,
524 NET_LOG(ERROR
) << "Connect to " << interface
<< " " << signal
<< " failed.";
527 void ShillClientHelper::OnPropertyChanged(dbus::Signal
* signal
) {
528 if (!observer_list_
.might_have_observers())
531 dbus::MessageReader
reader(signal
);
533 if (!reader
.PopString(&name
))
535 scoped_ptr
<base::Value
> value(dbus::PopDataAsValue(&reader
));
539 FOR_EACH_OBSERVER(ShillPropertyChangedObserver
, observer_list_
,
540 OnPropertyChanged(name
, *value
));
543 } // namespace chromeos