diff options
author | Alex Vakulenko <avakulenko@google.com> | 2015-10-12 15:21:28 -0700 |
---|---|---|
committer | Alex Vakulenko <avakulenko@google.com> | 2015-10-13 16:10:03 -0700 |
commit | 9ed0cab99f18acb3570a35e9408f24355f6b8324 (patch) | |
tree | 60e3b4c2822b812b3218489a9a6d835df1e8ca6e /brillo/dbus/dbus_method_invoker.h | |
parent | eabfe23a51c91a103042793ac2d5c28170994e1f (diff) | |
download | libchromeos-9ed0cab99f18acb3570a35e9408f24355f6b8324.tar.gz |
Move chromeos symbols into brillo namespace
And move the include files into "brillo" directory instead of "chromeos"
BUG: 24872993
TEST=built aosp and brillo and unit tests pass on dragonoboard
Change-Id: Ieb979d1ebd3152921d36cd15acbd6247f02aae69
Diffstat (limited to 'brillo/dbus/dbus_method_invoker.h')
-rw-r--r-- | brillo/dbus/dbus_method_invoker.h | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/brillo/dbus/dbus_method_invoker.h b/brillo/dbus/dbus_method_invoker.h new file mode 100644 index 0000000..df8c3c5 --- /dev/null +++ b/brillo/dbus/dbus_method_invoker.h @@ -0,0 +1,324 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file provides a way to call D-Bus methods on objects in remote processes +// as if they were native C++ function calls. + +// CallMethodAndBlock (along with CallMethodAndBlockWithTimeout) lets you call +// a D-Bus method synchronously and pass all the required parameters as C++ +// function arguments. CallMethodAndBlock relies on automatic C++ to D-Bus data +// serialization implemented in brillo/dbus/data_serialization.h. +// CallMethodAndBlock invokes the D-Bus method and returns the Response. + +// The method call response should be parsed with ExtractMethodCallResults(). +// The method takes an optional list of pointers to the expected return values +// of the D-Bus method. + +// CallMethod and CallMethodWithTimeout are similar to CallMethodAndBlock but +// make the calls asynchronously. They take two callbacks: one for successful +// method invocation and the second is for error conditions. + +// Here is an example of synchronous calls: +// Call "std::string MyInterface::MyMethod(int, double)" over D-Bus: + +// using brillo::dbus_utils::CallMethodAndBlock; +// using brillo::dbus_utils::ExtractMethodCallResults; +// +// brillo::ErrorPtr error; +// auto resp = CallMethodAndBlock(obj, +// "org.chromium.MyService.MyInterface", +// "MyMethod", +// &error, +// 2, 8.7); +// std::string return_value; +// if (resp && ExtractMethodCallResults(resp.get(), &error, &return_value)) { +// // Use the |return_value|. +// } else { +// // An error occurred. Use |error| to get details. +// } + +// And here is how to call D-Bus methods asynchronously: +// Call "std::string MyInterface::MyMethod(int, double)" over D-Bus: + +// using brillo::dbus_utils::CallMethod; +// using brillo::dbus_utils::ExtractMethodCallResults; +// +// void OnSuccess(const std::string& return_value) { +// // Use the |return_value|. +// } +// +// void OnError(brillo::Error* error) { +// // An error occurred. Use |error| to get details. +// } +// +// brillo::dbus_utils::CallMethod(obj, +// "org.chromium.MyService.MyInterface", +// "MyMethod", +// base::Bind(OnSuccess), +// base::Bind(OnError), +// 2, 8.7); + +#ifndef LIBCHROMEOS_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_ +#define LIBCHROMEOS_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_ + +#include <memory> +#include <string> +#include <tuple> + +#include <base/bind.h> +#include <brillo/dbus/dbus_param_reader.h> +#include <brillo/dbus/dbus_param_writer.h> +#include <brillo/dbus/utils.h> +#include <brillo/errors/error.h> +#include <brillo/errors/error_codes.h> +#include <brillo/brillo_export.h> +#include <dbus/file_descriptor.h> +#include <dbus/message.h> +#include <dbus/object_proxy.h> + +namespace brillo { +namespace dbus_utils { + +// A helper method to dispatch a blocking D-Bus method call. Can specify +// zero or more method call arguments in |args| which will be sent over D-Bus. +// This method sends a D-Bus message and blocks for a time period specified +// in |timeout_ms| while waiting for a reply. The time out is in milliseconds or +// -1 (DBUS_TIMEOUT_USE_DEFAULT) for default, or DBUS_TIMEOUT_INFINITE for no +// timeout. If a timeout occurs, the response object contains an error object +// with DBUS_ERROR_NO_REPLY error code (those constants come from libdbus +// [dbus/dbus.h]). +// Returns a dbus::Response object on success. On failure, returns nullptr and +// fills in additional error details into the |error| object. +template<typename... Args> +inline std::unique_ptr<dbus::Response> CallMethodAndBlockWithTimeout( + int timeout_ms, + dbus::ObjectProxy* object, + const std::string& interface_name, + const std::string& method_name, + ErrorPtr* error, + const Args&... args) { + dbus::MethodCall method_call(interface_name, method_name); + // Add method arguments to the message buffer. + dbus::MessageWriter writer(&method_call); + DBusParamWriter::Append(&writer, args...); + dbus::ScopedDBusError dbus_error; + auto response = object->CallMethodAndBlockWithErrorDetails( + &method_call, timeout_ms, &dbus_error); + if (!response) { + if (dbus_error.is_set()) { + Error::AddTo(error, + FROM_HERE, + errors::dbus::kDomain, + dbus_error.name(), + dbus_error.message()); + } else { + Error::AddToPrintf(error, + FROM_HERE, + errors::dbus::kDomain, + DBUS_ERROR_FAILED, + "Failed to call D-Bus method: %s.%s", + interface_name.c_str(), + method_name.c_str()); + } + } + return std::unique_ptr<dbus::Response>(response.release()); +} + +// Same as CallMethodAndBlockWithTimeout() but uses a default timeout value. +template<typename... Args> +inline std::unique_ptr<dbus::Response> CallMethodAndBlock( + dbus::ObjectProxy* object, + const std::string& interface_name, + const std::string& method_name, + ErrorPtr* error, + const Args&... args) { + return CallMethodAndBlockWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + object, + interface_name, + method_name, + error, + args...); +} + +namespace internal { +// In order to support non-copyable dbus::FileDescriptor, we have this +// internal::HackMove() helper function that does really nothing for normal +// types but uses Pass() for file descriptors so we can move them out from +// the temporaries created inside DBusParamReader<...>::Invoke(). +// If only libchrome supported real rvalues so we can just do std::move() and +// be done with it. +template <typename T> +inline const T& HackMove(const T& val) { + return val; +} + +// Even though |val| here is passed as const&, the actual value is created +// inside DBusParamReader<...>::Invoke() and is temporary in nature, so it is +// safe to move the file descriptor out of |val|. That's why we are doing +// const_cast here. It is a bit hacky, but there is no negative side effects. +inline dbus::FileDescriptor HackMove(const dbus::FileDescriptor& val) { + return const_cast<dbus::FileDescriptor&>(val).Pass(); +} +} // namespace internal + +// Extracts the parameters of |ResultTypes...| types from the message reader +// and puts the values in the resulting |tuple|. Returns false on error and +// provides additional error details in |error| object. +template<typename... ResultTypes> +inline bool ExtractMessageParametersAsTuple( + dbus::MessageReader* reader, + ErrorPtr* error, + std::tuple<ResultTypes...>* val_tuple) { + auto callback = [val_tuple](const ResultTypes&... params) { + *val_tuple = std::forward_as_tuple(internal::HackMove(params)...); + }; + return DBusParamReader<false, ResultTypes...>::Invoke( + callback, reader, error); +} +// Overload of ExtractMessageParametersAsTuple to handle reference types in +// tuples created with std::tie(). +template<typename... ResultTypes> +inline bool ExtractMessageParametersAsTuple( + dbus::MessageReader* reader, + ErrorPtr* error, + std::tuple<ResultTypes&...>* ref_tuple) { + auto callback = [ref_tuple](const ResultTypes&... params) { + *ref_tuple = std::forward_as_tuple(internal::HackMove(params)...); + }; + return DBusParamReader<false, ResultTypes...>::Invoke( + callback, reader, error); +} + +// A helper method to extract a list of values from a message buffer. +// The function will return false and provide detailed error information on +// failure. It fails if the D-Bus message buffer (represented by the |reader|) +// contains too many, too few parameters or the parameters are of wrong types +// (signatures). +// The usage pattern is as follows: +// +// int32_t data1; +// std::string data2; +// ErrorPtr error; +// if (ExtractMessageParameters(reader, &error, &data1, &data2)) { ... } +// +// The above example extracts an Int32 and a String from D-Bus message buffer. +template<typename... ResultTypes> +inline bool ExtractMessageParameters(dbus::MessageReader* reader, + ErrorPtr* error, + ResultTypes*... results) { + auto ref_tuple = std::tie(*results...); + return ExtractMessageParametersAsTuple<ResultTypes...>( + reader, error, &ref_tuple); +} + +// Convenient helper method to extract return value(s) of a D-Bus method call. +// |results| must be zero or more pointers to data expected to be returned +// from the method called. If an error occurs, returns false and provides +// additional details in |error| object. +// +// It is OK to call this method even if the D-Bus method doesn't expect +// any return values. Just do not specify any output |results|. In this case, +// ExtractMethodCallResults() will verify that the method didn't return any +// data in the |message|. +template<typename... ResultTypes> +inline bool ExtractMethodCallResults(dbus::Message* message, + ErrorPtr* error, + ResultTypes*... results) { + CHECK(message) << "Unable to extract parameters from a NULL message."; + + dbus::MessageReader reader(message); + if (message->GetMessageType() == dbus::Message::MESSAGE_ERROR) { + std::string error_message; + if (ExtractMessageParameters(&reader, error, &error_message)) + AddDBusError(error, message->GetErrorName(), error_message); + return false; + } + return ExtractMessageParameters(&reader, error, results...); +} + +////////////////////////////////////////////////////////////////////////////// +// Asynchronous method invocation support + +using AsyncErrorCallback = base::Callback<void(Error* error)>; + +// A helper function that translates dbus::ErrorResponse response +// from D-Bus into brillo::Error* and invokes the |callback|. +void BRILLO_EXPORT TranslateErrorResponse(const AsyncErrorCallback& callback, + dbus::ErrorResponse* resp); + +// A helper function that translates dbus::Response from D-Bus into +// a list of C++ values passed as parameters to |success_callback|. If the +// response message doesn't have the correct number of parameters, or they +// are of wrong types, an error is sent to |error_callback|. +template<typename... OutArgs> +void TranslateSuccessResponse( + const base::Callback<void(OutArgs...)>& success_callback, + const AsyncErrorCallback& error_callback, + dbus::Response* resp) { + auto callback = [&success_callback](const OutArgs&... params) { + if (!success_callback.is_null()) { + success_callback.Run(params...); + } + }; + ErrorPtr error; + dbus::MessageReader reader(resp); + if (!DBusParamReader<false, OutArgs...>::Invoke(callback, &reader, &error) && + !error_callback.is_null()) { + error_callback.Run(error.get()); + } +} + +// A helper method to dispatch a non-blocking D-Bus method call. Can specify +// zero or more method call arguments in |params| which will be sent over D-Bus. +// This method sends a D-Bus message and returns immediately. +// When the remote method returns successfully, the success callback is +// invoked with the return value(s), if any. +// On error, the error callback is called. Note, the error callback can be +// called synchronously (before CallMethodWithTimeout returns) if there was +// a problem invoking a method (e.g. object or method doesn't exist). +// If the response is not received within |timeout_ms|, an error callback is +// called with DBUS_ERROR_NO_REPLY error code. +template<typename... InArgs, typename... OutArgs> +inline void CallMethodWithTimeout( + int timeout_ms, + dbus::ObjectProxy* object, + const std::string& interface_name, + const std::string& method_name, + const base::Callback<void(OutArgs...)>& success_callback, + const AsyncErrorCallback& error_callback, + const InArgs&... params) { + dbus::MethodCall method_call(interface_name, method_name); + dbus::MessageWriter writer(&method_call); + DBusParamWriter::Append(&writer, params...); + + dbus::ObjectProxy::ErrorCallback dbus_error_callback = + base::Bind(&TranslateErrorResponse, error_callback); + dbus::ObjectProxy::ResponseCallback dbus_success_callback = base::Bind( + &TranslateSuccessResponse<OutArgs...>, success_callback, error_callback); + + object->CallMethodWithErrorCallback( + &method_call, timeout_ms, dbus_success_callback, dbus_error_callback); +} + +// Same as CallMethodWithTimeout() but uses a default timeout value. +template<typename... InArgs, typename... OutArgs> +inline void CallMethod(dbus::ObjectProxy* object, + const std::string& interface_name, + const std::string& method_name, + const base::Callback<void(OutArgs...)>& success_callback, + const AsyncErrorCallback& error_callback, + const InArgs&... params) { + return CallMethodWithTimeout(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + object, + interface_name, + method_name, + success_callback, + error_callback, + params...); +} + +} // namespace dbus_utils +} // namespace brillo + +#endif // LIBCHROMEOS_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_ |