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/callback_helpers.h"
9 #include "base/values.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 // 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 {
23 public:
RefHolder(base::WeakPtr<ShillClientHelper> helper)24 explicit RefHolder(base::WeakPtr<ShillClientHelper> helper)
25 : helper_(helper) {
26 helper_->AddRef();
27 }
~RefHolder()28 ~RefHolder() {
29 if (helper_)
30 helper_->Release();
31 }
32
33 private:
34 base::WeakPtr<ShillClientHelper> helper_;
35 };
36
37 namespace {
38
39 const char kInvalidResponseErrorName[] = ""; // No error name.
40 const char kInvalidResponseErrorMessage[] = "Invalid response.";
41
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.
OnBooleanMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::BooleanCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)45 void OnBooleanMethodWithErrorCallback(
46 ShillClientHelper::RefHolder* ref_holder,
47 const ShillClientHelper::BooleanCallback& callback,
48 const ShillClientHelper::ErrorCallback& error_callback,
49 dbus::Response* response) {
50 if (!response) {
51 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
52 return;
53 }
54 dbus::MessageReader reader(response);
55 bool result;
56 if (!reader.PopBool(&result)) {
57 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
58 return;
59 }
60 callback.Run(result);
61 }
62
OnStringMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::StringCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)63 void OnStringMethodWithErrorCallback(
64 ShillClientHelper::RefHolder* ref_holder,
65 const ShillClientHelper::StringCallback& callback,
66 const ShillClientHelper::ErrorCallback& error_callback,
67 dbus::Response* response) {
68 if (!response) {
69 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
70 return;
71 }
72 dbus::MessageReader reader(response);
73 std::string result;
74 if (!reader.PopString(&result)) {
75 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
76 return;
77 }
78 callback.Run(result);
79 }
80
81 // Handles responses for methods without results.
OnVoidMethod(ShillClientHelper::RefHolder * ref_holder,const VoidDBusMethodCallback & callback,dbus::Response * response)82 void OnVoidMethod(ShillClientHelper::RefHolder* ref_holder,
83 const VoidDBusMethodCallback& callback,
84 dbus::Response* response) {
85 if (!response) {
86 callback.Run(DBUS_METHOD_CALL_FAILURE);
87 return;
88 }
89 callback.Run(DBUS_METHOD_CALL_SUCCESS);
90 }
91
92 // Handles responses for methods with ObjectPath results.
OnObjectPathMethod(ShillClientHelper::RefHolder * ref_holder,const ObjectPathDBusMethodCallback & callback,dbus::Response * response)93 void OnObjectPathMethod(
94 ShillClientHelper::RefHolder* ref_holder,
95 const ObjectPathDBusMethodCallback& callback,
96 dbus::Response* response) {
97 if (!response) {
98 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath());
99 return;
100 }
101 dbus::MessageReader reader(response);
102 dbus::ObjectPath result;
103 if (!reader.PopObjectPath(&result)) {
104 callback.Run(DBUS_METHOD_CALL_FAILURE, dbus::ObjectPath());
105 return;
106 }
107 callback.Run(DBUS_METHOD_CALL_SUCCESS, result);
108 }
109
110 // Handles responses for methods with ObjectPath results and no status.
OnObjectPathMethodWithoutStatus(ShillClientHelper::RefHolder * ref_holder,const ObjectPathCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)111 void OnObjectPathMethodWithoutStatus(
112 ShillClientHelper::RefHolder* ref_holder,
113 const ObjectPathCallback& callback,
114 const ShillClientHelper::ErrorCallback& error_callback,
115 dbus::Response* response) {
116 if (!response) {
117 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
118 return;
119 }
120 dbus::MessageReader reader(response);
121 dbus::ObjectPath result;
122 if (!reader.PopObjectPath(&result)) {
123 error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
124 return;
125 }
126 callback.Run(result);
127 }
128
129 // Handles responses for methods with DictionaryValue results.
OnDictionaryValueMethod(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::DictionaryValueCallback & callback,dbus::Response * response)130 void OnDictionaryValueMethod(
131 ShillClientHelper::RefHolder* ref_holder,
132 const ShillClientHelper::DictionaryValueCallback& callback,
133 dbus::Response* response) {
134 if (!response) {
135 base::DictionaryValue result;
136 callback.Run(DBUS_METHOD_CALL_FAILURE, result);
137 return;
138 }
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);
145 return;
146 }
147 callback.Run(DBUS_METHOD_CALL_SUCCESS, *result);
148 }
149
150 // Handles responses for methods without results.
OnVoidMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const base::Closure & callback,dbus::Response * response)151 void OnVoidMethodWithErrorCallback(
152 ShillClientHelper::RefHolder* ref_holder,
153 const base::Closure& callback,
154 dbus::Response* response) {
155 callback.Run();
156 }
157
158 // Handles responses for methods with DictionaryValue results.
159 // Used by CallDictionaryValueMethodWithErrorCallback().
OnDictionaryValueMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::DictionaryValueCallbackWithoutStatus & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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);
170 return;
171 }
172 callback.Run(*result);
173 }
174
175 // Handles responses for methods with ListValue results.
OnListValueMethodWithErrorCallback(ShillClientHelper::RefHolder * ref_holder,const ShillClientHelper::ListValueCallback & callback,const ShillClientHelper::ErrorCallback & error_callback,dbus::Response * response)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);
186 return;
187 }
188 callback.Run(*result);
189 }
190
191 // Handles running appropriate error callbacks.
OnError(const ShillClientHelper::ErrorCallback & error_callback,dbus::ErrorResponse * response)192 void OnError(const ShillClientHelper::ErrorCallback& error_callback,
193 dbus::ErrorResponse* response) {
194 std::string error_name;
195 std::string error_message;
196 if (response) {
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);
201 }
202 error_callback.Run(error_name, error_message);
203 }
204
205 } // namespace
206
ShillClientHelper(dbus::ObjectProxy * proxy)207 ShillClientHelper::ShillClientHelper(dbus::ObjectProxy* proxy)
208 : proxy_(proxy),
209 active_refs_(0),
210 weak_ptr_factory_(this) {
211 }
212
~ShillClientHelper()213 ShillClientHelper::~ShillClientHelper() {
214 LOG_IF(ERROR, observer_list_.might_have_observers())
215 << "ShillClientHelper destroyed with active observers";
216 }
217
SetReleasedCallback(ReleasedCallback callback)218 void ShillClientHelper::SetReleasedCallback(ReleasedCallback callback) {
219 CHECK(released_callback_.is_null());
220 released_callback_ = callback;
221 }
222
AddPropertyChangedObserver(ShillPropertyChangedObserver * observer)223 void ShillClientHelper::AddPropertyChangedObserver(
224 ShillPropertyChangedObserver* observer) {
225 if (observer_list_.HasObserver(observer))
226 return;
227 AddRef();
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]);
231 }
232 interfaces_to_be_monitored_.clear();
233
234 observer_list_.AddObserver(observer);
235 }
236
RemovePropertyChangedObserver(ShillPropertyChangedObserver * observer)237 void ShillClientHelper::RemovePropertyChangedObserver(
238 ShillPropertyChangedObserver* observer) {
239 if (!observer_list_.HasObserver(observer))
240 return;
241 observer_list_.RemoveObserver(observer);
242 Release();
243 }
244
MonitorPropertyChanged(const std::string & interface_name)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);
250 } else {
251 // Delay the ConnectToSignal until an observer is added.
252 interfaces_to_be_monitored_.push_back(interface_name);
253 }
254 }
255
MonitorPropertyChangedInternal(const std::string & 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()));
266 }
267
CallVoidMethod(dbus::MethodCall * method_call,const VoidDBusMethodCallback & callback)268 void ShillClientHelper::CallVoidMethod(
269 dbus::MethodCall* method_call,
270 const VoidDBusMethodCallback& callback) {
271 DCHECK(!callback.is_null());
272 proxy_->CallMethod(
273 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
274 base::Bind(&OnVoidMethod,
275 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
276 callback));
277 }
278
CallObjectPathMethod(dbus::MethodCall * method_call,const ObjectPathDBusMethodCallback & callback)279 void ShillClientHelper::CallObjectPathMethod(
280 dbus::MethodCall* method_call,
281 const ObjectPathDBusMethodCallback& callback) {
282 DCHECK(!callback.is_null());
283 proxy_->CallMethod(
284 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
285 base::Bind(&OnObjectPathMethod,
286 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
287 callback));
288 }
289
CallObjectPathMethodWithErrorCallback(dbus::MethodCall * method_call,const ObjectPathCallback & callback,const ErrorCallback & error_callback)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(
297 method_call,
298 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
299 base::Bind(&OnObjectPathMethodWithoutStatus,
300 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
301 callback,
302 error_callback),
303 base::Bind(&OnError,
304 error_callback));
305 }
306
CallDictionaryValueMethod(dbus::MethodCall * method_call,const DictionaryValueCallback & callback)307 void ShillClientHelper::CallDictionaryValueMethod(
308 dbus::MethodCall* method_call,
309 const DictionaryValueCallback& callback) {
310 DCHECK(!callback.is_null());
311 proxy_->CallMethod(
312 method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
313 base::Bind(&OnDictionaryValueMethod,
314 base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
315 callback));
316 }
317
CallVoidMethodWithErrorCallback(dbus::MethodCall * method_call,const base::Closure & callback,const ErrorCallback & error_callback)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())),
328 callback),
329 base::Bind(&OnError,
330 error_callback));
331 }
332
CallBooleanMethodWithErrorCallback(dbus::MethodCall * method_call,const BooleanCallback & callback,const ErrorCallback & error_callback)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())),
343 callback,
344 error_callback),
345 base::Bind(&OnError,
346 error_callback));
347 }
348
CallStringMethodWithErrorCallback(dbus::MethodCall * method_call,const StringCallback & callback,const ErrorCallback & error_callback)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())),
359 callback,
360 error_callback),
361 base::Bind(&OnError,
362 error_callback));
363 }
364
CallDictionaryValueMethodWithErrorCallback(dbus::MethodCall * method_call,const DictionaryValueCallbackWithoutStatus & callback,const ErrorCallback & error_callback)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())),
375 callback,
376 error_callback),
377 base::Bind(&OnError,
378 error_callback));
379 }
380
CallListValueMethodWithErrorCallback(dbus::MethodCall * method_call,const ListValueCallback & callback,const ErrorCallback & error_callback)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())),
391 callback,
392 error_callback),
393 base::Bind(&OnError,
394 error_callback));
395 }
396
397 // static
AppendValueDataAsVariant(dbus::MessageWriter * writer,const base::Value & value)398 void ShillClientHelper::AppendValueDataAsVariant(dbus::MessageWriter* writer,
399 const base::Value& value) {
400 // Support basic types and string-to-string dictionary.
401 switch (value.GetType()) {
402 case base::Value::TYPE_DICTIONARY: {
403 const base::DictionaryValue* dictionary = NULL;
404 value.GetAsDictionary(&dictionary);
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);
410 !it.IsAtEnd();
411 it.Advance()) {
412 dbus::MessageWriter entry_writer(NULL);
413 array_writer.OpenDictEntry(&entry_writer);
414 entry_writer.AppendString(it.key());
415 const base::Value& value = it.value();
416 std::string value_string;
417 DLOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING)
418 << "Unexpected type " << value.GetType();
419 value.GetAsString(&value_string);
420 entry_writer.AppendString(value_string);
421 array_writer.CloseContainer(&entry_writer);
422 }
423 variant_writer.CloseContainer(&array_writer);
424 writer->CloseContainer(&variant_writer);
425 break;
426 }
427 case base::Value::TYPE_LIST: {
428 const base::ListValue* list = NULL;
429 value.GetAsList(&list);
430 dbus::MessageWriter variant_writer(NULL);
431 writer->OpenVariant("as", &variant_writer);
432 dbus::MessageWriter array_writer(NULL);
433 variant_writer.OpenArray("s", &array_writer);
434 for (base::ListValue::const_iterator it = list->begin();
435 it != list->end(); ++it) {
436 const base::Value& value = **it;
437 LOG_IF(ERROR, value.GetType() != base::Value::TYPE_STRING)
438 << "Unexpected type " << value.GetType();
439 std::string value_string;
440 value.GetAsString(&value_string);
441 array_writer.AppendString(value_string);
442 }
443 variant_writer.CloseContainer(&array_writer);
444 writer->CloseContainer(&variant_writer);
445 break;
446 }
447 case base::Value::TYPE_BOOLEAN:
448 case base::Value::TYPE_INTEGER:
449 case base::Value::TYPE_DOUBLE:
450 case base::Value::TYPE_STRING:
451 dbus::AppendBasicTypeValueDataAsVariant(writer, value);
452 break;
453 default:
454 DLOG(ERROR) << "Unexpected type " << value.GetType();
455 }
456
457 }
458
459 // static
AppendServicePropertiesDictionary(dbus::MessageWriter * writer,const base::DictionaryValue & dictionary)460 void ShillClientHelper::AppendServicePropertiesDictionary(
461 dbus::MessageWriter* writer,
462 const base::DictionaryValue& dictionary) {
463 dbus::MessageWriter array_writer(NULL);
464 writer->OpenArray("{sv}", &array_writer);
465 for (base::DictionaryValue::Iterator it(dictionary);
466 !it.IsAtEnd();
467 it.Advance()) {
468 dbus::MessageWriter entry_writer(NULL);
469 array_writer.OpenDictEntry(&entry_writer);
470 entry_writer.AppendString(it.key());
471 ShillClientHelper::AppendValueDataAsVariant(&entry_writer, it.value());
472 array_writer.CloseContainer(&entry_writer);
473 }
474 writer->CloseContainer(&array_writer);
475 }
476
AddRef()477 void ShillClientHelper::AddRef() {
478 ++active_refs_;
479 }
480
Release()481 void ShillClientHelper::Release() {
482 --active_refs_;
483 if (active_refs_ == 0 && !released_callback_.is_null())
484 base::ResetAndReturn(&released_callback_).Run(this); // May delete this
485 }
486
OnSignalConnected(const std::string & interface,const std::string & signal,bool success)487 void ShillClientHelper::OnSignalConnected(const std::string& interface,
488 const std::string& signal,
489 bool success) {
490 LOG_IF(ERROR, !success) << "Connect to " << interface << " " << signal
491 << " failed.";
492 }
493
OnPropertyChanged(dbus::Signal * signal)494 void ShillClientHelper::OnPropertyChanged(dbus::Signal* signal) {
495 if (!observer_list_.might_have_observers())
496 return;
497
498 dbus::MessageReader reader(signal);
499 std::string name;
500 if (!reader.PopString(&name))
501 return;
502 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
503 if (!value.get())
504 return;
505
506 FOR_EACH_OBSERVER(ShillPropertyChangedObserver, observer_list_,
507 OnPropertyChanged(name, *value));
508 }
509
510 } // namespace chromeos
511