xref: /aosp_15_r20/external/libbrillo/brillo/dbus/dbus_method_invoker.h (revision 1a96fba65179ea7d3f56207137718607415c5953)
1*1a96fba6SXin Li // Copyright 2014 The Chromium OS Authors. All rights reserved.
2*1a96fba6SXin Li // Use of this source code is governed by a BSD-style license that can be
3*1a96fba6SXin Li // found in the LICENSE file.
4*1a96fba6SXin Li 
5*1a96fba6SXin Li // This file provides a way to call D-Bus methods on objects in remote processes
6*1a96fba6SXin Li // as if they were native C++ function calls.
7*1a96fba6SXin Li 
8*1a96fba6SXin Li // CallMethodAndBlock (along with CallMethodAndBlockWithTimeout) lets you call
9*1a96fba6SXin Li // a D-Bus method synchronously and pass all the required parameters as C++
10*1a96fba6SXin Li // function arguments. CallMethodAndBlock relies on automatic C++ to D-Bus data
11*1a96fba6SXin Li // serialization implemented in brillo/dbus/data_serialization.h.
12*1a96fba6SXin Li // CallMethodAndBlock invokes the D-Bus method and returns the Response.
13*1a96fba6SXin Li 
14*1a96fba6SXin Li // The method call response should be parsed with ExtractMethodCallResults().
15*1a96fba6SXin Li // The method takes an optional list of pointers to the expected return values
16*1a96fba6SXin Li // of the D-Bus method.
17*1a96fba6SXin Li 
18*1a96fba6SXin Li // CallMethod and CallMethodWithTimeout are similar to CallMethodAndBlock but
19*1a96fba6SXin Li // make the calls asynchronously. They take two callbacks: one for successful
20*1a96fba6SXin Li // method invocation and the second is for error conditions.
21*1a96fba6SXin Li 
22*1a96fba6SXin Li // Here is an example of synchronous calls:
23*1a96fba6SXin Li // Call "std::string MyInterface::MyMethod(int, double)" over D-Bus:
24*1a96fba6SXin Li 
25*1a96fba6SXin Li //  using brillo::dbus_utils::CallMethodAndBlock;
26*1a96fba6SXin Li //  using brillo::dbus_utils::ExtractMethodCallResults;
27*1a96fba6SXin Li //
28*1a96fba6SXin Li //  brillo::ErrorPtr error;
29*1a96fba6SXin Li //  auto resp = CallMethodAndBlock(obj,
30*1a96fba6SXin Li //                                 "org.chromium.MyService.MyInterface",
31*1a96fba6SXin Li //                                 "MyMethod",
32*1a96fba6SXin Li //                                 &error,
33*1a96fba6SXin Li //                                 2, 8.7);
34*1a96fba6SXin Li //  std::string return_value;
35*1a96fba6SXin Li //  if (resp && ExtractMethodCallResults(resp.get(), &error, &return_value)) {
36*1a96fba6SXin Li //    // Use the |return_value|.
37*1a96fba6SXin Li //  } else {
38*1a96fba6SXin Li //    // An error occurred. Use |error| to get details.
39*1a96fba6SXin Li //  }
40*1a96fba6SXin Li 
41*1a96fba6SXin Li // And here is how to call D-Bus methods asynchronously:
42*1a96fba6SXin Li // Call "std::string MyInterface::MyMethod(int, double)" over D-Bus:
43*1a96fba6SXin Li 
44*1a96fba6SXin Li //  using brillo::dbus_utils::CallMethod;
45*1a96fba6SXin Li //  using brillo::dbus_utils::ExtractMethodCallResults;
46*1a96fba6SXin Li //
47*1a96fba6SXin Li //  void OnSuccess(const std::string& return_value) {
48*1a96fba6SXin Li //    // Use the |return_value|.
49*1a96fba6SXin Li //  }
50*1a96fba6SXin Li //
51*1a96fba6SXin Li //  void OnError(brillo::Error* error) {
52*1a96fba6SXin Li //    // An error occurred. Use |error| to get details.
53*1a96fba6SXin Li //  }
54*1a96fba6SXin Li //
55*1a96fba6SXin Li //  brillo::dbus_utils::CallMethod(obj,
56*1a96fba6SXin Li //                                   "org.chromium.MyService.MyInterface",
57*1a96fba6SXin Li //                                   "MyMethod",
58*1a96fba6SXin Li //                                   base::Bind(OnSuccess),
59*1a96fba6SXin Li //                                   base::Bind(OnError),
60*1a96fba6SXin Li //                                   2, 8.7);
61*1a96fba6SXin Li 
62*1a96fba6SXin Li #ifndef LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
63*1a96fba6SXin Li #define LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
64*1a96fba6SXin Li 
65*1a96fba6SXin Li #include <memory>
66*1a96fba6SXin Li #include <string>
67*1a96fba6SXin Li #include <tuple>
68*1a96fba6SXin Li #include <utility>
69*1a96fba6SXin Li 
70*1a96fba6SXin Li #include <base/bind.h>
71*1a96fba6SXin Li #include <base/files/scoped_file.h>
72*1a96fba6SXin Li #include <brillo/dbus/dbus_param_reader.h>
73*1a96fba6SXin Li #include <brillo/dbus/dbus_param_writer.h>
74*1a96fba6SXin Li #include <brillo/dbus/file_descriptor.h>
75*1a96fba6SXin Li #include <brillo/dbus/utils.h>
76*1a96fba6SXin Li #include <brillo/errors/error.h>
77*1a96fba6SXin Li #include <brillo/errors/error_codes.h>
78*1a96fba6SXin Li #include <brillo/brillo_export.h>
79*1a96fba6SXin Li #include <dbus/message.h>
80*1a96fba6SXin Li #include <dbus/object_proxy.h>
81*1a96fba6SXin Li 
82*1a96fba6SXin Li namespace brillo {
83*1a96fba6SXin Li namespace dbus_utils {
84*1a96fba6SXin Li 
85*1a96fba6SXin Li // A helper method to dispatch a blocking D-Bus method call. Can specify
86*1a96fba6SXin Li // zero or more method call arguments in |args| which will be sent over D-Bus.
87*1a96fba6SXin Li // This method sends a D-Bus message and blocks for a time period specified
88*1a96fba6SXin Li // in |timeout_ms| while waiting for a reply. The time out is in milliseconds or
89*1a96fba6SXin Li // -1 (DBUS_TIMEOUT_USE_DEFAULT) for default, or DBUS_TIMEOUT_INFINITE for no
90*1a96fba6SXin Li // timeout. If a timeout occurs, the response object contains an error object
91*1a96fba6SXin Li // with DBUS_ERROR_NO_REPLY error code (those constants come from libdbus
92*1a96fba6SXin Li // [dbus/dbus.h]).
93*1a96fba6SXin Li // Returns a dbus::Response object on success. On failure, returns nullptr and
94*1a96fba6SXin Li // fills in additional error details into the |error| object.
95*1a96fba6SXin Li template <typename... Args>
CallMethodAndBlockWithTimeout(int timeout_ms,::dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,ErrorPtr * error,const Args &...args)96*1a96fba6SXin Li inline std::unique_ptr<::dbus::Response> CallMethodAndBlockWithTimeout(
97*1a96fba6SXin Li     int timeout_ms,
98*1a96fba6SXin Li     ::dbus::ObjectProxy* object,
99*1a96fba6SXin Li     const std::string& interface_name,
100*1a96fba6SXin Li     const std::string& method_name,
101*1a96fba6SXin Li     ErrorPtr* error,
102*1a96fba6SXin Li     const Args&... args) {
103*1a96fba6SXin Li   ::dbus::MethodCall method_call(interface_name, method_name);
104*1a96fba6SXin Li   // Add method arguments to the message buffer.
105*1a96fba6SXin Li   ::dbus::MessageWriter writer(&method_call);
106*1a96fba6SXin Li   DBusParamWriter::Append(&writer, args...);
107*1a96fba6SXin Li   ::dbus::ScopedDBusError dbus_error;
108*1a96fba6SXin Li   auto response = object->CallMethodAndBlockWithErrorDetails(
109*1a96fba6SXin Li       &method_call, timeout_ms, &dbus_error);
110*1a96fba6SXin Li   if (!response) {
111*1a96fba6SXin Li     if (dbus_error.is_set()) {
112*1a96fba6SXin Li       Error::AddTo(error,
113*1a96fba6SXin Li                    FROM_HERE,
114*1a96fba6SXin Li                    errors::dbus::kDomain,
115*1a96fba6SXin Li                    dbus_error.name(),
116*1a96fba6SXin Li                    dbus_error.message());
117*1a96fba6SXin Li     } else {
118*1a96fba6SXin Li       Error::AddToPrintf(error,
119*1a96fba6SXin Li                          FROM_HERE,
120*1a96fba6SXin Li                          errors::dbus::kDomain,
121*1a96fba6SXin Li                          DBUS_ERROR_FAILED,
122*1a96fba6SXin Li                          "Failed to call D-Bus method: %s.%s",
123*1a96fba6SXin Li                          interface_name.c_str(),
124*1a96fba6SXin Li                          method_name.c_str());
125*1a96fba6SXin Li     }
126*1a96fba6SXin Li   }
127*1a96fba6SXin Li   return response;
128*1a96fba6SXin Li }
129*1a96fba6SXin Li 
130*1a96fba6SXin Li // Same as CallMethodAndBlockWithTimeout() but uses a default timeout value.
131*1a96fba6SXin Li template <typename... Args>
CallMethodAndBlock(::dbus::ObjectProxy * object,const std::string & interface_name,const std::string & method_name,ErrorPtr * error,const Args &...args)132*1a96fba6SXin Li inline std::unique_ptr<::dbus::Response> CallMethodAndBlock(
133*1a96fba6SXin Li     ::dbus::ObjectProxy* object,
134*1a96fba6SXin Li     const std::string& interface_name,
135*1a96fba6SXin Li     const std::string& method_name,
136*1a96fba6SXin Li     ErrorPtr* error,
137*1a96fba6SXin Li     const Args&... args) {
138*1a96fba6SXin Li   return CallMethodAndBlockWithTimeout(::dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
139*1a96fba6SXin Li                                        object, interface_name, method_name,
140*1a96fba6SXin Li                                        error, args...);
141*1a96fba6SXin Li }
142*1a96fba6SXin Li 
143*1a96fba6SXin Li namespace internal {
144*1a96fba6SXin Li // In order to support non-copyable file descriptor types, we have this
145*1a96fba6SXin Li // internal::HackMove() helper function that does really nothing for normal
146*1a96fba6SXin Li // types but uses Pass() for file descriptors so we can move them out from
147*1a96fba6SXin Li // the temporaries created inside DBusParamReader<...>::Invoke().
148*1a96fba6SXin Li // If only libchrome supported real rvalues so we can just do std::move() and
149*1a96fba6SXin Li // be done with it.
150*1a96fba6SXin Li template <typename T>
HackMove(const T & val)151*1a96fba6SXin Li inline const T& HackMove(const T& val) {
152*1a96fba6SXin Li   return val;
153*1a96fba6SXin Li }
154*1a96fba6SXin Li 
155*1a96fba6SXin Li // Even though |val| here is passed as const&, the actual value is created
156*1a96fba6SXin Li // inside DBusParamReader<...>::Invoke() and is temporary in nature, so it is
157*1a96fba6SXin Li // safe to move the file descriptor out of |val|. That's why we are doing
158*1a96fba6SXin Li // const_cast here. It is a bit hacky, but there is no negative side effects.
HackMove(const base::ScopedFD & val)159*1a96fba6SXin Li inline base::ScopedFD HackMove(const base::ScopedFD& val) {
160*1a96fba6SXin Li   return std::move(const_cast<base::ScopedFD&>(val));
161*1a96fba6SXin Li }
HackMove(const FileDescriptor & val)162*1a96fba6SXin Li inline FileDescriptor HackMove(const FileDescriptor& val) {
163*1a96fba6SXin Li   return std::move(const_cast<FileDescriptor&>(val));
164*1a96fba6SXin Li }
165*1a96fba6SXin Li }  // namespace internal
166*1a96fba6SXin Li 
167*1a96fba6SXin Li // Extracts the parameters of |ResultTypes...| types from the message reader
168*1a96fba6SXin Li // and puts the values in the resulting |tuple|. Returns false on error and
169*1a96fba6SXin Li // provides additional error details in |error| object.
170*1a96fba6SXin Li template <typename... ResultTypes>
ExtractMessageParametersAsTuple(::dbus::MessageReader * reader,ErrorPtr * error,std::tuple<ResultTypes...> * val_tuple)171*1a96fba6SXin Li inline bool ExtractMessageParametersAsTuple(
172*1a96fba6SXin Li     ::dbus::MessageReader* reader,
173*1a96fba6SXin Li     ErrorPtr* error,
174*1a96fba6SXin Li     std::tuple<ResultTypes...>* val_tuple) {
175*1a96fba6SXin Li   auto callback = [val_tuple](const ResultTypes&... params) {
176*1a96fba6SXin Li     *val_tuple = std::forward_as_tuple(params...);
177*1a96fba6SXin Li   };
178*1a96fba6SXin Li   return DBusParamReader<false, ResultTypes...>::Invoke(
179*1a96fba6SXin Li       callback, reader, error);
180*1a96fba6SXin Li }
181*1a96fba6SXin Li // Overload of ExtractMessageParametersAsTuple to handle reference types in
182*1a96fba6SXin Li // tuples created with std::tie().
183*1a96fba6SXin Li template <typename... ResultTypes>
ExtractMessageParametersAsTuple(::dbus::MessageReader * reader,ErrorPtr * error,std::tuple<ResultTypes &...> * ref_tuple)184*1a96fba6SXin Li inline bool ExtractMessageParametersAsTuple(
185*1a96fba6SXin Li     ::dbus::MessageReader* reader,
186*1a96fba6SXin Li     ErrorPtr* error,
187*1a96fba6SXin Li     std::tuple<ResultTypes&...>* ref_tuple) {
188*1a96fba6SXin Li   auto callback = [ref_tuple](const ResultTypes&... params) {
189*1a96fba6SXin Li     *ref_tuple = std::forward_as_tuple(params...);
190*1a96fba6SXin Li   };
191*1a96fba6SXin Li   return DBusParamReader<false, ResultTypes...>::Invoke(
192*1a96fba6SXin Li       callback, reader, error);
193*1a96fba6SXin Li }
194*1a96fba6SXin Li 
195*1a96fba6SXin Li // A helper method to extract a list of values from a message buffer.
196*1a96fba6SXin Li // The function will return false and provide detailed error information on
197*1a96fba6SXin Li // failure. It fails if the D-Bus message buffer (represented by the |reader|)
198*1a96fba6SXin Li // contains too many, too few parameters or the parameters are of wrong types
199*1a96fba6SXin Li // (signatures).
200*1a96fba6SXin Li // The usage pattern is as follows:
201*1a96fba6SXin Li //
202*1a96fba6SXin Li //  int32_t data1;
203*1a96fba6SXin Li //  std::string data2;
204*1a96fba6SXin Li //  ErrorPtr error;
205*1a96fba6SXin Li //  if (ExtractMessageParameters(reader, &error, &data1, &data2)) { ... }
206*1a96fba6SXin Li //
207*1a96fba6SXin Li // The above example extracts an Int32 and a String from D-Bus message buffer.
208*1a96fba6SXin Li template <typename... ResultTypes>
ExtractMessageParameters(::dbus::MessageReader * reader,ErrorPtr * error,ResultTypes * ...results)209*1a96fba6SXin Li inline bool ExtractMessageParameters(::dbus::MessageReader* reader,
210*1a96fba6SXin Li                                      ErrorPtr* error,
211*1a96fba6SXin Li                                      ResultTypes*... results) {
212*1a96fba6SXin Li   auto ref_tuple = std::tie(*results...);
213*1a96fba6SXin Li   return ExtractMessageParametersAsTuple<ResultTypes...>(
214*1a96fba6SXin Li       reader, error, &ref_tuple);
215*1a96fba6SXin Li }
216*1a96fba6SXin Li 
217*1a96fba6SXin Li // Convenient helper method to extract return value(s) of a D-Bus method call.
218*1a96fba6SXin Li // |results| must be zero or more pointers to data expected to be returned
219*1a96fba6SXin Li // from the method called. If an error occurs, returns false and provides
220*1a96fba6SXin Li // additional details in |error| object.
221*1a96fba6SXin Li //
222*1a96fba6SXin Li // It is OK to call this method even if the D-Bus method doesn't expect
223*1a96fba6SXin Li // any return values. Just do not specify any output |results|. In this case,
224*1a96fba6SXin Li // ExtractMethodCallResults() will verify that the method didn't return any
225*1a96fba6SXin Li // data in the |message|.
226*1a96fba6SXin Li template <typename... ResultTypes>
ExtractMethodCallResults(::dbus::Message * message,ErrorPtr * error,ResultTypes * ...results)227*1a96fba6SXin Li inline bool ExtractMethodCallResults(::dbus::Message* message,
228*1a96fba6SXin Li                                      ErrorPtr* error,
229*1a96fba6SXin Li                                      ResultTypes*... results) {
230*1a96fba6SXin Li   CHECK(message) << "Unable to extract parameters from a NULL message.";
231*1a96fba6SXin Li 
232*1a96fba6SXin Li   ::dbus::MessageReader reader(message);
233*1a96fba6SXin Li   if (message->GetMessageType() == ::dbus::Message::MESSAGE_ERROR) {
234*1a96fba6SXin Li     std::string error_message;
235*1a96fba6SXin Li     if (ExtractMessageParameters(&reader, error, &error_message))
236*1a96fba6SXin Li       AddDBusError(error, message->GetErrorName(), error_message);
237*1a96fba6SXin Li     return false;
238*1a96fba6SXin Li   }
239*1a96fba6SXin Li   return ExtractMessageParameters(&reader, error, results...);
240*1a96fba6SXin Li }
241*1a96fba6SXin Li 
242*1a96fba6SXin Li //////////////////////////////////////////////////////////////////////////////
243*1a96fba6SXin Li // Asynchronous method invocation support
244*1a96fba6SXin Li 
245*1a96fba6SXin Li using AsyncErrorCallback = base::Callback<void(Error* error)>;
246*1a96fba6SXin Li 
247*1a96fba6SXin Li // A helper function that translates dbus::ErrorResponse response
248*1a96fba6SXin Li // from D-Bus into brillo::Error* and invokes the |callback|.
249*1a96fba6SXin Li void BRILLO_EXPORT TranslateErrorResponse(const AsyncErrorCallback& callback,
250*1a96fba6SXin Li                                           ::dbus::ErrorResponse* resp);
251*1a96fba6SXin Li 
252*1a96fba6SXin Li // A helper function that translates dbus::Response from D-Bus into
253*1a96fba6SXin Li // a list of C++ values passed as parameters to |success_callback|. If the
254*1a96fba6SXin Li // response message doesn't have the correct number of parameters, or they
255*1a96fba6SXin Li // are of wrong types, an error is sent to |error_callback|.
256*1a96fba6SXin Li template <typename... OutArgs>
TranslateSuccessResponse(const base::Callback<void (OutArgs...)> & success_callback,const AsyncErrorCallback & error_callback,::dbus::Response * resp)257*1a96fba6SXin Li void TranslateSuccessResponse(
258*1a96fba6SXin Li     const base::Callback<void(OutArgs...)>& success_callback,
259*1a96fba6SXin Li     const AsyncErrorCallback& error_callback,
260*1a96fba6SXin Li     ::dbus::Response* resp) {
261*1a96fba6SXin Li   auto callback = [&success_callback](const OutArgs&... params) {
262*1a96fba6SXin Li     if (!success_callback.is_null()) {
263*1a96fba6SXin Li       success_callback.Run(params...);
264*1a96fba6SXin Li     }
265*1a96fba6SXin Li   };
266*1a96fba6SXin Li   ErrorPtr error;
267*1a96fba6SXin Li   ::dbus::MessageReader reader(resp);
268*1a96fba6SXin Li   if (!DBusParamReader<false, OutArgs...>::Invoke(callback, &reader, &error) &&
269*1a96fba6SXin Li       !error_callback.is_null()) {
270*1a96fba6SXin Li     error_callback.Run(error.get());
271*1a96fba6SXin Li   }
272*1a96fba6SXin Li }
273*1a96fba6SXin Li 
274*1a96fba6SXin Li // A helper method to dispatch a non-blocking D-Bus method call. Can specify
275*1a96fba6SXin Li // zero or more method call arguments in |params| which will be sent over D-Bus.
276*1a96fba6SXin Li // This method sends a D-Bus message and returns immediately.
277*1a96fba6SXin Li // When the remote method returns successfully, the success callback is
278*1a96fba6SXin Li // invoked with the return value(s), if any.
279*1a96fba6SXin Li // On error, the error callback is called. Note, the error callback can be
280*1a96fba6SXin Li // called synchronously (before CallMethodWithTimeout returns) if there was
281*1a96fba6SXin Li // a problem invoking a method (e.g. object or method doesn't exist).
282*1a96fba6SXin Li // If the response is not received within |timeout_ms|, an error callback is
283*1a96fba6SXin Li // called with DBUS_ERROR_NO_REPLY error code.
284*1a96fba6SXin Li template <typename... InArgs, typename... OutArgs>
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)285*1a96fba6SXin Li inline void CallMethodWithTimeout(
286*1a96fba6SXin Li     int timeout_ms,
287*1a96fba6SXin Li     ::dbus::ObjectProxy* object,
288*1a96fba6SXin Li     const std::string& interface_name,
289*1a96fba6SXin Li     const std::string& method_name,
290*1a96fba6SXin Li     const base::Callback<void(OutArgs...)>& success_callback,
291*1a96fba6SXin Li     const AsyncErrorCallback& error_callback,
292*1a96fba6SXin Li     const InArgs&... params) {
293*1a96fba6SXin Li   ::dbus::MethodCall method_call(interface_name, method_name);
294*1a96fba6SXin Li   ::dbus::MessageWriter writer(&method_call);
295*1a96fba6SXin Li   DBusParamWriter::Append(&writer, params...);
296*1a96fba6SXin Li 
297*1a96fba6SXin Li   ::dbus::ObjectProxy::ErrorCallback dbus_error_callback =
298*1a96fba6SXin Li       base::Bind(&TranslateErrorResponse, error_callback);
299*1a96fba6SXin Li   ::dbus::ObjectProxy::ResponseCallback dbus_success_callback = base::Bind(
300*1a96fba6SXin Li       &TranslateSuccessResponse<OutArgs...>, success_callback, error_callback);
301*1a96fba6SXin Li 
302*1a96fba6SXin Li   object->CallMethodWithErrorCallback(&method_call, timeout_ms,
303*1a96fba6SXin Li                                       std::move(dbus_success_callback),
304*1a96fba6SXin Li                                       std::move(dbus_error_callback));
305*1a96fba6SXin Li }
306*1a96fba6SXin Li 
307*1a96fba6SXin Li // Same as CallMethodWithTimeout() but uses a default timeout value.
308*1a96fba6SXin Li template <typename... InArgs, typename... OutArgs>
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)309*1a96fba6SXin Li inline void CallMethod(::dbus::ObjectProxy* object,
310*1a96fba6SXin Li                        const std::string& interface_name,
311*1a96fba6SXin Li                        const std::string& method_name,
312*1a96fba6SXin Li                        const base::Callback<void(OutArgs...)>& success_callback,
313*1a96fba6SXin Li                        const AsyncErrorCallback& error_callback,
314*1a96fba6SXin Li                        const InArgs&... params) {
315*1a96fba6SXin Li   return CallMethodWithTimeout(::dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, object,
316*1a96fba6SXin Li                                interface_name, method_name, success_callback,
317*1a96fba6SXin Li                                error_callback, params...);
318*1a96fba6SXin Li }
319*1a96fba6SXin Li 
320*1a96fba6SXin Li }  // namespace dbus_utils
321*1a96fba6SXin Li }  // namespace brillo
322*1a96fba6SXin Li 
323*1a96fba6SXin Li #endif  // LIBBRILLO_BRILLO_DBUS_DBUS_METHOD_INVOKER_H_
324