From fb915ed71679feafd4ed53deb2c5ba84862a9e57 Mon Sep 17 00:00:00 2001
From: Sonny Sasaka <sonnysasaka@chromium.org>
Date: Mon, 10 Dec 2018 14:03:49 -0800
Subject: [PATCH] dbus: Support UnexportMethod from an exported object.

Currently there is no way to override a method handler that is already
registered to an ExportedObject. A support to do so is required to
correctly implement Chrome OS Bluetooth dispatcher which needs to
add/remove an interface to an exported object dynamically. Therefore
this CL adds methods to allow method handlers to be unexported so
another handler can be exported afterwards.

Bug: 883039
---
 dbus/exported_object.cc     | 50 +++++++++++++++++++++++++++++++++++++
 dbus/exported_object.h      | 34 +++++++++++++++++++++++++
 dbus/mock_exported_object.h |  7 ++++++
 3 files changed, 91 insertions(+)

diff --git a/dbus/exported_object.cc b/dbus/exported_object.cc
index 5fa1b916f251..727a5707b869 100644
--- a/dbus/exported_object.cc
+++ b/dbus/exported_object.cc
@@ -68,6 +68,22 @@ bool ExportedObject::ExportMethodAndBlock(
   return true;
 }
 
+bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
+                                            const std::string& method_name) {
+  bus_->AssertOnDBusThread();
+
+  const std::string absolute_method_name =
+      GetAbsoluteMemberName(interface_name, method_name);
+  if (method_table_.find(absolute_method_name) == method_table_.end()) {
+    LOG(ERROR) << absolute_method_name << " is not exported";
+    return false;
+  }
+
+  method_table_.erase(absolute_method_name);
+
+  return true;
+}
+
 void ExportedObject::ExportMethod(const std::string& interface_name,
                                   const std::string& method_name,
                                   MethodCallCallback method_call_callback,
@@ -83,6 +99,18 @@ void ExportedObject::ExportMethod(const std::string& interface_name,
   bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
 }
 
+void ExportedObject::UnexportMethod(
+    const std::string& interface_name,
+    const std::string& method_name,
+    OnUnexportedCallback on_unexported_calback) {
+  bus_->AssertOnOriginThread();
+
+  base::Closure task =
+      base::Bind(&ExportedObject::UnexportMethodInternal, this, interface_name,
+                 method_name, on_unexported_calback);
+  bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
+}
+
 void ExportedObject::SendSignal(Signal* signal) {
   // For signals, the object path should be set to the path to the sender
   // object, which is this exported object here.
@@ -141,6 +169,19 @@ void ExportedObject::ExportMethodInternal(
                                                    success));
 }
 
+void ExportedObject::UnexportMethodInternal(
+    const std::string& interface_name,
+    const std::string& method_name,
+    OnUnexportedCallback on_unexported_calback) {
+  bus_->AssertOnDBusThread();
+
+  const bool success = UnexportMethodAndBlock(interface_name, method_name);
+  bus_->GetOriginTaskRunner()->PostTask(
+      FROM_HERE,
+      base::Bind(&ExportedObject::OnUnexported, this, on_unexported_calback,
+                 interface_name, method_name, success));
+}
+
 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
                                 const std::string& interface_name,
                                 const std::string& method_name,
@@ -150,6 +191,15 @@ void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
   on_exported_callback.Run(interface_name, method_name, success);
 }
 
+void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
+                                  const std::string& interface_name,
+                                  const std::string& method_name,
+                                  bool success) {
+  bus_->AssertOnOriginThread();
+
+  on_unexported_callback.Run(interface_name, method_name, success);
+}
+
 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
                                         DBusMessage* signal_message) {
   uint32_t serial = 0;
diff --git a/dbus/exported_object.h b/dbus/exported_object.h
index 69a63a5e075e..d314083430ef 100644
--- a/dbus/exported_object.h
+++ b/dbus/exported_object.h
@@ -60,6 +60,13 @@ class CHROME_DBUS_EXPORT ExportedObject
                                bool success)>
       OnExportedCallback;
 
+  // Called when method unexporting is done.
+  // |success| indicates whether unexporting was successful or not.
+  typedef base::Callback<void(const std::string& interface_name,
+                              const std::string& method_name,
+                              bool success)>
+      OnUnexportedCallback;
+
   // Exports the method specified by |interface_name| and |method_name|,
   // and blocks until exporting is done. Returns true on success.
   //
@@ -81,6 +88,11 @@ class CHROME_DBUS_EXPORT ExportedObject
                                     const std::string& method_name,
                                     MethodCallCallback method_call_callback);
 
+  // Unexports the method specified by |interface_name| and |method_name|,
+  // and blocks until unexporting is done. Returns true on success.
+  virtual bool UnexportMethodAndBlock(const std::string& interface_name,
+                                      const std::string& method_name);
+
   // Requests to export the method specified by |interface_name| and
   // |method_name|. See Also ExportMethodAndBlock().
   //
@@ -93,6 +105,17 @@ class CHROME_DBUS_EXPORT ExportedObject
                             MethodCallCallback method_call_callback,
                             OnExportedCallback on_exported_callback);
 
+  // Requests to unexport the method specified by |interface_name| and
+  // |method_name|. See also UnexportMethodAndBlock().
+  //
+  // |on_unexported_callback| is called when the method is unexported or
+  // failed to be unexported, in the origin thread.
+  //
+  // Must be called in the origin thread.
+  virtual void UnexportMethod(const std::string& interface_name,
+                              const std::string& method_name,
+                              OnUnexportedCallback on_unexported_callback);
+
   // Requests to send the signal from this object. The signal will be sent
   // synchronously if this method is called from the message loop in the D-Bus
   // thread and asynchronously otherwise.
@@ -117,12 +140,23 @@ class CHROME_DBUS_EXPORT ExportedObject
                             MethodCallCallback method_call_callback,
                             OnExportedCallback exported_callback);
 
+  // Helper function for UnexportMethod().
+  void UnexportMethodInternal(const std::string& interface_name,
+                              const std::string& method_name,
+                              OnUnexportedCallback unexported_callback);
+
   // Called when the object is exported.
   void OnExported(OnExportedCallback on_exported_callback,
                   const std::string& interface_name,
                   const std::string& method_name,
                   bool success);
 
+  // Called when a method is unexported.
+  void OnUnexported(OnExportedCallback on_unexported_callback,
+                    const std::string& interface_name,
+                    const std::string& method_name,
+                    bool success);
+
   // Helper function for SendSignal().
   void SendSignalInternal(base::TimeTicks start_time,
                           DBusMessage* signal_message);
diff --git a/dbus/mock_exported_object.h b/dbus/mock_exported_object.h
index 99c363f9b532..9d5b3a894179 100644
--- a/dbus/mock_exported_object.h
+++ b/dbus/mock_exported_object.h
@@ -28,6 +28,13 @@ class MockExportedObject : public ExportedObject {
                     const std::string& method_name,
                     MethodCallCallback method_call_callback,
                     OnExportedCallback on_exported_callback));
+  MOCK_METHOD2(UnexportMethodAndBlock,
+               bool(const std::string& interface_name,
+                    const std::string& method_name));
+  MOCK_METHOD3(UnexportMethod,
+               void(const std::string& interface_name,
+                    const std::string& method_name,
+                    OnUnexportedCallback on_unexported_callback));
   MOCK_METHOD1(SendSignal, void(Signal* signal));
   MOCK_METHOD0(Unregister, void());
 
-- 
2.20.0.rc2.403.gdbc3b29805-goog

