xref: /aosp_15_r20/external/libbrillo/brillo/glib/dbus.cc (revision 1a96fba65179ea7d3f56207137718607415c5953)
1*1a96fba6SXin Li // Copyright (c) 2009 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 #include "brillo/glib/dbus.h"
6*1a96fba6SXin Li 
7*1a96fba6SXin Li #include <dbus/dbus.h>
8*1a96fba6SXin Li #include <dbus/dbus-glib-bindings.h>
9*1a96fba6SXin Li #include <dbus/dbus-glib-lowlevel.h>
10*1a96fba6SXin Li 
11*1a96fba6SXin Li #include <base/logging.h>
12*1a96fba6SXin Li #include <base/strings/stringprintf.h>
13*1a96fba6SXin Li 
14*1a96fba6SXin Li namespace brillo {
15*1a96fba6SXin Li namespace dbus {
16*1a96fba6SXin Li 
CallPtrArray(const Proxy & proxy,const char * method,glib::ScopedPtrArray<const char * > * result)17*1a96fba6SXin Li bool CallPtrArray(const Proxy& proxy,
18*1a96fba6SXin Li                   const char* method,
19*1a96fba6SXin Li                   glib::ScopedPtrArray<const char*>* result) {
20*1a96fba6SXin Li   glib::ScopedError error;
21*1a96fba6SXin Li 
22*1a96fba6SXin Li   ::GType g_type_array = ::dbus_g_type_get_collection("GPtrArray",
23*1a96fba6SXin Li                                                        DBUS_TYPE_G_OBJECT_PATH);
24*1a96fba6SXin Li 
25*1a96fba6SXin Li 
26*1a96fba6SXin Li   if (!::dbus_g_proxy_call(proxy.gproxy(), method, &Resetter(&error).lvalue(),
27*1a96fba6SXin Li                            G_TYPE_INVALID, g_type_array,
28*1a96fba6SXin Li                            &Resetter(result).lvalue(), G_TYPE_INVALID)) {
29*1a96fba6SXin Li     LOG(WARNING) << "CallPtrArray failed: "
30*1a96fba6SXin Li         << (error->message ? error->message : "Unknown Error.");
31*1a96fba6SXin Li     return false;
32*1a96fba6SXin Li   }
33*1a96fba6SXin Li 
34*1a96fba6SXin Li   return true;
35*1a96fba6SXin Li }
36*1a96fba6SXin Li 
GetSystemBusConnection()37*1a96fba6SXin Li BusConnection GetSystemBusConnection() {
38*1a96fba6SXin Li   glib::ScopedError error;
39*1a96fba6SXin Li   ::DBusGConnection* result = ::dbus_g_bus_get(DBUS_BUS_SYSTEM,
40*1a96fba6SXin Li                                                &Resetter(&error).lvalue());
41*1a96fba6SXin Li   if (!result) {
42*1a96fba6SXin Li     LOG(ERROR) << "dbus_g_bus_get(DBUS_BUS_SYSTEM) failed: "
43*1a96fba6SXin Li                << ((error.get() && error->message) ?
44*1a96fba6SXin Li                    error->message : "Unknown Error");
45*1a96fba6SXin Li     return BusConnection(nullptr);
46*1a96fba6SXin Li   }
47*1a96fba6SXin Li   // Set to not exit when system bus is disconnected.
48*1a96fba6SXin Li   // This fixes the problem where when the dbus daemon is stopped, exit is
49*1a96fba6SXin Li   // called which kills Chrome.
50*1a96fba6SXin Li   ::dbus_connection_set_exit_on_disconnect(
51*1a96fba6SXin Li       ::dbus_g_connection_get_connection(result), FALSE);
52*1a96fba6SXin Li   return BusConnection(result);
53*1a96fba6SXin Li }
54*1a96fba6SXin Li 
GetPrivateBusConnection(const char * address)55*1a96fba6SXin Li BusConnection GetPrivateBusConnection(const char* address) {
56*1a96fba6SXin Li   // Since dbus-glib does not have an API like dbus_g_connection_open_private(),
57*1a96fba6SXin Li   // we have to implement our own.
58*1a96fba6SXin Li 
59*1a96fba6SXin Li   // We have to call _dbus_g_value_types_init() to register standard marshalers
60*1a96fba6SXin Li   // just like as dbus_g_bus_get() and dbus_g_connection_open() do, but the
61*1a96fba6SXin Li   // function is not exported. So we call GetPrivateBusConnection() which calls
62*1a96fba6SXin Li   // dbus_g_bus_get() here instead. Note that if we don't call
63*1a96fba6SXin Li   // _dbus_g_value_types_init(), we might get "WARNING **: No demarshaller
64*1a96fba6SXin Li   // registered for type xxxxx" error and might not be able to handle incoming
65*1a96fba6SXin Li   // signals nor method calls.
66*1a96fba6SXin Li   {
67*1a96fba6SXin Li     BusConnection system_bus_connection = GetSystemBusConnection();
68*1a96fba6SXin Li     if (!system_bus_connection.HasConnection()) {
69*1a96fba6SXin Li       return system_bus_connection;  // returns NULL connection.
70*1a96fba6SXin Li     }
71*1a96fba6SXin Li   }
72*1a96fba6SXin Li 
73*1a96fba6SXin Li   ::DBusError error;
74*1a96fba6SXin Li   ::dbus_error_init(&error);
75*1a96fba6SXin Li 
76*1a96fba6SXin Li   ::DBusGConnection* result = nullptr;
77*1a96fba6SXin Li   ::DBusConnection* raw_connection
78*1a96fba6SXin Li         = ::dbus_connection_open_private(address, &error);
79*1a96fba6SXin Li   if (!raw_connection) {
80*1a96fba6SXin Li     LOG(WARNING) << "dbus_connection_open_private failed: " << address;
81*1a96fba6SXin Li     return BusConnection(nullptr);
82*1a96fba6SXin Li   }
83*1a96fba6SXin Li 
84*1a96fba6SXin Li   if (!::dbus_bus_register(raw_connection, &error)) {
85*1a96fba6SXin Li     LOG(ERROR) << "dbus_bus_register failed: "
86*1a96fba6SXin Li                << (error.message ? error.message : "Unknown Error.");
87*1a96fba6SXin Li     ::dbus_error_free(&error);
88*1a96fba6SXin Li     // TODO(yusukes): We don't call dbus_connection_close() nor g_object_unref()
89*1a96fba6SXin Li     // here for now since these calls might interfere with IBusBus connections
90*1a96fba6SXin Li     // in libcros and Chrome. See the comment in ~InputMethodStatusConnection()
91*1a96fba6SXin Li     // function in platform/cros/chromeos_input_method.cc for details.
92*1a96fba6SXin Li     return BusConnection(nullptr);
93*1a96fba6SXin Li   }
94*1a96fba6SXin Li 
95*1a96fba6SXin Li   ::dbus_connection_setup_with_g_main(
96*1a96fba6SXin Li       raw_connection, nullptr /* default context */);
97*1a96fba6SXin Li 
98*1a96fba6SXin Li   // A reference count of |raw_connection| is transferred to |result|. You don't
99*1a96fba6SXin Li   // have to (and should not) unref the |raw_connection|.
100*1a96fba6SXin Li   result = ::dbus_connection_get_g_connection(raw_connection);
101*1a96fba6SXin Li   CHECK(result);
102*1a96fba6SXin Li 
103*1a96fba6SXin Li   ::dbus_connection_set_exit_on_disconnect(
104*1a96fba6SXin Li       ::dbus_g_connection_get_connection(result), FALSE);
105*1a96fba6SXin Li 
106*1a96fba6SXin Li   return BusConnection(result);
107*1a96fba6SXin Li }
108*1a96fba6SXin Li 
RetrieveProperties(const Proxy & proxy,const char * interface,glib::ScopedHashTable * result)109*1a96fba6SXin Li bool RetrieveProperties(const Proxy& proxy,
110*1a96fba6SXin Li                         const char* interface,
111*1a96fba6SXin Li                         glib::ScopedHashTable* result) {
112*1a96fba6SXin Li   glib::ScopedError error;
113*1a96fba6SXin Li 
114*1a96fba6SXin Li   if (!::dbus_g_proxy_call(proxy.gproxy(), "GetAll", &Resetter(&error).lvalue(),
115*1a96fba6SXin Li                            G_TYPE_STRING, interface, G_TYPE_INVALID,
116*1a96fba6SXin Li                            ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
117*1a96fba6SXin Li                                                  G_TYPE_VALUE),
118*1a96fba6SXin Li                            &Resetter(result).lvalue(), G_TYPE_INVALID)) {
119*1a96fba6SXin Li     LOG(WARNING) << "RetrieveProperties failed: "
120*1a96fba6SXin Li         << (error->message ? error->message : "Unknown Error.");
121*1a96fba6SXin Li     return false;
122*1a96fba6SXin Li   }
123*1a96fba6SXin Li   return true;
124*1a96fba6SXin Li }
125*1a96fba6SXin Li 
Proxy()126*1a96fba6SXin Li Proxy::Proxy()
127*1a96fba6SXin Li     : object_(nullptr) {
128*1a96fba6SXin Li }
129*1a96fba6SXin Li 
130*1a96fba6SXin Li // Set |connect_to_name_owner| true if you'd like to use
131*1a96fba6SXin Li // dbus_g_proxy_new_for_name_owner() rather than dbus_g_proxy_new_for_name().
Proxy(const BusConnection & connection,const char * name,const char * path,const char * interface,bool connect_to_name_owner)132*1a96fba6SXin Li Proxy::Proxy(const BusConnection& connection,
133*1a96fba6SXin Li              const char* name,
134*1a96fba6SXin Li              const char* path,
135*1a96fba6SXin Li              const char* interface,
136*1a96fba6SXin Li              bool connect_to_name_owner)
137*1a96fba6SXin Li     : object_(GetGProxy(
138*1a96fba6SXin Li         connection, name, path, interface, connect_to_name_owner)) {
139*1a96fba6SXin Li }
140*1a96fba6SXin Li 
141*1a96fba6SXin Li // Equivalent to Proxy(connection, name, path, interface, false).
Proxy(const BusConnection & connection,const char * name,const char * path,const char * interface)142*1a96fba6SXin Li Proxy::Proxy(const BusConnection& connection,
143*1a96fba6SXin Li              const char* name,
144*1a96fba6SXin Li              const char* path,
145*1a96fba6SXin Li              const char* interface)
146*1a96fba6SXin Li     : object_(GetGProxy(connection, name, path, interface, false)) {
147*1a96fba6SXin Li }
148*1a96fba6SXin Li 
149*1a96fba6SXin Li // Creates a peer proxy using dbus_g_proxy_new_for_peer.
Proxy(const BusConnection & connection,const char * path,const char * interface)150*1a96fba6SXin Li Proxy::Proxy(const BusConnection& connection,
151*1a96fba6SXin Li              const char* path,
152*1a96fba6SXin Li              const char* interface)
153*1a96fba6SXin Li     : object_(GetGPeerProxy(connection, path, interface)) {
154*1a96fba6SXin Li }
155*1a96fba6SXin Li 
Proxy(const Proxy & x)156*1a96fba6SXin Li Proxy::Proxy(const Proxy& x)
157*1a96fba6SXin Li     : object_(x.object_) {
158*1a96fba6SXin Li   if (object_)
159*1a96fba6SXin Li     ::g_object_ref(object_);
160*1a96fba6SXin Li }
161*1a96fba6SXin Li 
~Proxy()162*1a96fba6SXin Li Proxy::~Proxy() {
163*1a96fba6SXin Li   if (object_)
164*1a96fba6SXin Li     ::g_object_unref(object_);
165*1a96fba6SXin Li }
166*1a96fba6SXin Li 
167*1a96fba6SXin Li /* static */
GetGProxy(const BusConnection & connection,const char * name,const char * path,const char * interface,bool connect_to_name_owner)168*1a96fba6SXin Li Proxy::value_type Proxy::GetGProxy(const BusConnection& connection,
169*1a96fba6SXin Li                                    const char* name,
170*1a96fba6SXin Li                                    const char* path,
171*1a96fba6SXin Li                                    const char* interface,
172*1a96fba6SXin Li                                    bool connect_to_name_owner) {
173*1a96fba6SXin Li   value_type result = nullptr;
174*1a96fba6SXin Li   if (connect_to_name_owner) {
175*1a96fba6SXin Li     glib::ScopedError error;
176*1a96fba6SXin Li     result = ::dbus_g_proxy_new_for_name_owner(connection.object_,
177*1a96fba6SXin Li                                                name,
178*1a96fba6SXin Li                                                path,
179*1a96fba6SXin Li                                                interface,
180*1a96fba6SXin Li                                                &Resetter(&error).lvalue());
181*1a96fba6SXin Li     if (!result) {
182*1a96fba6SXin Li       DLOG(ERROR) << "Failed to construct proxy: "
183*1a96fba6SXin Li                   << (error->message ? error->message : "Unknown Error")
184*1a96fba6SXin Li                   << ": " << path;
185*1a96fba6SXin Li     }
186*1a96fba6SXin Li   } else {
187*1a96fba6SXin Li     result = ::dbus_g_proxy_new_for_name(connection.object_,
188*1a96fba6SXin Li                                          name,
189*1a96fba6SXin Li                                          path,
190*1a96fba6SXin Li                                          interface);
191*1a96fba6SXin Li     if (!result) {
192*1a96fba6SXin Li       LOG(ERROR) << "Failed to construct proxy: " << path;
193*1a96fba6SXin Li     }
194*1a96fba6SXin Li   }
195*1a96fba6SXin Li   return result;
196*1a96fba6SXin Li }
197*1a96fba6SXin Li 
198*1a96fba6SXin Li /* static */
GetGPeerProxy(const BusConnection & connection,const char * path,const char * interface)199*1a96fba6SXin Li Proxy::value_type Proxy::GetGPeerProxy(const BusConnection& connection,
200*1a96fba6SXin Li                                        const char* path,
201*1a96fba6SXin Li                                        const char* interface) {
202*1a96fba6SXin Li   value_type result = ::dbus_g_proxy_new_for_peer(connection.object_,
203*1a96fba6SXin Li                                                   path,
204*1a96fba6SXin Li                                                   interface);
205*1a96fba6SXin Li   if (!result)
206*1a96fba6SXin Li     LOG(ERROR) << "Failed to construct peer proxy: " << path;
207*1a96fba6SXin Li 
208*1a96fba6SXin Li   return result;
209*1a96fba6SXin Li }
210*1a96fba6SXin Li 
RegisterExclusiveService(const BusConnection & connection,const char * interface_name,const char * service_name,const char * service_path,GObject * object)211*1a96fba6SXin Li bool RegisterExclusiveService(const BusConnection& connection,
212*1a96fba6SXin Li                               const char* interface_name,
213*1a96fba6SXin Li                               const char* service_name,
214*1a96fba6SXin Li                               const char* service_path,
215*1a96fba6SXin Li                               GObject* object) {
216*1a96fba6SXin Li   CHECK(object);
217*1a96fba6SXin Li   CHECK(interface_name);
218*1a96fba6SXin Li   CHECK(service_name);
219*1a96fba6SXin Li   // Create a proxy to DBus itself so that we can request to become a
220*1a96fba6SXin Li   // service name owner and then register an object at the related service path.
221*1a96fba6SXin Li   Proxy proxy = brillo::dbus::Proxy(connection,
222*1a96fba6SXin Li                                       DBUS_SERVICE_DBUS,
223*1a96fba6SXin Li                                       DBUS_PATH_DBUS,
224*1a96fba6SXin Li                                       DBUS_INTERFACE_DBUS);
225*1a96fba6SXin Li   // Exclusivity is determined by replacing any existing
226*1a96fba6SXin Li   // service, not queuing, and ensuring we are the primary
227*1a96fba6SXin Li   // owner after the name is ours.
228*1a96fba6SXin Li   glib::ScopedError err;
229*1a96fba6SXin Li   guint result = 0;
230*1a96fba6SXin Li   // TODO(wad) determine if we are moving away from using generated functions
231*1a96fba6SXin Li   if (!org_freedesktop_DBus_request_name(proxy.gproxy(),
232*1a96fba6SXin Li                                          service_name,
233*1a96fba6SXin Li                                          0,
234*1a96fba6SXin Li                                          &result,
235*1a96fba6SXin Li                                          &Resetter(&err).lvalue())) {
236*1a96fba6SXin Li     LOG(ERROR) << "Unable to request service name: "
237*1a96fba6SXin Li                << (err->message ? err->message : "Unknown Error.");
238*1a96fba6SXin Li     return false;
239*1a96fba6SXin Li   }
240*1a96fba6SXin Li 
241*1a96fba6SXin Li   // Handle the error codes, releasing the name if exclusivity conditions
242*1a96fba6SXin Li   // are not met.
243*1a96fba6SXin Li   bool needs_release = false;
244*1a96fba6SXin Li   if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
245*1a96fba6SXin Li     LOG(ERROR) << "Failed to become the primary owner. Releasing . . .";
246*1a96fba6SXin Li     needs_release = true;
247*1a96fba6SXin Li   }
248*1a96fba6SXin Li   if (result == DBUS_REQUEST_NAME_REPLY_EXISTS) {
249*1a96fba6SXin Li     LOG(ERROR) << "Service name exists: " << service_name;
250*1a96fba6SXin Li     return false;
251*1a96fba6SXin Li   } else if (result == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
252*1a96fba6SXin Li     LOG(ERROR) << "Service name request enqueued despite our flags. Releasing";
253*1a96fba6SXin Li     needs_release = true;
254*1a96fba6SXin Li   }
255*1a96fba6SXin Li   LOG_IF(WARNING, result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER)
256*1a96fba6SXin Li     << "Service name already owned by this process";
257*1a96fba6SXin Li   if (needs_release) {
258*1a96fba6SXin Li     if (!org_freedesktop_DBus_release_name(
259*1a96fba6SXin Li            proxy.gproxy(),
260*1a96fba6SXin Li            service_name,
261*1a96fba6SXin Li            &result,
262*1a96fba6SXin Li            &Resetter(&err).lvalue())) {
263*1a96fba6SXin Li       LOG(ERROR) << "Unabled to release service name: "
264*1a96fba6SXin Li                  << (err->message ? err->message : "Unknown Error.");
265*1a96fba6SXin Li     }
266*1a96fba6SXin Li     DLOG(INFO) << "ReleaseName returned code " << result;
267*1a96fba6SXin Li     return false;
268*1a96fba6SXin Li   }
269*1a96fba6SXin Li 
270*1a96fba6SXin Li   // Determine a path from the service name and register the object.
271*1a96fba6SXin Li   dbus_g_connection_register_g_object(connection.g_connection(),
272*1a96fba6SXin Li                                       service_path,
273*1a96fba6SXin Li                                       object);
274*1a96fba6SXin Li   return true;
275*1a96fba6SXin Li }
276*1a96fba6SXin Li 
CallMethodWithNoArguments(const char * service_name,const char * path,const char * interface_name,const char * method_name)277*1a96fba6SXin Li void CallMethodWithNoArguments(const char* service_name,
278*1a96fba6SXin Li                                const char* path,
279*1a96fba6SXin Li                                const char* interface_name,
280*1a96fba6SXin Li                                const char* method_name) {
281*1a96fba6SXin Li   Proxy proxy(dbus::GetSystemBusConnection(),
282*1a96fba6SXin Li               service_name,
283*1a96fba6SXin Li               path,
284*1a96fba6SXin Li               interface_name);
285*1a96fba6SXin Li   ::dbus_g_proxy_call_no_reply(proxy.gproxy(), method_name, G_TYPE_INVALID);
286*1a96fba6SXin Li }
287*1a96fba6SXin Li 
StartMonitoring(const std::string & interface,const std::string & signal)288*1a96fba6SXin Li void SignalWatcher::StartMonitoring(const std::string& interface,
289*1a96fba6SXin Li                                     const std::string& signal) {
290*1a96fba6SXin Li   DCHECK(interface_.empty()) << "StartMonitoring() must be called only once";
291*1a96fba6SXin Li   interface_ = interface;
292*1a96fba6SXin Li   signal_ = signal;
293*1a96fba6SXin Li 
294*1a96fba6SXin Li   // Snoop on D-Bus messages so we can get notified about signals.
295*1a96fba6SXin Li   DBusConnection* dbus_conn = dbus_g_connection_get_connection(
296*1a96fba6SXin Li       GetSystemBusConnection().g_connection());
297*1a96fba6SXin Li   DCHECK(dbus_conn);
298*1a96fba6SXin Li 
299*1a96fba6SXin Li   DBusError error;
300*1a96fba6SXin Li   dbus_error_init(&error);
301*1a96fba6SXin Li   dbus_bus_add_match(dbus_conn, GetDBusMatchString().c_str(), &error);
302*1a96fba6SXin Li   if (dbus_error_is_set(&error)) {
303*1a96fba6SXin Li     LOG(DFATAL) << "Got error while adding D-Bus match rule: " << error.name
304*1a96fba6SXin Li                 << " (" << error.message << ")";
305*1a96fba6SXin Li   }
306*1a96fba6SXin Li 
307*1a96fba6SXin Li   if (!dbus_connection_add_filter(dbus_conn,
308*1a96fba6SXin Li                                   &SignalWatcher::FilterDBusMessage,
309*1a96fba6SXin Li                                   this,        // user_data
310*1a96fba6SXin Li                                   nullptr)) {  // free_data_function
311*1a96fba6SXin Li     LOG(DFATAL) << "Unable to add D-Bus filter";
312*1a96fba6SXin Li   }
313*1a96fba6SXin Li }
314*1a96fba6SXin Li 
~SignalWatcher()315*1a96fba6SXin Li SignalWatcher::~SignalWatcher() {
316*1a96fba6SXin Li   if (interface_.empty())
317*1a96fba6SXin Li     return;
318*1a96fba6SXin Li 
319*1a96fba6SXin Li   DBusConnection* dbus_conn = dbus_g_connection_get_connection(
320*1a96fba6SXin Li       dbus::GetSystemBusConnection().g_connection());
321*1a96fba6SXin Li   DCHECK(dbus_conn);
322*1a96fba6SXin Li 
323*1a96fba6SXin Li   dbus_connection_remove_filter(dbus_conn,
324*1a96fba6SXin Li                                 &SignalWatcher::FilterDBusMessage,
325*1a96fba6SXin Li                                 this);
326*1a96fba6SXin Li 
327*1a96fba6SXin Li   DBusError error;
328*1a96fba6SXin Li   dbus_error_init(&error);
329*1a96fba6SXin Li   dbus_bus_remove_match(dbus_conn, GetDBusMatchString().c_str(), &error);
330*1a96fba6SXin Li   if (dbus_error_is_set(&error)) {
331*1a96fba6SXin Li     LOG(DFATAL) << "Got error while removing D-Bus match rule: " << error.name
332*1a96fba6SXin Li                 << " (" << error.message << ")";
333*1a96fba6SXin Li   }
334*1a96fba6SXin Li }
335*1a96fba6SXin Li 
GetDBusMatchString() const336*1a96fba6SXin Li std::string SignalWatcher::GetDBusMatchString() const {
337*1a96fba6SXin Li   return base::StringPrintf("type='signal', interface='%s', member='%s'",
338*1a96fba6SXin Li                             interface_.c_str(), signal_.c_str());
339*1a96fba6SXin Li }
340*1a96fba6SXin Li 
341*1a96fba6SXin Li /* static */
FilterDBusMessage(DBusConnection * dbus_conn,DBusMessage * message,void * data)342*1a96fba6SXin Li DBusHandlerResult SignalWatcher::FilterDBusMessage(DBusConnection* dbus_conn,
343*1a96fba6SXin Li                                                    DBusMessage* message,
344*1a96fba6SXin Li                                                    void* data) {
345*1a96fba6SXin Li   SignalWatcher* self = static_cast<SignalWatcher*>(data);
346*1a96fba6SXin Li   if (dbus_message_is_signal(
347*1a96fba6SXin Li           message, self->interface_.c_str(), self->signal_.c_str())) {
348*1a96fba6SXin Li     self->OnSignal(message);
349*1a96fba6SXin Li     return DBUS_HANDLER_RESULT_HANDLED;
350*1a96fba6SXin Li   } else {
351*1a96fba6SXin Li     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
352*1a96fba6SXin Li   }
353*1a96fba6SXin Li }
354*1a96fba6SXin Li 
355*1a96fba6SXin Li }  // namespace dbus
356*1a96fba6SXin Li }  // namespace brillo
357