diff --git a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
index bdfaedd989d4678fda2d95aa392d86abdb92f755..2e6e32be131dd9bdfb8e0c0cb3ebb10377dabb84 100644
--- a/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
+++ b/bin/dbus/cx.ring.Ring.PluginManagerInterface.xml
@@ -19,14 +19,6 @@
             </arg>
         </method>
 
-        <method name="togglePlugin" tp:name-for-bindings="togglePlugin">
-            <tp:added version="9.2.0"/>
-            <arg type="s" name="path" direction="in">
-            </arg>
-            <arg type="b" name="toggle" direction="in">
-            </arg>
-        </method>
-
         <method name="getPluginDetails" tp:name-for-bindings="getPluginDetails">
             <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringString"/>
             <tp:added version="9.2.0"/>
@@ -77,14 +69,14 @@
             </arg>
         </method>
 
-       <method name="listAvailablePlugins" tp:name-for-bindings="listAvailablePlugins">
-            <tp:added version="9.2.0"/>
+       <method name="getInstalledPlugins" tp:name-for-bindings="getInstalledPlugins">
+            <tp:added version="9.9.0"/>
             <arg type="as" name="availablePlugins" direction="out">
             </arg>
         </method>
 
-       <method name="listLoadedPlugins" tp:name-for-bindings="listLoadedPlugins">
-            <tp:added version="9.2.0"/>
+       <method name="getLoadedPlugins" tp:name-for-bindings="getLoadedPlugins">
+            <tp:added version="9.9.0"/>
             <arg type="as" name="LoadedPlugins" direction="out">
             </arg>
         </method>
@@ -107,9 +99,15 @@
             </arg>
         </method>
 
-       <method name="listCallMediaHandlers" tp:name-for-bindings="listCallMediaHandlers">
-            <tp:added version="9.2.0"/>
-            <arg type="as" name="listCallMediaHandlers" direction="out">
+       <method name="getCallMediaHandlers" tp:name-for-bindings="getCallMediaHandlers">
+            <tp:added version="9.9.0"/>
+            <arg type="as" name="getCallMediaHandlers" direction="out">
+            </arg>
+        </method>
+
+       <method name="getChatHandlers" tp:name-for-bindings="getChatHandlers">
+            <tp:added version="9.9.0"/>
+            <arg type="as" name="getChatHandlers" direction="out">
             </arg>
         </method>
 
@@ -123,6 +121,18 @@
             </arg>
         </method>
 
+       <method name="toggleChatHandler" tp:name-for-bindings="toggleChatHandler">
+            <tp:added version="9.9.0"/>
+            <arg type="s" name="chatHandlerId" direction="in">
+            </arg>
+            <arg type="s" name="accountId" direction="in">
+            </arg>
+            <arg type="s" name="peerId" direction="in">
+            </arg>
+            <arg type="b" name="toggle" direction="in">
+            </arg>
+        </method>
+
        <method name="getCallMediaHandlerDetails" tp:name-for-bindings="getCallMediaHandlerDetails">
             <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringString"/>
             <tp:added version="9.2.0"/>
@@ -133,6 +143,38 @@
             </arg>
         </method>
 
+       <method name="getCallMediaHandlerStatus" tp:name-for-bindings="getCallMediaHandlerStatus">
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorString"/>
+            <tp:added version="9.3.0"/>
+            <arg type="s" name="callId" direction="in">
+            </arg>
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorString"/>
+            <arg type="as" name="getCallMediaHandlerStatus" direction="out">
+            </arg>
+        </method>
+
+       <method name="getChatHandlerDetails" tp:name-for-bindings="getChatHandlerDetails">
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringString"/>
+            <tp:added version="9.9.0"/>
+            <arg type="s" name="chatHandlerId" direction="in">
+            </arg>
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringString"/>
+            <arg type="a{ss}" name="ChatHandlerDetails" direction="out">
+            </arg>
+        </method>
+
+       <method name="getChatHandlerStatus" tp:name-for-bindings="getChatHandlerStatus">
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorString"/>
+            <tp:added version="9.9.0"/>
+            <arg type="s" name="accountId" direction="in">
+            </arg>
+            <arg type="s" name="peerId" direction="in">
+            </arg>
+            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="VectorString"/>
+            <arg type="as" name="getChatHandlerStatus" direction="out">
+            </arg>
+        </method>
+
         <method name="getPluginsEnabled" tp:name-for-bindings="getPluginsEnabled">
             <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="Bool"/>
             <tp:added version="9.3.0"/>
@@ -148,15 +190,5 @@
             </arg>
         </method>
 
-       <method name="getCallMediaHandlerStatus" tp:name-for-bindings="getCallMediaHandlerStatus">
-            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringVectorString"/>
-            <tp:added version="9.3.0"/>
-            <arg type="s" name="callId" direction="in">
-            </arg>
-            <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="MapStringVectorString"/>
-            <arg type="a{sas}" name="getCallMediaHandlerStatus" direction="out">
-            </arg>
-        </method>
-
     </interface>
 </node>
diff --git a/bin/dbus/dbuspluginmanagerinterface.cpp b/bin/dbus/dbuspluginmanagerinterface.cpp
index af648739bf67fba17daa53aa60bc754c8efadb04..94d12a40d273e7a1029de419b61cc971259ddedc 100644
--- a/bin/dbus/dbuspluginmanagerinterface.cpp
+++ b/bin/dbus/dbuspluginmanagerinterface.cpp
@@ -37,12 +37,6 @@ DBusPluginManagerInterface::unloadPlugin(const std::string& path)
     return DRing::unloadPlugin(path);
 }
 
-void
-DBusPluginManagerInterface::togglePlugin(const std::string& path, const bool& toggle)
-{
-    DRing::togglePlugin(path, toggle);
-}
-
 std::map<std::string, std::string>
 DBusPluginManagerInterface::getPluginDetails(const std::string& path)
 {
@@ -76,15 +70,15 @@ DBusPluginManagerInterface::resetPluginPreferencesValues(const std::string& path
 }
 
 auto
-DBusPluginManagerInterface::listAvailablePlugins() -> decltype(DRing::listAvailablePlugins())
+DBusPluginManagerInterface::getInstalledPlugins() -> decltype(DRing::getInstalledPlugins())
 {
-    return DRing::listAvailablePlugins();
+    return DRing::getInstalledPlugins();
 }
 
 auto
-DBusPluginManagerInterface::listLoadedPlugins() -> decltype(DRing::listLoadedPlugins())
+DBusPluginManagerInterface::getLoadedPlugins() -> decltype(DRing::getLoadedPlugins())
 {
-    return DRing::listLoadedPlugins();
+    return DRing::getLoadedPlugins();
 }
 
 int
@@ -100,9 +94,15 @@ DBusPluginManagerInterface::uninstallPlugin(const std::string& pluginRootPath)
 }
 
 auto
-DBusPluginManagerInterface::listCallMediaHandlers() -> decltype(DRing::listCallMediaHandlers())
+DBusPluginManagerInterface::getCallMediaHandlers() -> decltype(DRing::getCallMediaHandlers())
+{
+    return DRing::getCallMediaHandlers();
+}
+
+auto
+DBusPluginManagerInterface::getChatHandlers() -> decltype(DRing::getChatHandlers())
 {
-    return DRing::listCallMediaHandlers();
+    return DRing::getChatHandlers();
 }
 
 void
@@ -113,12 +113,40 @@ DBusPluginManagerInterface::toggleCallMediaHandler(const std::string& mediaHandl
     DRing::toggleCallMediaHandler(mediaHandlerId, callId, toggle);
 }
 
+void
+DBusPluginManagerInterface::toggleChatHandler(const std::string& chatHandlerId,
+                                              const std::string& accountId,
+                                              const std::string& peerId,
+                                              const bool& toggle)
+{
+    DRing::toggleChatHandler(chatHandlerId, accountId, peerId, toggle);
+}
+
 std::map<std::string, std::string>
 DBusPluginManagerInterface::getCallMediaHandlerDetails(const std::string& mediaHanlderId)
 {
     return DRing::getCallMediaHandlerDetails(mediaHanlderId);
 }
 
+std::vector<std::string>
+DBusPluginManagerInterface::getCallMediaHandlerStatus(const std::string& callId)
+{
+    return DRing::getCallMediaHandlerStatus(callId);
+}
+
+std::map<std::string, std::string>
+DBusPluginManagerInterface::getChatHandlerDetails(const std::string& chatHanlderId)
+{
+    return DRing::getChatHandlerDetails(chatHanlderId);
+}
+
+std::vector<std::string>
+DBusPluginManagerInterface::getChatHandlerStatus(const std::string& accountId,
+                                                 const std::string& peerId)
+{
+    return DRing::getChatHandlerStatus(accountId, peerId);
+}
+
 bool
 DBusPluginManagerInterface::getPluginsEnabled()
 {
@@ -130,9 +158,3 @@ DBusPluginManagerInterface::setPluginsEnabled(const bool& state)
 {
     DRing::setPluginsEnabled(state);
 }
-
-std::map<std::string, std::vector<std::string>>
-DBusPluginManagerInterface::getCallMediaHandlerStatus(const std::string& callId)
-{
-    return DRing::getCallMediaHandlerStatus(callId);
-}
diff --git a/bin/dbus/dbuspluginmanagerinterface.h b/bin/dbus/dbuspluginmanagerinterface.h
index 5474754ad198be51e7662afc1ed4e5a795a23744..681af59542508719f4a48431b12be0d167d6be3d 100644
--- a/bin/dbus/dbuspluginmanagerinterface.h
+++ b/bin/dbus/dbuspluginmanagerinterface.h
@@ -54,7 +54,6 @@ public:
     // Methods
     bool loadPlugin(const std::string& path);
     bool unloadPlugin(const std::string& path);
-    void togglePlugin(const std::string& path, const bool& toggle);
     std::map<std::string, std::string> getPluginDetails(const std::string& path);
     std::vector<std::map<std::string, std::string>> getPluginPreferences(const std::string& path);
     bool setPluginPreference(const std::string& path,
@@ -62,18 +61,25 @@ public:
                              const std::string& value);
     std::map<std::string, std::string> getPluginPreferencesValues(const std::string& path);
     bool resetPluginPreferencesValues(const std::string& path);
-    std::vector<std::string> listAvailablePlugins();
-    std::vector<std::string> listLoadedPlugins();
+    std::vector<std::string> getInstalledPlugins();
+    std::vector<std::string> getLoadedPlugins();
     int installPlugin(const std::string& jplPath, const bool& force);
     int uninstallPlugin(const std::string& pluginRootPath);
-    std::vector<std::string> listCallMediaHandlers();
+    std::vector<std::string> getCallMediaHandlers();
+    std::vector<std::string> getChatHandlers();
     void toggleCallMediaHandler(const std::string& mediaHandlerId,
                                 const std::string& callId,
                                 const bool& toggle);
+    void toggleChatHandler(const std::string& chatHandlerId,
+                           const std::string& accountId,
+                           const std::string& peerId,
+                           const bool& toggle);
     std::map<std::string, std::string> getCallMediaHandlerDetails(const std::string& mediaHandlerId);
+    std::vector<std::string> getCallMediaHandlerStatus(const std::string& callId);
+    std::map<std::string, std::string> getChatHandlerDetails(const std::string& chatHandlerId);
+    std::vector<std::string> getChatHandlerStatus(const std::string& accontId,
+                                                  const std::string& peerId);
 
     bool getPluginsEnabled();
     void setPluginsEnabled(const bool& state);
-    std::map<std::string, std::vector<std::string>> getCallMediaHandlerStatus(
-        const std::string& callId);
 };
diff --git a/bin/jni/plugin_manager_interface.i b/bin/jni/plugin_manager_interface.i
index 4552457ea64b0c5321d3735ee0307596d5415c72..8f7086df6ca852bf2a232c4f23ace1c2d8f65244 100644
--- a/bin/jni/plugin_manager_interface.i
+++ b/bin/jni/plugin_manager_interface.i
@@ -26,20 +26,23 @@
 namespace DRing {
 bool loadPlugin(const std::string& path);
 bool unloadPlugin(const std::string& path);
-void togglePlugin(const std::string& path, bool toggle);
 std::map<std::string,std::string> getPluginDetails(const std::string& path);
 std::vector<std::map<std::string,std::string>> getPluginPreferences(const std::string& path);
 bool setPluginPreference(const std::string& path, const std::string& key, const std::string& value);
 std::map<std::string,std::string> getPluginPreferencesValues(const std::string& path);
 bool resetPluginPreferencesValues(const std::string& path);
-std::vector<std::string> listAvailablePlugins();
-std::vector<std::string> listLoadedPlugins();
+std::vector<std::string> getInstalledPlugins();
+std::vector<std::string> getLoadedPlugins();
 int installPlugin(const std::string& jplPath, bool force);
 int uninstallPlugin(const std::string& pluginRootPath);
-std::vector<std::string> listCallMediaHandlers();
+std::vector<std::string> getCallMediaHandlers();
+std::vector<std::string> getChatHandlers();
 void toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& callId, bool toggle);
+void toggleChatHandler(const std::string& chatHandlerId, const std::string& accountId, const std::string& peerId, bool toggle);
 std::map<std::string,std::string> getCallMediaHandlerDetails(const std::string& mediaHandlerId);
+std::vector<std::string> getCallMediaHandlerStatus(const std::string& callId);
+std::map<std::string,std::string> getChatHandlerDetails(const std::string& chatHandlerId);
+std::vector<std::string> getChatHandlerStatus(const std::string& accountId, const std::string& peerId);
 bool getPluginsEnabled();
 void setPluginsEnabled(bool state);
-std::map<std::string, std::vector<std::string>> getCallMediaHandlerStatus(const std::string& callId);
 }
diff --git a/contrib/src/ffnvcodec/rules.mak b/contrib/src/ffnvcodec/rules.mak
index 6c7ea15e31d2e4990b49c467a78c3c72949ad4f9..26dd855db2aeae9c96d457528e7cf51f10c4a8ca 100644
--- a/contrib/src/ffnvcodec/rules.mak
+++ b/contrib/src/ffnvcodec/rules.mak
@@ -1,5 +1,5 @@
 # ffnvcodec
-FFNVCODEC_VERSION := n10.0.26.0
+FFNVCODEC_VERSION := n11.0.10.0
 FFNVCODEC_GITURL := https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
 
 ifeq ($(call need_pkg,"ffnvcodec >= 8"),)
diff --git a/meson.build b/meson.build
index 86b0643e0939b7242fd342b9ecb79e1292d08451..f8e660811c7f807fb789b3833aafac532249afc8 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('jami-daemon', ['c', 'cpp'],
-        version: '9.6.0',
+        version: '9.9.0',
         license: 'GPL3+',
         default_options: ['cpp_std=gnu++17', 'buildtype=debugoptimized'],
         meson_version:'>= 0.54'
diff --git a/src/call.cpp b/src/call.cpp
index 757992842c48badd42c033b6e81ad2f2a0f06bab..556c20b03f3d00d7dfce9e18c61ce95924e088ed 100644
--- a/src/call.cpp
+++ b/src/call.cpp
@@ -23,6 +23,10 @@
 #include "call.h"
 #include "account.h"
 #include "manager.h"
+#ifdef ENABLE_PLUGIN
+#include "plugin/jamipluginmanager.h"
+#include "plugin/streamdata.h"
+#endif
 #include "audio/ringbufferpool.h"
 #include "dring/call_const.h"
 #include "client/ring_signal.h"
@@ -410,6 +414,14 @@ Call::onTextMessage(std::map<std::string, std::string>&& messages)
             return;
         }
     }
+#ifdef ENABLE_PLUGIN
+    auto& pluginChatManager
+        = jami::Manager::instance().getJamiPluginManager().getChatServicesManager();
+    std::shared_ptr<JamiMessage> cm = std::make_shared<JamiMessage>(
+        getAccountId(), getPeerNumber(), true, const_cast<std::map<std::string, std::string>&>(messages), false);
+    pluginChatManager.publishMessage(cm);
+
+#endif
     Manager::instance().incomingMessage(getCallId(), getPeerNumber(), messages);
 }
 
diff --git a/src/client/plugin_manager_interface.cpp b/src/client/plugin_manager_interface.cpp
index 5b7911fc8e11cc105ea6abf740da8bac15f1c184..0840725d21f1ac1b541e50e2a9e413cf6795ec35 100644
--- a/src/client/plugin_manager_interface.cpp
+++ b/src/client/plugin_manager_interface.cpp
@@ -45,12 +45,6 @@ unloadPlugin(const std::string& path)
     return status;
 }
 
-void
-togglePlugin(const std::string& path, bool toggle)
-{
-    jami::Manager::instance().getJamiPluginManager().togglePlugin(path, toggle);
-}
-
 std::map<std::string, std::string>
 getPluginDetails(const std::string& path)
 {
@@ -81,15 +75,15 @@ resetPluginPreferencesValues(const std::string& path)
 }
 
 std::vector<std::string>
-listAvailablePlugins()
+getInstalledPlugins()
 {
-    return jami::Manager::instance().getJamiPluginManager().listAvailablePlugins();
+    return jami::Manager::instance().getJamiPluginManager().getInstalledPlugins();
 }
 
 std::vector<std::string>
-listLoadedPlugins()
+getLoadedPlugins()
 {
-    return jami::Manager::instance().getJamiPluginManager().listLoadedPlugins();
+    return jami::Manager::instance().getJamiPluginManager().getLoadedPlugins();
 }
 
 int
@@ -105,12 +99,21 @@ uninstallPlugin(const std::string& pluginRootPath)
 }
 
 std::vector<std::string>
-listCallMediaHandlers()
+getCallMediaHandlers()
 {
     return jami::Manager::instance()
         .getJamiPluginManager()
         .getCallServicesManager()
-        .listCallMediaHandlers();
+        .getCallMediaHandlers();
+}
+
+std::vector<std::string>
+getChatHandlers()
+{
+    return jami::Manager::instance()
+        .getJamiPluginManager()
+        .getChatServicesManager()
+        .getChatHandlers();
 }
 
 void
@@ -122,6 +125,18 @@ toggleCallMediaHandler(const std::string& mediaHandlerId, const std::string& cal
         .toggleCallMediaHandler(mediaHandlerId, callId, toggle);
 }
 
+void
+toggleChatHandler(const std::string& chatHandlerId,
+                  const std::string& accountId,
+                  const std::string& peerId,
+                  bool toggle)
+{
+    return jami::Manager::instance()
+        .getJamiPluginManager()
+        .getChatServicesManager()
+        .toggleChatHandler(chatHandlerId, accountId, peerId, toggle);
+}
+
 std::map<std::string, std::string>
 getCallMediaHandlerDetails(const std::string& mediaHandlerId)
 {
@@ -131,6 +146,33 @@ getCallMediaHandlerDetails(const std::string& mediaHandlerId)
         .getCallMediaHandlerDetails(mediaHandlerId);
 }
 
+std::vector<std::string>
+getCallMediaHandlerStatus(const std::string& callId)
+{
+    return jami::Manager::instance()
+        .getJamiPluginManager()
+        .getCallServicesManager()
+        .getCallMediaHandlerStatus(callId);
+}
+
+std::map<std::string, std::string>
+getChatHandlerDetails(const std::string& chatHandlerId)
+{
+    return jami::Manager::instance()
+        .getJamiPluginManager()
+        .getChatServicesManager()
+        .getChatHandlerDetails(chatHandlerId);
+}
+
+std::vector<std::string>
+getChatHandlerStatus(const std::string& accountId, const std::string& peerId)
+{
+    return jami::Manager::instance()
+        .getJamiPluginManager()
+        .getChatServicesManager()
+        .getChatHandlerStatus(accountId, peerId);
+}
+
 bool
 getPluginsEnabled()
 {
@@ -143,13 +185,4 @@ setPluginsEnabled(bool state)
     jami::Manager::instance().pluginPreferences.setPluginsEnabled(state);
     jami::Manager::instance().saveConfig();
 }
-
-std::map<std::string, std::vector<std::string>>
-getCallMediaHandlerStatus(const std::string& callId)
-{
-    return jami::Manager::instance()
-        .getJamiPluginManager()
-        .getCallServicesManager()
-        .getCallMediaHandlerStatus(callId);
-}
 } // namespace DRing
diff --git a/src/dring/plugin_manager_interface.h b/src/dring/plugin_manager_interface.h
index 19bd8ade9a799138a02372772d9ef2d1e03d3cb4..6c91e57d5fee51e741cf3e162109112697498bc4 100644
--- a/src/dring/plugin_manager_interface.h
+++ b/src/dring/plugin_manager_interface.h
@@ -36,7 +36,6 @@
 namespace DRing {
 DRING_PUBLIC bool loadPlugin(const std::string& path);
 DRING_PUBLIC bool unloadPlugin(const std::string& path);
-DRING_PUBLIC void togglePlugin(const std::string& path, bool toggle);
 DRING_PUBLIC std::map<std::string, std::string> getPluginDetails(const std::string& path);
 DRING_PUBLIC std::vector<std::map<std::string, std::string>> getPluginPreferences(
     const std::string& path);
@@ -45,18 +44,26 @@ DRING_PUBLIC bool setPluginPreference(const std::string& path,
                                       const std::string& value);
 DRING_PUBLIC std::map<std::string, std::string> getPluginPreferencesValues(const std::string& path);
 DRING_PUBLIC bool resetPluginPreferencesValues(const std::string& path);
-DRING_PUBLIC std::vector<std::string> listAvailablePlugins();
-DRING_PUBLIC std::vector<std::string> listLoadedPlugins();
+DRING_PUBLIC std::vector<std::string> getInstalledPlugins();
+DRING_PUBLIC std::vector<std::string> getLoadedPlugins();
 DRING_PUBLIC int installPlugin(const std::string& jplPath, bool force);
 DRING_PUBLIC int uninstallPlugin(const std::string& pluginRootPath);
-DRING_PUBLIC std::vector<std::string> listCallMediaHandlers();
+DRING_PUBLIC std::vector<std::string> getCallMediaHandlers();
+DRING_PUBLIC std::vector<std::string> getChatHandlers();
 DRING_PUBLIC void toggleCallMediaHandler(const std::string& mediaHandlerId,
                                          const std::string& callId,
                                          bool toggle);
+DRING_PUBLIC void toggleChatHandler(const std::string& chatHandlerId,
+                                    const std::string& accountId,
+                                    const std::string& peerId,
+                                    bool toggle);
 DRING_PUBLIC std::map<std::string, std::string> getCallMediaHandlerDetails(
     const std::string& mediaHandlerId);
+DRING_PUBLIC std::vector<std::string> getCallMediaHandlerStatus(const std::string& callId);
+DRING_PUBLIC std::map<std::string, std::string> getChatHandlerDetails(
+    const std::string& chatHandlerId);
+DRING_PUBLIC std::vector<std::string> getChatHandlerStatus(const std::string& accountId,
+                                                           const std::string& peerId);
 DRING_PUBLIC bool getPluginsEnabled();
 DRING_PUBLIC void setPluginsEnabled(bool state);
-DRING_PUBLIC std::map<std::string, std::vector<std::string>> getCallMediaHandlerStatus(
-    const std::string& callId);
 } // namespace DRing
diff --git a/src/jamidht/contact_list.cpp b/src/jamidht/contact_list.cpp
index ad8ff44460f417ca1452cfe704c5bdc1081729ff..bb4b4ec8fcd98441a8a322cbfd79367e3615b7ab 100644
--- a/src/jamidht/contact_list.cpp
+++ b/src/jamidht/contact_list.cpp
@@ -20,6 +20,11 @@
 #include "jamiaccount.h"
 #include "fileutils.h"
 
+#ifdef ENABLE_PLUGIN
+#include "manager.h"
+#include "plugin/jamipluginmanager.h"
+#endif
+
 #include "account_const.h"
 
 #include <fstream>
@@ -102,6 +107,16 @@ ContactList::removeContact(const dht::InfoHash& h, bool ban)
     if (ban and trustRequests_.erase(h) > 0)
         saveTrustRequests();
     saveContacts();
+#ifdef ENABLE_PLUGIN
+    std::size_t found = path_.find_last_of(DIR_SEPARATOR_CH);
+    if (found != std::string::npos) {
+        auto filename = path_.substr(found + 1);
+        jami::Manager::instance()
+            .getJamiPluginManager()
+            .getChatServicesManager()
+            .cleanChatSubjects(filename, uri);
+    }
+#endif
     callbacks_.contactRemoved(uri, ban);
     return true;
 }
@@ -414,19 +429,20 @@ ContactList::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>&
                  name.c_str(),
                  crt->getId().toString().c_str());
         tls::CertificateStore::instance().pinCertificate(crt);
-        if (crt->ocspResponse){
+        if (crt->ocspResponse) {
             unsigned int status = crt->ocspResponse->getCertificateStatus();
-            if (status == GNUTLS_OCSP_CERT_GOOD){
+            if (status == GNUTLS_OCSP_CERT_GOOD) {
                 JAMI_DBG("Certificate %s has good OCSP status", crt->getId().to_c_str());
-                trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::ALLOWED);
-            }
-            else if (status == GNUTLS_OCSP_CERT_REVOKED){
+                trust_.setCertificateStatus(crt->getId().toString(),
+                                            tls::TrustStore::PermissionStatus::ALLOWED);
+            } else if (status == GNUTLS_OCSP_CERT_REVOKED) {
                 JAMI_ERR("Certificate %s has revoked OCSP status", crt->getId().to_c_str());
-                trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::BANNED);
-            }
-            else {
+                trust_.setCertificateStatus(crt->getId().toString(),
+                                            tls::TrustStore::PermissionStatus::BANNED);
+            } else {
                 JAMI_ERR("Certificate %s has unknown OCSP status", crt->getId().to_c_str());
-                trust_.setCertificateStatus(crt->getId().toString(), tls::TrustStore::PermissionStatus::UNDEFINED);
+                trust_.setCertificateStatus(crt->getId().toString(),
+                                            tls::TrustStore::PermissionStatus::UNDEFINED);
             }
         }
         saveKnownDevices();
diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp
index 1a03c9772cd81ef0528d0420ee93b7864f117264..a1357123d370a0b08ee30db43e4076e6a53c055f 100644
--- a/src/jamidht/jamiaccount.cpp
+++ b/src/jamidht/jamiaccount.cpp
@@ -61,6 +61,11 @@
 #include "manager.h"
 #include "utf8_utils.h"
 
+#ifdef ENABLE_PLUGIN
+#include "plugin/jamipluginmanager.h"
+#include "plugin/chatservicesmanager.h"
+#endif
+
 #ifdef ENABLE_VIDEO
 #include "libav_utils.h"
 #endif
@@ -2570,6 +2575,10 @@ JamiAccount::doUnregister(std::function<void(bool)> released_cb)
 
     if (released_cb)
         released_cb(false);
+#ifdef ENABLE_PLUGIN
+    jami::Manager::instance().getJamiPluginManager().getChatServicesManager().cleanChatSubjects(
+        getAccountID());
+#endif
 }
 
 void
diff --git a/src/manager.cpp b/src/manager.cpp
index c309bd6bfe8f6942eceddc7706e6dfdc5e59350b..3dbf5a87d5a9275010a4b4508822ce0911fc9fc2 100644
--- a/src/manager.cpp
+++ b/src/manager.cpp
@@ -68,6 +68,7 @@ using random_device = dht::crypto::random_device;
 
 #ifdef ENABLE_PLUGIN
 #include "plugin/jamipluginmanager.h"
+#include "plugin/streamdata.h"
 #endif
 
 #ifdef ENABLE_VIDEO
@@ -1708,8 +1709,8 @@ Manager::scheduleTask(std::function<void()>&& task, std::chrono::steady_clock::t
     return pimpl_->scheduler_.schedule(std::move(task), when);
 }
 
-std::shared_ptr<Task> Manager::scheduleTaskIn(std::function<void()>&& task,
-                                std::chrono::steady_clock::duration timeout)
+std::shared_ptr<Task>
+Manager::scheduleTaskIn(std::function<void()>&& task, std::chrono::steady_clock::duration timeout)
 {
     return pimpl_->scheduler_.scheduleIn(std::move(task), timeout);
 }
@@ -3071,18 +3072,24 @@ Manager::isPasswordValid(const std::string& accountID, const std::string& passwo
 uint64_t
 Manager::sendTextMessage(const std::string& accountID,
                          const std::string& to,
-                         const std::map<std::string, std::string>& payloads)
+                         const std::map<std::string, std::string>& payloads,
+                         const bool fromPlugin)
 {
     if (const auto acc = getAccount(accountID)) {
         try {
-#ifdef ENABLE_PLUGIN
-            auto& convManager
-                = jami::Manager::instance().getJamiPluginManager().getConversationServicesManager();
-            std::shared_ptr<jami::ConversationMessage> cm
-                = std::make_shared<jami::ConversationMessage>(
-                    accountID, to, const_cast<std::map<std::string, std::string>&>(payloads));
-            convManager.sendTextMessage(cm);
-            return acc->sendTextMessage(cm->to_, cm->data_);
+#ifdef ENABLE_PLUGIN // modifies send message
+            auto& pluginChatManager
+                = jami::Manager::instance().getJamiPluginManager().getChatServicesManager();
+            std::shared_ptr<JamiMessage> cm
+                = std::make_shared<JamiMessage>(accountID,
+                                                to,
+                                                false,
+                                                const_cast<std::map<std::string, std::string>&>(
+                                                    payloads),
+                                                fromPlugin);
+            if (!fromPlugin)
+                pluginChatManager.publishMessage(cm);
+            return acc->sendTextMessage(cm->peerId, cm->data);
 #else
             return acc->sendTextMessage(to, payloads);
 #endif // ENABLE_PLUGIN
@@ -3257,7 +3264,9 @@ Manager::setModerator(const std::string& confId, const std::string& peerId, cons
     if (auto conf = getConferenceFromID(confId)) {
         conf->setModerator(peerId, state);
     } else
-        JAMI_WARN("Fail to change moderator %s, conference %s not found", peerId.c_str(), confId.c_str());
+        JAMI_WARN("Fail to change moderator %s, conference %s not found",
+                  peerId.c_str(),
+                  confId.c_str());
 }
 
 void
diff --git a/src/manager.h b/src/manager.h
index b4a62f3b487c021a60e77f5fa085898827e7b83b..60012200fa2895dabcf0f9362cf0cdde38e292cd 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -415,7 +415,8 @@ public:
 
     uint64_t sendTextMessage(const std::string& accountID,
                              const std::string& to,
-                             const std::map<std::string, std::string>& payloads);
+                             const std::map<std::string, std::string>& payloads,
+                             const bool fromPlugin = false);
 
     int getMessageStatus(uint64_t id) const;
     int getMessageStatus(const std::string& accountID, uint64_t id) const;
@@ -894,7 +895,7 @@ public:
     std::shared_ptr<Task> scheduleTask(std::function<void()>&& task,
                                        std::chrono::steady_clock::time_point when);
     std::shared_ptr<Task> scheduleTaskIn(std::function<void()>&& task,
-                                       std::chrono::steady_clock::duration timeout);
+                                         std::chrono::steady_clock::duration timeout);
 
     std::map<std::string, std::string> getNearbyPeers(const std::string& accountID);
 
diff --git a/src/meson.build b/src/meson.build
index 82a31297f600718972b8107e19f4ddb8ee1d8d97..5a873190509443822efe0aba73e255e9aaebee2d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -273,7 +273,8 @@ if conf.get('ENABLE_PLUGIN')
         'client/plugin_manager_interface.cpp',
         'plugin/jamipluginmanager.cpp',
         'plugin/pluginloaderdl.cpp',
-        'plugin/pluginmanager.cpp'
+        'plugin/pluginmanager.cpp',
+        'plugin/pluginpreferencesutils.cpp'
     )
     libjami_dependencies += [deplibarchive, depdl]
 endif
diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt
index 25224bd81d552f14c63faf80632fd7dd61e09775..65a18b391803860d92a9dc9911328dcc49a31d95 100644
--- a/src/plugin/CMakeLists.txt
+++ b/src/plugin/CMakeLists.txt
@@ -5,14 +5,18 @@ list (APPEND Source_Files__plugin
       "${CMAKE_CURRENT_SOURCE_DIR}/jamipluginmanager.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/pluginloaderdl.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/pluginmanager.cpp"
+      "${CMAKE_CURRENT_SOURCE_DIR}/pluginpreferencesutils.cpp"
       "${CMAKE_CURRENT_SOURCE_DIR}/callservicesmanager.h"
-      "${CMAKE_CURRENT_SOURCE_DIR}/conversationservicesmanager.h"
-      "${CMAKE_CURRENT_SOURCE_DIR}/conversationhandler.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/callservicesmanager.cpp"
+      "${CMAKE_CURRENT_SOURCE_DIR}/chatservicesmanager.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/chatservicesmanager.cpp"
+      "${CMAKE_CURRENT_SOURCE_DIR}/chathandler.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/jamiplugin.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/jamipluginmanager.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/mediahandler.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/pluginloader.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/pluginmanager.h"
+      "${CMAKE_CURRENT_SOURCE_DIR}/pluginpreferencesutils.h"
       "${CMAKE_CURRENT_SOURCE_DIR}/streamdata.h"
 )
 
diff --git a/src/plugin/Makefile.am b/src/plugin/Makefile.am
index b528b290233286609d1c7661ab52506851d0a882..b02cadf6fdb9d0e9bf6dc12d7726437c9434f776 100644
--- a/src/plugin/Makefile.am
+++ b/src/plugin/Makefile.am
@@ -4,19 +4,23 @@ noinst_LTLIBRARIES = libplugin.la
 
 noinst_HEADERS = \
 	callservicesmanager.h \
-	conversationhandler.h \
-	conversationservicesmanager.h \
+	chathandler.h \
+	chatservicesmanager.h \
 	jamiplugin.h \
 	jamipluginmanager.h \
 	mediahandler.h \
 	pluginloader.h \
 	pluginmanager.h \
+	pluginpreferencesutils.h \
 	streamdata.h
 
 libplugin_la_SOURCES = \
 	jamipluginmanager.cpp \
 	pluginloaderdl.cpp \
-	pluginmanager.cpp 
+	pluginmanager.cpp \
+	pluginpreferencesutils.cpp \
+	chatservicesmanager.cpp \
+	callservicesmanager.cpp
 
 
 libplugin_la_LIBADD = @ARCHIVE_LIBS@
diff --git a/src/plugin/callservicesmanager.cpp b/src/plugin/callservicesmanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ab148663be84fc62e7cf215060e1295bcd753ac
--- /dev/null
+++ b/src/plugin/callservicesmanager.cpp
@@ -0,0 +1,229 @@
+/**
+ *  Copyright (C)2020-2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include "callservicesmanager.h"
+#include "logger.h"
+#include "sip/sipcall.h"
+#include "fileutils.h"
+#include "pluginpreferencesutils.h"
+#include "manager.h"
+
+namespace jami {
+
+CallServicesManager::CallServicesManager(PluginManager& pm)
+{
+    registerComponentsLifeCycleManagers(pm);
+}
+
+CallServicesManager::~CallServicesManager()
+{
+    callMediaHandlers_.clear();
+}
+
+void
+CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
+{
+    // This guarantees unicity of subjects by id
+    callAVsubjects_.emplace_back(data, subject);
+
+    for (auto& callMediaHandler : callMediaHandlers_) {
+        std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH);
+        auto preferences = PluginPreferencesUtils::getPreferencesValuesMap(callMediaHandler->id().substr(0, found));
+#ifndef __ANDROID__
+        if (preferences.at("always") == "1")
+            toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true);
+        else
+#endif
+            for (const auto& toggledMediaHandler : mediaHandlerToggled_[data.id]) {
+                if (toggledMediaHandler == (uintptr_t) callMediaHandler.get()) {
+                    toggleCallMediaHandler(toggledMediaHandler, data.id, true);
+                    break;
+                }
+            }
+    }
+}
+
+void
+CallServicesManager::clearAVSubject(const std::string& callId)
+{
+    for (auto it = callAVsubjects_.begin(); it != callAVsubjects_.end();) {
+        if (it->first.id == callId) {
+            it = callAVsubjects_.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+void
+CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm)
+{
+    auto registerCallMediaHandler = [this](void* data) {
+        CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
+
+        if (!ptr)
+            return -1;
+        callMediaHandlers_.emplace_back(std::move(ptr));
+        return 0;
+    };
+
+    auto unregisterMediaHandler = [this](void* data) {
+        for (auto it = callMediaHandlers_.begin(); it != callMediaHandlers_.end(); ++it) {
+            if (it->get() == data) {
+                callMediaHandlers_.erase(it);
+                break;
+            }
+        }
+        return 0;
+    };
+
+    pm.registerComponentManager("CallMediaHandlerManager",
+                                registerCallMediaHandler,
+                                unregisterMediaHandler);
+}
+
+std::vector<std::string>
+CallServicesManager::getCallMediaHandlers()
+{
+    std::vector<std::string> res;
+    res.reserve(callMediaHandlers_.size());
+    for (const auto& mediaHandler : callMediaHandlers_) {
+        res.emplace_back(std::to_string((uintptr_t) mediaHandler.get()));
+    }
+    return res;
+}
+
+void
+CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId,
+                            const std::string& callId,
+                            const bool toggle)
+{
+    toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle);
+}
+
+std::map<std::string, std::string>
+CallServicesManager::getCallMediaHandlerDetails(const std::string& mediaHandlerIdStr)
+{
+    auto mediaHandlerId = std::stoull(mediaHandlerIdStr);
+    for (auto& mediaHandler : callMediaHandlers_) {
+        if ((uintptr_t) mediaHandler.get() == mediaHandlerId) {
+            return mediaHandler->getCallMediaHandlerDetails();
+        }
+    }
+    return {};
+}
+
+bool
+CallServicesManager::isVideoType(const CallMediaHandlerPtr& mediaHandler)
+{
+    const auto& details = mediaHandler->getCallMediaHandlerDetails();
+    const auto& it = details.find("dataType");
+    if (it != details.end()) {
+        bool status;
+        std::istringstream(it->second) >> status;
+        return status;
+    }
+    return true;
+}
+
+bool
+CallServicesManager::isAttached(const CallMediaHandlerPtr& mediaHandler)
+{
+    const auto& details = mediaHandler->getCallMediaHandlerDetails();
+    const auto& it = details.find("attached");
+    if (it != details.end()) {
+        bool status;
+        std::istringstream(it->second) >> status;
+        return status;
+    }
+    return true;
+}
+
+std::vector<std::string>
+CallServicesManager::getCallMediaHandlerStatus(const std::string& callId)
+{
+    std::vector<std::string> ret;
+    const auto& it = mediaHandlerToggled_.find(callId);
+    if (it != mediaHandlerToggled_.end()) {
+        ret.reserve(it->second.size());
+        for (const auto& mediaHandlerId : it->second)
+            ret.emplace_back(std::to_string(mediaHandlerId));
+    }
+    return ret;
+}
+
+void
+CallServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr)
+{
+    for (auto& mediaHandler : callMediaHandlers_) {
+        if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"])
+            != std::string::npos) {
+            mediaHandler->setPreferenceAttribute(key, value);
+        }
+    }
+}
+
+void
+CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr,
+                        const StreamData& data,
+                        AVSubjectSPtr& subject)
+{
+    if (auto soSubject = subject.lock())
+        callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject);
+}
+
+void
+CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId,
+                            const std::string& callId,
+                            const bool toggle)
+{
+    auto& handlers = mediaHandlerToggled_[callId];
+
+    bool applyRestart = false;
+
+    for (auto subject : callAVsubjects_) {
+        if (subject.first.id == callId) {
+
+            auto handlerIt = std::find_if(callMediaHandlers_.begin(), callMediaHandlers_.end(),
+                    [mediaHandlerId](CallMediaHandlerPtr& handler) {
+                        return ((uintptr_t) handler.get() == mediaHandlerId);
+                        });
+
+            if (handlerIt != callMediaHandlers_.end()) {
+                if (toggle) {
+                    notifyAVSubject((*handlerIt), subject.first, subject.second);
+                    if (isAttached((*handlerIt))
+                        && handlers.find(mediaHandlerId) == handlers.end())
+                        handlers.insert(mediaHandlerId);
+                } else {
+                    (*handlerIt)->detach();
+                    handlers.erase(mediaHandlerId);
+                }
+                if (subject.first.type == StreamType::video && isVideoType((*handlerIt)))
+                    applyRestart = true;
+            }
+        }
+    }
+#ifndef __ANDROID__
+    if (applyRestart)
+        Manager::instance().callFactory.getCall<SIPCall>(callId)->getVideoRtp().restartSender();
+#endif
+}
+} // namespace jami
diff --git a/src/plugin/callservicesmanager.h b/src/plugin/callservicesmanager.h
index 38c3a7de1c260096601d05af48b7f2b4ad72df70..8ab1a5a62bf2b683d598682d4b2029ebce0428e2 100644
--- a/src/plugin/callservicesmanager.h
+++ b/src/plugin/callservicesmanager.h
@@ -19,16 +19,11 @@
  */
 
 #pragma once
-// Utils
+
 #include "noncopyable.h"
-#include "logger.h"
-#include "manager.h"
-#include "sip/sipcall.h"
-// Plugin Manager
 #include "pluginmanager.h"
 #include "streamdata.h"
 #include "mediahandler.h"
-// STL
 #include <list>
 #include <set>
 #include <tuple>
@@ -41,205 +36,71 @@ using AVSubjectSPtr = std::weak_ptr<Observable<AVFrame*>>;
 class CallServicesManager
 {
 public:
-    CallServicesManager(PluginManager& pm) { registerComponentsLifeCycleManagers(pm); }
+    CallServicesManager(PluginManager& pm);
 
     /**
      *   unload all media handlers
      **/
-    ~CallServicesManager() { callMediaHandlers.clear(); }
+    ~CallServicesManager();
 
     NON_COPYABLE(CallServicesManager);
 
-public:
     /**
      * @brief createAVSubject
      * @param data
      * Creates an av frame subject with properties StreamData
      */
-    void createAVSubject(const StreamData& data, AVSubjectSPtr subject)
-    {
-        // This guarantees unicity of subjects by id
-        callAVsubjects.push_back(std::make_pair(data, subject));
-        for (const auto& toggledMediaHandler : mediaHandlerToggled_[data.id]) {
-            toggleCallMediaHandler(toggledMediaHandler, data.id, true);
-        }
-    }
+    void createAVSubject(const StreamData& data, AVSubjectSPtr subject);
 
-    void clearAVSubject(const std::string& callId)
-    {
-        for (auto it = callAVsubjects.begin(); it != callAVsubjects.end();) {
-            if (it->first.id == callId) {
-                it = callAVsubjects.erase(it);
-            } else {
-                ++it;
-            }
-        }
-    }
+    void clearAVSubject(const std::string& callId);
 
     /**
      * @brief registerComponentsLifeCycleManagers
      * Exposes components life cycle managers to the main API
      */
-    void registerComponentsLifeCycleManagers(PluginManager& pm)
-    {
-        auto registerCallMediaHandler = [this](void* data) {
-            CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
-
-            if (ptr) {
-                callMediaHandlers.emplace_back(std::move(ptr));
-            }
-            return 0;
-        };
-
-        auto unregisterMediaHandler = [this](void* data) {
-            for (auto it = callMediaHandlers.begin(); it != callMediaHandlers.end(); ++it) {
-                if (it->get() == data) {
-                    callMediaHandlers.erase(it);
-                    break;
-                }
-            }
-            return 0;
-        };
-
-        pm.registerComponentManager("CallMediaHandlerManager",
-                                    registerCallMediaHandler,
-                                    unregisterMediaHandler);
-    }
+    void registerComponentsLifeCycleManagers(PluginManager& pm);
 
     /**
-     * @brief listCallMediaHandlers
+     * @brief getCallMediaHandlers
      * List all call media handlers
      * @return
      */
-    std::vector<std::string> listCallMediaHandlers()
-    {
-        std::vector<std::string> res;
-        for (const auto& mediaHandler : callMediaHandlers) {
-            res.emplace_back(getCallHandlerId(mediaHandler));
-        }
-        return res;
-    }
+    std::vector<std::string> getCallMediaHandlers();
 
     /**
      * @brief toggleCallMediaHandler
      * Toggle CallMediaHandler, if on, notify with new subjects
      * if off, detach it
-     * @param id
+     * @param mediaHandler ID handler ID
+     * @param callId call ID
+     * @param toggle notify with new subjects if true, detach if false.
+     * 
+     * In the case when the mediaHandler receives a hardware format
+     * frame and converts it to main memory, we need to restart the
+     * sender to unlink ours encoder and decoder.
+     *
+     * When we deactivate a mediaHandler, we try to relink the encoder
+     * and decoder by restarting the sender.
      */
     void toggleCallMediaHandler(const std::string& mediaHandlerId,
                                 const std::string& callId,
-                                const bool toggle)
-    {
-        if (mediaHandlerId.empty() || callId.empty())
-            return;
-
-        auto find = mediaHandlerToggled_.find(callId);
-        if (find == mediaHandlerToggled_.end())
-            mediaHandlerToggled_[callId] = {};
-        bool applyRestart = false;
-        for (auto it = callAVsubjects.begin(); it != callAVsubjects.end(); ++it) {
-            if (it->first.id == callId) {
-                for (auto& mediaHandler : callMediaHandlers) {
-                    if (getCallHandlerId(mediaHandler) == mediaHandlerId) {
-                        if (toggle) {
-                            notifyAVSubject(mediaHandler, it->first, it->second);
-                            if (isAttached(mediaHandler)
-                                && mediaHandlerToggled_[callId].find(mediaHandlerId)
-                                       == mediaHandlerToggled_[callId].end())
-                                mediaHandlerToggled_[callId].insert(mediaHandlerId);
-                        } else {
-                            mediaHandler->detach();
-                            if (mediaHandlerToggled_[callId].find(mediaHandlerId)
-                                != mediaHandlerToggled_[callId].end())
-                                mediaHandlerToggled_[callId].erase(mediaHandlerId);
-                        }
-                        if (it->first.type == StreamType::video && isVideoType(mediaHandler))
-                            applyRestart = true;
-                        break;
-                    }
-                }
-            }
-        }
-
-        /* In the case when the mediaHandler receives a hardware format
-         * frame and converts it to main memory, we need to restart the
-         * sender to unlink ours encoder and decoder.
-         *
-         * When we deactivate a mediaHandler, we try to relink the encoder
-         * and decoder by restarting the sender.
-         */
-        if (applyRestart)
-            Manager::instance().callFactory.getCall<SIPCall>(callId)->getVideoRtp().restartSender();
-    }
+                                const bool toggle);
 
     /**
      * @brief getCallMediaHandlerDetails
      * @param id of the call media handler
      * @return map of Call Media Handler Details
      */
-    std::map<std::string, std::string> getCallMediaHandlerDetails(const std::string& mediaHandlerId)
-    {
-        for (auto& mediaHandler : callMediaHandlers) {
-            if (getCallHandlerId(mediaHandler) == mediaHandlerId) {
-                return mediaHandler->getCallMediaHandlerDetails();
-            }
-        }
-        return {};
-    }
+    std::map<std::string, std::string> getCallMediaHandlerDetails(
+        const std::string& mediaHandlerIdStr);
 
-    bool isVideoType(const CallMediaHandlerPtr& mediaHandler)
-    {
-        const auto& details = mediaHandler->getCallMediaHandlerDetails();
-        const auto& it = details.find("dataType");
-        if (it != details.end()) {
-            JAMI_INFO() << "type: ";
-            bool status;
-            std::istringstream(it->second) >> status;
-            JAMI_INFO() << status;
-            return status;
-        }
-        JAMI_INFO() << "dataType not found";
-        return true;
-    }
+    bool isVideoType(const CallMediaHandlerPtr& mediaHandler);
 
-    bool isAttached(const CallMediaHandlerPtr& mediaHandler)
-    {
-        const auto& details = mediaHandler->getCallMediaHandlerDetails();
-        const auto& it = details.find("attached");
-        if (it != details.end()) {
-            JAMI_INFO() << "status: ";
-            bool status;
-            std::istringstream(it->second) >> status;
-            JAMI_INFO() << status;
-            return status;
-        }
-        JAMI_INFO() << "attached not found";
-        return true;
-    }
+    bool isAttached(const CallMediaHandlerPtr& mediaHandler);
 
-    std::map<std::string, std::vector<std::string>> getCallMediaHandlerStatus(
-        const std::string& callId)
-    {
-        const auto& it = mediaHandlerToggled_.find(callId);
-        if (it != mediaHandlerToggled_.end()) {
-            std::vector<std::string> ret;
-            for (const auto& mediaHandlerId : it->second)
-                ret.push_back(mediaHandlerId);
-            return {{callId, ret}};
-        }
+    std::vector<std::string> getCallMediaHandlerStatus(const std::string& callId);
 
-        return {{callId, {}}};
-    }
-
-    void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr)
-    {
-        for (auto& mediaHandler : callMediaHandlers) {
-            if (scopeStr.find(mediaHandler->getCallMediaHandlerDetails()["name"])
-                != std::string::npos) {
-                mediaHandler->setPreferenceAttribute(key, value);
-            }
-        }
-    }
+    void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr);
 
 private:
     /**
@@ -250,61 +111,34 @@ private:
      */
     void notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr,
                          const StreamData& data,
-                         AVSubjectSPtr& subject)
-    {
-        if (auto soSubject = subject.lock())
-            callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject);
-    }
-
-    /**
-     * @brief listAvailableSubjects
-     * @param callMediaHandlerPtr
-     * This functions lets the call media handler component know which subjects are available
-     */
-    void listAvailableSubjects(const std::string& callID, CallMediaHandlerPtr& callMediaHandlerPtr)
-    {
-        for (auto it = callAVsubjects.begin(); it != callAVsubjects.end(); ++it) {
-            if (it->first.id == callID)
-                notifyAVSubject(callMediaHandlerPtr, it->first, it->second);
-        }
-    }
+                         AVSubjectSPtr& subject);
 
-    /**
-     * @brief getCallHandlerId
-     * Returns the callMediaHandler id from a callMediaHandler pointer
-     * @param callMediaHandler
-     * @return string id
-     */
-    std::string getCallHandlerId(const CallMediaHandlerPtr& callMediaHandler)
-    {
-        if (callMediaHandler) {
-            std::ostringstream callHandlerIdStream;
-            callHandlerIdStream << callMediaHandler.get();
-            return callHandlerIdStream.str();
-        }
-        return "";
-    }
+    void toggleCallMediaHandler(const uintptr_t mediaHandlerId,
+                                const std::string& callId,
+                                const bool toggle);
 
-private:
     /**
-     * @brief callMediaHandlers
+     * @brief callMediaHandlers_
      * Components that a plugin can register through registerCallMediaHandler service
      * These objects can then be notified with notifySubject
      * whenever there is a new CallAVSubject like a video receive
      */
-    std::list<CallMediaHandlerPtr> callMediaHandlers;
+    std::list<CallMediaHandlerPtr> callMediaHandlers_;
 
     /**
-     * @brief callAVsubjects
-     * When there is a SIPCall, CallAVSubjects are created there
+     * @brief callAVsubjects_
+     * When there is a SIPCall, CallAVSubjects_ are created there
      * Here we keep a reference to them in order to make them interact with
-     * CallMediaHandlers
+     * CallMediaHandlers_
      * It is pushed to this list list
      */
-    std::list<std::pair<const StreamData, AVSubjectSPtr>> callAVsubjects;
-    // std::map<std::string, std::tuple<const StreamData, AVSubjectSPtr>> callAVsubjects;
+    std::list<std::pair<const StreamData, AVSubjectSPtr>> callAVsubjects_;
 
-    std::map<std::string, std::set<std::string>> mediaHandlerToggled_;
+    /**
+     * @brief mediaHandlerToggled_
+     * A map of callId and list of mediaHandlers pointers str
+     */
+    std::map<std::string, std::set<uintptr_t>> mediaHandlerToggled_; // callId, list of mediaHandlers
 };
 
 } // namespace jami
diff --git a/src/plugin/chathandler.h b/src/plugin/chathandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..411f10019d4acda02728657435a7f520706babe1
--- /dev/null
+++ b/src/plugin/chathandler.h
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include "observer.h"
+#include "streamdata.h"
+#include <string>
+#include <map>
+
+namespace jami {
+
+using pluginMessagePtr = std::shared_ptr<JamiMessage>;
+using chatSubjectPtr = std::shared_ptr<PublishObservable<pluginMessagePtr>>;
+
+class ChatHandler
+{
+public:
+    virtual void notifyChatSubject(std::pair<std::string, std::string>& subjectConnection,
+                                   chatSubjectPtr subject)
+        = 0;
+    virtual std::map<std::string, std::string> getChatHandlerDetails() = 0;
+    virtual void detach(chatSubjectPtr subject) = 0;
+    virtual void setPreferenceAttribute(const std::string& key, const std::string& value) = 0;
+    virtual bool preferenceMapHasKey(const std::string& key) = 0;
+
+    /**
+     * @brief id
+     * The id is the path of the plugin that created this MediaHandler
+     * @return
+     */
+    std::string id() const { return id_; }
+    virtual void setId(const std::string& id) final { id_ = id; }
+
+private:
+    std::string id_;
+};
+} // namespace jami
diff --git a/src/plugin/chatservicesmanager.cpp b/src/plugin/chatservicesmanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..28f6ae1703e6cc9a5d1e2c7ad9f522c2adb9bc91
--- /dev/null
+++ b/src/plugin/chatservicesmanager.cpp
@@ -0,0 +1,208 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include "chatservicesmanager.h"
+#include "pluginpreferencesutils.h"
+#include "logger.h"
+#include "manager.h"
+#include "fileutils.h"
+
+namespace jami {
+
+ChatServicesManager::ChatServicesManager(PluginManager& pm)
+{
+    registerComponentsLifeCycleManagers(pm);
+    registerChatService(pm);
+}
+
+void
+ChatServicesManager::registerComponentsLifeCycleManagers(PluginManager& pm)
+{
+    auto registerChatHandler = [this](void* data) {
+        ChatHandlerPtr ptr {(static_cast<ChatHandler*>(data))};
+
+        if (!ptr)
+            return -1;
+        chatHandlers_.emplace_back(std::move(ptr));
+        return 0;
+    };
+
+    auto unregisterChatHandler = [this](void* data) {
+        auto handlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(),
+                            [data](ChatHandlerPtr& handler) {
+                                return (handler.get() == data);
+                                });
+
+        if (handlerIt != chatHandlers_.end()) {
+            for (auto& toggledList: chatHandlerToggled_) {
+                auto handlerId = std::find_if(toggledList.second.begin(), toggledList.second.end(),
+                            [this, handlerIt](uintptr_t handlerId) {
+                                return (handlerId == (uintptr_t) handlerIt->get());
+                                });
+                if (handlerId != toggledList.second.end()) {
+                    handlerId = toggledList.second.erase(handlerId);
+                    (*handlerIt)->detach(chatSubjects_[toggledList.first]);
+                }
+            }
+            chatHandlers_.erase(handlerIt);
+        }
+        return 0;
+    };
+
+    pm.registerComponentManager("ChatHandlerManager",
+                                registerChatHandler,
+                                unregisterChatHandler);
+}
+
+void
+ChatServicesManager::registerChatService(PluginManager& pm)
+{
+    auto sendTextMessage = [this](const DLPlugin*, void* data) {
+        auto cm = static_cast<JamiMessage*>(data);
+        jami::Manager::instance().sendTextMessage(cm->accountId, cm->peerId, cm->data, true);
+        return 0;
+    };
+
+    pm.registerService("sendTextMessage", sendTextMessage);
+}
+
+std::vector<std::string>
+ChatServicesManager::getChatHandlers()
+{
+    std::vector<std::string> res;
+    res.reserve(chatHandlers_.size());
+    for (const auto& chatHandler : chatHandlers_) {
+        res.emplace_back(std::to_string((uintptr_t) chatHandler.get()));
+    }
+    return res;
+}
+
+void
+ChatServicesManager::publishMessage(pluginMessagePtr& cm)
+{
+    if (cm->fromPlugin)
+        return;
+    std::pair<std::string, std::string> mPair(cm->accountId, cm->peerId);
+    auto& handlers = chatHandlerToggled_[mPair];
+    for (auto& chatHandler : chatHandlers_) {
+        std::size_t found = chatHandler->id().find_last_of(DIR_SEPARATOR_CH);
+        auto preferences = PluginPreferencesUtils::getPreferencesValuesMap(
+            chatHandler->id().substr(0, found));
+        bool toggled = false;
+        if (handlers.find((uintptr_t) chatHandler.get()) != handlers.end())
+            toggled = true;
+        if (preferences.at("always") == "1" || toggled) {
+            chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>());
+            if (!toggled) {
+                handlers.insert((uintptr_t) chatHandler.get());
+                chatHandler->notifyChatSubject(mPair, chatSubjects_[mPair]);
+            }
+            chatSubjects_[mPair]->publish(cm);
+        }
+    }
+}
+
+void
+ChatServicesManager::cleanChatSubjects(const std::string& accountId, const std::string& peerId)
+{
+    std::pair<std::string, std::string> mPair(accountId, peerId);
+    for (auto it = chatSubjects_.begin(); it != chatSubjects_.end();) {
+        if (peerId.empty() && it->first.first == accountId)
+            it = chatSubjects_.erase(it);
+        else if (!peerId.empty() && it->first == mPair)
+            it = chatSubjects_.erase(it);
+        else
+            ++it;
+    }
+}
+
+void
+ChatServicesManager::toggleChatHandler(const std::string& chatHandlerId,
+                        const std::string& accountId,
+                        const std::string& peerId,
+                        const bool toggle)
+{
+    toggleChatHandler(std::stoull(chatHandlerId), accountId, peerId, toggle);
+}
+
+std::vector<std::string>
+ChatServicesManager::getChatHandlerStatus(const std::string& accountId,
+                                                const std::string& peerId)
+{
+    std::pair<std::string, std::string> mPair(accountId, peerId);
+    const auto& it = chatHandlerToggled_.find(mPair);
+    std::vector<std::string> ret;
+    if (it != chatHandlerToggled_.end()) {
+        ret.reserve(it->second.size());
+        for (const auto& chatHandlerId : it->second)
+            ret.emplace_back(std::to_string(chatHandlerId));
+        return ret;
+    }
+
+    return ret;
+}
+
+std::map<std::string, std::string>
+ChatServicesManager::getChatHandlerDetails(const std::string& chatHandlerIdStr)
+{
+    auto chatHandlerId = std::stoull(chatHandlerIdStr);
+    for (auto& chatHandler : chatHandlers_) {
+        if ((uintptr_t) chatHandler.get() == chatHandlerId) {
+            return chatHandler->getChatHandlerDetails();
+        }
+    }
+    return {};
+}
+
+void
+ChatServicesManager::setPreference(const std::string& key, const std::string& value, const std::string& scopeStr)
+{
+    for (auto& chatHandler : chatHandlers_) {
+        if (scopeStr.find(chatHandler->getChatHandlerDetails()["name"]) != std::string::npos) {
+            chatHandler->setPreferenceAttribute(key, value);
+        }
+    }
+}
+
+void
+ChatServicesManager::toggleChatHandler(const uintptr_t chatHandlerId,
+                        const std::string& accountId,
+                        const std::string& peerId,
+                        const bool toggle)
+{
+    std::pair<std::string, std::string> mPair(accountId, peerId);
+    auto& handlers = chatHandlerToggled_[mPair];
+    chatSubjects_.emplace(mPair, std::make_shared<PublishObservable<pluginMessagePtr>>());
+
+    auto chatHandlerIt = std::find_if(chatHandlers_.begin(), chatHandlers_.end(),
+                    [chatHandlerId](ChatHandlerPtr& handler) {
+                        return ((uintptr_t) handler.get() == chatHandlerId);
+                        });
+
+    if (chatHandlerIt != chatHandlers_.end()) {
+        if (toggle) {
+            (*chatHandlerIt)->notifyChatSubject(mPair, chatSubjects_[mPair]);
+            if (handlers.find(chatHandlerId) == handlers.end())
+                handlers.insert(chatHandlerId);
+        } else {
+            (*chatHandlerIt)->detach(chatSubjects_[mPair]);
+            handlers.erase(chatHandlerId);
+        }
+    }
+}
+} // namespace jami
diff --git a/src/plugin/chatservicesmanager.h b/src/plugin/chatservicesmanager.h
new file mode 100644
index 0000000000000000000000000000000000000000..de4bceb9ce5cde84ee6cb1a6158494c6a9658164
--- /dev/null
+++ b/src/plugin/chatservicesmanager.h
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+#pragma once
+
+#include "noncopyable.h"
+#include "pluginmanager.h"
+#include "chathandler.h"
+
+namespace jami {
+
+using ChatHandlerPtr = std::unique_ptr<ChatHandler>;
+
+class ChatServicesManager
+{
+public:
+    ChatServicesManager(PluginManager& pm);
+
+    NON_COPYABLE(ChatServicesManager);
+
+    void registerComponentsLifeCycleManagers(PluginManager& pm);
+
+    void registerChatService(PluginManager& pm);
+
+    std::vector<std::string> getChatHandlers();
+
+    void publishMessage(pluginMessagePtr& cm);
+
+    void cleanChatSubjects(const std::string& accountId, const std::string& peerId = "");
+
+    void toggleChatHandler(const std::string& chatHandlerId,
+                           const std::string& accountId,
+                           const std::string& peerId,
+                           const bool toggle);
+
+    std::vector<std::string> getChatHandlerStatus(const std::string& accountId,
+                                                  const std::string& peerId);
+
+    /**
+     * @brief getChatHandlerDetails
+     * @param chatHandlerIdStr of the chat handler
+     * @return map of Chat Handler Details
+     */
+    std::map<std::string, std::string> getChatHandlerDetails(const std::string& chatHandlerIdStr);
+
+    void setPreference(const std::string& key, const std::string& value, const std::string& scopeStr);
+
+private:
+    void toggleChatHandler(const uintptr_t chatHandlerId,
+                           const std::string& accountId,
+                           const std::string& peerId,
+                           const bool toggle);
+
+    std::list<ChatHandlerPtr> chatHandlers_;
+    std::map<std::pair<std::string, std::string>, std::set<uintptr_t>>
+        chatHandlerToggled_; // {account,peer}, list of chatHandlers
+
+    std::map<std::pair<std::string, std::string>, chatSubjectPtr> chatSubjects_;
+};
+} // namespace jami
diff --git a/src/plugin/conversationhandler.h b/src/plugin/conversationhandler.h
deleted file mode 100644
index 86c36fb79890e48915b3caa86825c63942029436..0000000000000000000000000000000000000000
--- a/src/plugin/conversationhandler.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-#pragma once
-#include "observer.h"
-#include <string>
-#include <map>
-
-namespace jami {
-struct ConversationMessage
-{
-    ConversationMessage(const std::string& author,
-                        const std::string& to,
-                        std::map<std::string, std::string>& dataMap)
-        : author_ {author}
-        , to_ {to}
-        , data_ {dataMap}
-    {}
-    std::string author_;
-    std::string to_;
-    std::map<std::string, std::string> data_;
-};
-
-using ConvMsgPtr = std::shared_ptr<ConversationMessage>;
-
-using strMapSubjectPtr = std::shared_ptr<PublishObservable<ConvMsgPtr>>;
-
-class ConversationHandler
-{
-public:
-    virtual ~ConversationHandler() = default;
-    virtual void notifyStrMapSubject(const bool direction, strMapSubjectPtr subject) = 0;
-};
-} // namespace jami
diff --git a/src/plugin/conversationservicesmanager.h b/src/plugin/conversationservicesmanager.h
deleted file mode 100644
index a4df61096ff8f12ca368ac29b987a0a700847b5d..0000000000000000000000000000000000000000
--- a/src/plugin/conversationservicesmanager.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-#pragma once
-// Utils
-#include "noncopyable.h"
-// Plugin Manager
-#include "pluginmanager.h"
-#include "conversationhandler.h"
-#include "logger.h"
-// Manager
-#include "manager.h"
-
-namespace jami {
-
-using ConversationHandlerPtr = std::unique_ptr<ConversationHandler>;
-
-class ConversationServicesManager
-{
-public:
-    ConversationServicesManager(PluginManager& pm)
-    {
-        registerComponentsLifeCycleManagers(pm);
-
-        auto sendTextMessage = [this](const DLPlugin* plugin, void* data) {
-            auto cm = static_cast<ConversationMessage*>(data);
-            jami::Manager::instance().sendTextMessage(cm->author_, cm->to_, cm->data_);
-            return 0;
-        };
-
-        pm.registerService("sendTextMessage", sendTextMessage);
-    }
-    NON_COPYABLE(ConversationServicesManager);
-
-public:
-    void registerComponentsLifeCycleManagers(PluginManager& pm)
-    {
-        auto registerConversationHandler = [this](void* data) {
-            ConversationHandlerPtr ptr {(static_cast<ConversationHandler*>(data))};
-
-            if (ptr) {
-                conversationHandlers.push_back(std::make_pair(false, std::move(ptr)));
-                if (!conversationHandlers.empty()) {
-                    listAvailableSubjects(conversationHandlers.back().second);
-                }
-            }
-
-            return 0;
-        };
-
-        auto unregisterConversationHandler = [this](void* data) {
-            for (auto it = conversationHandlers.begin(); it != conversationHandlers.end(); ++it) {
-                if (it->second.get() == data) {
-                    conversationHandlers.erase(it);
-                    break;
-                }
-            }
-            return 0;
-        };
-
-        pm.registerComponentManager("ConversationHandlerManager",
-                                    registerConversationHandler,
-                                    unregisterConversationHandler);
-    }
-
-    void onTextMessage(std::shared_ptr<ConversationMessage>& cm)
-    {
-        if (!conversationHandlers.empty()) {
-            receiveSubject->publish(cm);
-        }
-    }
-
-    void sendTextMessage(std::shared_ptr<ConversationMessage>& cm)
-    {
-        if (!conversationHandlers.empty()) {
-            sendSubject->publish(cm);
-        }
-    }
-
-private:
-    void listAvailableSubjects(ConversationHandlerPtr& plugin)
-    {
-        plugin->notifyStrMapSubject(false, sendSubject);
-        plugin->notifyStrMapSubject(true, receiveSubject);
-    }
-
-private:
-    std::list<std::pair<bool, ConversationHandlerPtr>> conversationHandlers;
-    strMapSubjectPtr sendSubject
-        = std::make_shared<PublishObservable<std::shared_ptr<ConversationMessage>>>();
-    strMapSubjectPtr receiveSubject
-        = std::make_shared<PublishObservable<std::shared_ptr<ConversationMessage>>>();
-};
-} // namespace jami
diff --git a/src/plugin/jamipluginmanager.cpp b/src/plugin/jamipluginmanager.cpp
index 2f080318fd11f9fe831a144434b488404f4d20a5..267a2d4bbaa83684bf5a5afbdd315f700825d593 100644
--- a/src/plugin/jamipluginmanager.cpp
+++ b/src/plugin/jamipluginmanager.cpp
@@ -25,8 +25,7 @@
 #include <fstream>
 #include <regex>
 #include <stdexcept>
-
-// Manager
+#include <msgpack.hpp>
 #include "manager.h"
 #include "preferences.h"
 
@@ -34,9 +33,6 @@ extern "C" {
 #include <archive.h>
 }
 
-#include <json/json.h>
-#include <msgpack.hpp>
-
 #if defined(__arm__)
 #if defined(__ARM_ARCH_7A__)
 #define ABI "armeabi-v7a"
@@ -147,46 +143,6 @@ uncompressJplFunction(const std::string& relativeFileName)
     return std::make_pair(false, std::string {""});
 }
 
-std::string
-convertArrayToString(const Json::Value& jsonArray)
-{
-    std::string stringArray = "";
-
-    if (jsonArray.size()) {
-        for (unsigned i = 0; i < jsonArray.size() - 1; i++) {
-            if (jsonArray[i].isString()) {
-                stringArray += jsonArray[i].asString() + ",";
-            } else if (jsonArray[i].isArray()) {
-                stringArray += convertArrayToString(jsonArray[i]) + ",";
-            }
-        }
-
-        unsigned lastIndex = jsonArray.size() - 1;
-        if (jsonArray[lastIndex].isString()) {
-            stringArray += jsonArray[lastIndex].asString();
-        }
-    }
-
-    return stringArray;
-}
-
-std::map<std::string, std::string>
-parsePreferenceConfig(const Json::Value& jsonPreference, const std::string& type)
-{
-    std::map<std::string, std::string> preferenceMap;
-    const auto& members = jsonPreference.getMemberNames();
-    // Insert other fields
-    for (const auto& member : members) {
-        const Json::Value& value = jsonPreference[member];
-        if (value.isString()) {
-            preferenceMap.emplace(member, jsonPreference[member].asString());
-        } else if (value.isArray()) {
-            preferenceMap.emplace(member, convertArrayToString(jsonPreference[member]));
-        }
-    }
-    return preferenceMap;
-}
-
 std::map<std::string, std::string>
 JamiPluginManager::getPluginDetails(const std::string& rootPath)
 {
@@ -206,7 +162,7 @@ JamiPluginManager::getPluginDetails(const std::string& rootPath)
 }
 
 std::vector<std::string>
-JamiPluginManager::listAvailablePlugins()
+JamiPluginManager::getInstalledPlugins()
 {
     std::string pluginsPath = fileutils::get_data_dir() + DIR_SEPARATOR_CH + "plugins";
     std::vector<std::string> pluginsPaths = fileutils::readDirectory(pluginsPath);
@@ -329,30 +285,10 @@ JamiPluginManager::unloadPlugin(const std::string& rootPath)
     return false;
 }
 
-void
-JamiPluginManager::togglePlugin(const std::string& rootPath, bool toggle)
-{
-    // This function should not be used as is
-    // One should modify it to perform plugin install followed by load
-    // rootPath should be the jplpath!
-    try {
-        std::string soPath = getPluginDetails(rootPath).at("soPath");
-        // remove the previous plugin object if it was registered
-        pm_.destroyPluginComponents(soPath);
-        // If toggle, register a new instance of the plugin
-        // function
-        if (toggle) {
-            pm_.callPluginInitFunction(soPath);
-        }
-    } catch (const std::exception& e) {
-        JAMI_ERR() << e.what();
-    }
-}
-
 std::vector<std::string>
-JamiPluginManager::listLoadedPlugins() const
+JamiPluginManager::getLoadedPlugins() const
 {
-    std::vector<std::string> loadedSoPlugins = pm_.listLoadedPlugins();
+    std::vector<std::string> loadedSoPlugins = pm_.getLoadedPlugins();
     std::vector<std::string> loadedPlugins {};
     loadedPlugins.reserve(loadedSoPlugins.size());
     std::transform(loadedSoPlugins.begin(),
@@ -365,81 +301,13 @@ JamiPluginManager::listLoadedPlugins() const
 std::vector<std::map<std::string, std::string>>
 JamiPluginManager::getPluginPreferences(const std::string& rootPath)
 {
-    const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath);
-    std::ifstream file(preferenceFilePath);
-    Json::Value root;
-    Json::CharReaderBuilder rbuilder;
-    rbuilder["collectComments"] = false;
-    std::string errs;
-    std::set<std::string> keys;
-    std::vector<std::map<std::string, std::string>> preferences;
-    if (file) {
-        bool ok = Json::parseFromStream(rbuilder, file, &root, &errs);
-        if (ok && root.isArray()) {
-            for (unsigned i = 0; i < root.size(); i++) {
-                const Json::Value& jsonPreference = root[i];
-                std::string category = jsonPreference.get("category", "NoCategory").asString();
-                std::string type = jsonPreference.get("type", "None").asString();
-                std::string key = jsonPreference.get("key", "None").asString();
-                if (type != "None" && key != "None") {
-                    if (keys.find(key) == keys.end()) {
-                        auto preferenceAttributes = parsePreferenceConfig(jsonPreference, type);
-                        // If the parsing of the attributes was successful, commit the map and the keys
-                        auto defaultValue = preferenceAttributes.find("defaultValue");
-                        if (type == "Path" && defaultValue != preferenceAttributes.end()) {
-                            defaultValue->second = rootPath + DIR_SEPARATOR_STR
-                                                   + defaultValue->second;
-                        }
-
-                        if (!preferenceAttributes.empty()) {
-                            preferences.push_back(std::move(preferenceAttributes));
-                            keys.insert(key);
-                        }
-                    }
-                }
-            }
-        } else {
-            JAMI_ERR() << "PluginPreferencesParser:: Failed to parse preferences.json for plugin: "
-                       << preferenceFilePath;
-        }
-    }
-
-    return preferences;
+    return PluginPreferencesUtils::getPreferences(rootPath);
 }
 
 std::map<std::string, std::string>
 JamiPluginManager::getPluginUserPreferencesValuesMap(const std::string& rootPath)
 {
-    const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
-    std::ifstream file(preferencesValuesFilePath, std::ios::binary);
-    std::map<std::string, std::string> rmap;
-
-    // If file is accessible
-    if (file.good()) {
-        std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
-        // Get file size
-        std::string str;
-        file.seekg(0, std::ios::end);
-        size_t fileSize = static_cast<size_t>(file.tellg());
-        // If not empty
-        if (fileSize > 0) {
-            // Read whole file content and put it in the string str
-            str.reserve(static_cast<size_t>(file.tellg()));
-            file.seekg(0, std::ios::beg);
-            str.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
-            file.close();
-            try {
-                // Unpack the string
-                msgpack::object_handle oh = msgpack::unpack(str.data(), str.size());
-                // Deserialized object is valid during the msgpack::object_handle instance is alive.
-                msgpack::object deserialized = oh.get();
-                deserialized.convert(rmap);
-            } catch (const std::exception& e) {
-                JAMI_ERR() << e.what();
-            }
-        }
-    }
-    return rmap;
+    return PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath);
 }
 
 bool
@@ -447,23 +315,26 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath,
                                        const std::string& key,
                                        const std::string& value)
 {
-    std::map<std::string, std::string> pluginUserPreferencesMap = getPluginUserPreferencesValuesMap(
-        rootPath);
-    std::map<std::string, std::string> pluginPreferencesMap = getPluginPreferencesValuesMap(
+    std::map<std::string, std::string> pluginUserPreferencesMap
+        = PluginPreferencesUtils::getUserPreferencesValuesMap(rootPath);
+    std::map<std::string, std::string> pluginPreferencesMap = PluginPreferencesUtils::getPreferencesValuesMap(
         rootPath);
 
     auto find = pluginPreferencesMap.find(key);
     if (find != pluginPreferencesMap.end()) {
-        std::vector<std::map<std::string, std::string>> preferences = getPluginPreferences(rootPath);
+        std::vector<std::map<std::string, std::string>> preferences = PluginPreferencesUtils::getPreferences(
+            rootPath);
         for (auto& preference : preferences) {
             if (!preference["key"].compare(key)) {
-                csm_.setPreference(key, value, preference["scope"]);
+                callsm_.setPreference(key, value, preference["scope"]);
+                chatsm_.setPreference(key, value, preference["scope"]);
                 break;
             }
         }
 
         pluginUserPreferencesMap[key] = value;
-        const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
+        const std::string preferencesValuesFilePath = PluginPreferencesUtils::valuesFilePath(
+            rootPath);
         std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
         if (!fs.good()) {
             return false;
@@ -483,39 +354,13 @@ JamiPluginManager::setPluginPreference(const std::string& rootPath,
 std::map<std::string, std::string>
 JamiPluginManager::getPluginPreferencesValuesMap(const std::string& rootPath)
 {
-    std::map<std::string, std::string> rmap;
-
-    std::vector<std::map<std::string, std::string>> preferences = getPluginPreferences(rootPath);
-    for (auto& preference : preferences) {
-        rmap[preference["key"]] = preference["defaultValue"];
-    }
-
-    for (const auto& pair : getPluginUserPreferencesValuesMap(rootPath)) {
-        rmap[pair.first] = pair.second;
-    }
-    return rmap;
+    return PluginPreferencesUtils::getPreferencesValuesMap(rootPath);
 }
 
 bool
 JamiPluginManager::resetPluginPreferencesValuesMap(const std::string& rootPath)
 {
-    bool returnValue = true;
-    std::map<std::string, std::string> pluginPreferencesMap {};
-
-    const std::string preferencesValuesFilePath = pluginPreferencesValuesFilePath(rootPath);
-    std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
-    if (!fs.good()) {
-        return false;
-    }
-    try {
-        std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
-        msgpack::pack(fs, pluginPreferencesMap);
-    } catch (const std::exception& e) {
-        returnValue = false;
-        JAMI_ERR() << e.what();
-    }
-
-    return returnValue;
+    return PluginPreferencesUtils::resetPreferencesValuesMap(rootPath);
 }
 
 std::map<std::string, std::string>
diff --git a/src/plugin/jamipluginmanager.h b/src/plugin/jamipluginmanager.h
index 3083050ac776327e00c45fd78618508ddfd94cee..8f44beeaf2cb146a9a733d04910b820e829a0619 100644
--- a/src/plugin/jamipluginmanager.h
+++ b/src/plugin/jamipluginmanager.h
@@ -23,10 +23,11 @@
 #include "fileutils.h"
 #include "archiver.h"
 #include "pluginmanager.h"
+#include "pluginpreferencesutils.h"
 
 // Services
 #include "callservicesmanager.h"
-#include "conversationservicesmanager.h"
+#include "chatservicesmanager.h"
 
 #include <vector>
 #include <map>
@@ -39,8 +40,8 @@ class JamiPluginManager
 {
 public:
     JamiPluginManager()
-        : csm_ {pm_}
-        , convsm_ {pm_}
+        : callsm_ {pm_}
+        , chatsm_ {pm_}
     {
         registerServices();
     }
@@ -59,11 +60,11 @@ public:
     std::map<std::string, std::string> getPluginDetails(const std::string& rootPath);
 
     /**
-     * @brief listAvailablePlugins
+     * @brief getInstalledPlugins
      * Lists available plugins with valid manifest files
      * @return list of plugin directory names
      */
-    std::vector<std::string> listAvailablePlugins();
+    std::vector<std::string> getInstalledPlugins();
 
     /**
      * @brief installPlugin
@@ -102,19 +103,10 @@ public:
     bool unloadPlugin(const std::string& rootPath);
 
     /**
-     * @brief togglePlugin
-     * @param rootPath of the plugin folder
-     * @param toggle: if true, register a new instance of the plugin
-     * else, remove the existing instance
-     * N.B: before adding a new instance, remove any existing one
-     */
-    void togglePlugin(const std::string& rootPath, bool toggle);
-
-    /**
-     * @brief listLoadedPlugins
+     * @brief getLoadedPlugins
      * @return vector of rootpaths of the loaded plugins
      */
-    std::vector<std::string> listLoadedPlugins() const;
+    std::vector<std::string> getLoadedPlugins() const;
 
     std::vector<std::map<std::string, std::string>> getPluginPreferences(const std::string& rootPath);
 
@@ -126,10 +118,9 @@ public:
 
     bool resetPluginPreferencesValuesMap(const std::string& rootPath);
 
-public:
-    CallServicesManager& getCallServicesManager() { return csm_; }
+    CallServicesManager& getCallServicesManager() { return callsm_; }
 
-    ConversationServicesManager& getConversationServicesManager() { return convsm_; }
+    ChatServicesManager& getChatServicesManager() { return chatsm_; }
 
 private:
     NON_COPYABLE(JamiPluginManager);
@@ -191,7 +182,7 @@ private:
      */
     std::string getPreferencesConfigFilePath(const std::string& rootPath) const
     {
-        return rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "preferences.json";
+        return PluginPreferencesUtils::getPreferencesConfigFilePath(rootPath);
     }
 
     /**
@@ -203,18 +194,16 @@ private:
      */
     std::string pluginPreferencesValuesFilePath(const std::string& rootPath) const
     {
-        return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack";
+        return PluginPreferencesUtils::valuesFilePath(rootPath);
     }
 
     void registerServices();
 
-private:
     PluginManager pm_;
     std::map<std::string, std::map<std::string, std::string>> pluginDetailsMap_;
 
     // Services
-private:
-    CallServicesManager csm_;
-    ConversationServicesManager convsm_;
+    CallServicesManager callsm_;
+    ChatServicesManager chatsm_;
 };
 } // namespace jami
diff --git a/src/plugin/mediahandler.h b/src/plugin/mediahandler.h
index 7a25457da3f8e03981ecf6663b05b702a5b0b6b2..81cb2c3685b2b5c60d1f8b2239687cde6034a67e 100644
--- a/src/plugin/mediahandler.h
+++ b/src/plugin/mediahandler.h
@@ -1,5 +1,7 @@
 /*
- *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -15,7 +17,9 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
+
 #pragma once
+
 #include "streamdata.h"
 #include "observer.h"
 #include <libavutil/frame.h>
diff --git a/src/plugin/pluginmanager.cpp b/src/plugin/pluginmanager.cpp
index e63ad5c132a49bb6942e7c4e7273aafcfda932b3..56a740665a6e35cd7595ec37903e6a17f966466d 100644
--- a/src/plugin/pluginmanager.cpp
+++ b/src/plugin/pluginmanager.cpp
@@ -79,7 +79,7 @@ PluginManager::load(const std::string& path)
 bool
 PluginManager::unload(const std::string& path)
 {
-    bool returnValue {false};
+    bool returnValue{false};
     destroyPluginComponents(path);
     PluginMap::iterator it = dynPluginMap_.find(path);
     if (it != dynPluginMap_.end()) {
@@ -97,7 +97,7 @@ PluginManager::checkLoadedPlugin(const std::string& rootPath) const
 }
 
 std::vector<std::string>
-PluginManager::listLoadedPlugins() const
+PluginManager::getLoadedPlugins() const
 {
     std::vector<std::string> res {};
     for (const auto& pair : dynPluginMap_) {
@@ -375,5 +375,4 @@ PluginManager::registerObjectFactory_(const JAMI_PluginAPI* api, const char* typ
     const auto factory = reinterpret_cast<JAMI_PluginObjectFactory*>(data);
     return manager->registerObjectFactory(type, *factory) ? 0 : -1;
 }
-
 } // namespace jami
diff --git a/src/plugin/pluginmanager.h b/src/plugin/pluginmanager.h
index f2bcb408f2385d24abdcbb9f221bb7ea949cdcd9..582bc6c994b6126d252c9402ba380046028ea16b 100644
--- a/src/plugin/pluginmanager.h
+++ b/src/plugin/pluginmanager.h
@@ -85,10 +85,10 @@ public:
     bool unload(const std::string& path);
 
     /**
-     * @brief listLoadedPlugins
+     * @brief getLoadedPlugins
      * @return vector of strings of so files of the loaded plugins
      */
-    std::vector<std::string> listLoadedPlugins() const;
+    std::vector<std::string> getLoadedPlugins() const;
 
     /**
      * @brief checkLoadedPlugin
@@ -195,5 +195,4 @@ private:
     // references to plugins components, used for cleanup
     PluginComponentsMap pluginComponentsMap_ {};
 };
-
 } // namespace jami
diff --git a/src/plugin/pluginpreferencesutils.cpp b/src/plugin/pluginpreferencesutils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b48131914ae65694a66d7e5da697fafe560edc3
--- /dev/null
+++ b/src/plugin/pluginpreferencesutils.cpp
@@ -0,0 +1,205 @@
+/*
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA.
+ */
+
+#include "pluginpreferencesutils.h"
+
+#include <msgpack.hpp>
+#include <sstream>
+#include <fstream>
+#include "logger.h"
+#include "fileutils.h"
+
+namespace jami {
+
+std::string
+PluginPreferencesUtils::getPreferencesConfigFilePath(const std::string& rootPath)
+{
+    return rootPath + DIR_SEPARATOR_CH + "data" + DIR_SEPARATOR_CH + "preferences.json";
+}
+
+std::string
+PluginPreferencesUtils::valuesFilePath(const std::string& rootPath)
+{
+    return rootPath + DIR_SEPARATOR_CH + "preferences.msgpack";
+}
+
+std::string
+PluginPreferencesUtils::convertArrayToString(const Json::Value& jsonArray)
+{
+    std::string stringArray{};
+
+    if (jsonArray.size()) {
+        for (unsigned i = 0; i < jsonArray.size() - 1; i++) {
+            if (jsonArray[i].isString()) {
+                stringArray += jsonArray[i].asString() + ",";
+            } else if (jsonArray[i].isArray()) {
+                stringArray += convertArrayToString(jsonArray[i]) + ",";
+            }
+        }
+
+        unsigned lastIndex = jsonArray.size() - 1;
+        if (jsonArray[lastIndex].isString()) {
+            stringArray += jsonArray[lastIndex].asString();
+        }
+    }
+
+    return stringArray;
+}
+
+std::map<std::string, std::string>
+PluginPreferencesUtils::parsePreferenceConfig(const Json::Value& jsonPreference, const std::string& type)
+{
+    std::map<std::string, std::string> preferenceMap;
+    const auto& members = jsonPreference.getMemberNames();
+    // Insert other fields
+    for (const auto& member : members) {
+        const Json::Value& value = jsonPreference[member];
+        if (value.isString()) {
+            preferenceMap.emplace(member, jsonPreference[member].asString());
+        } else if (value.isArray()) {
+            preferenceMap.emplace(member, convertArrayToString(jsonPreference[member]));
+        }
+    }
+    return preferenceMap;
+}
+
+std::vector<std::map<std::string, std::string>>
+PluginPreferencesUtils::getPreferences(const std::string& rootPath)
+{
+    const std::string preferenceFilePath = getPreferencesConfigFilePath(rootPath);
+    std::ifstream file(preferenceFilePath);
+    Json::Value root;
+    Json::CharReaderBuilder rbuilder;
+    rbuilder["collectComments"] = false;
+    std::string errs;
+    std::set<std::string> keys;
+    std::vector<std::map<std::string, std::string>> preferences;
+    if (file) {
+        bool ok = Json::parseFromStream(rbuilder, file, &root, &errs);
+        if (ok && root.isArray()) {
+            for (unsigned i = 0; i < root.size(); i++) {
+                const Json::Value& jsonPreference = root[i];
+                std::string category = jsonPreference.get("category", "NoCategory").asString();
+                std::string type = jsonPreference.get("type", "None").asString();
+                std::string key = jsonPreference.get("key", "None").asString();
+                if (type != "None" && key != "None") {
+                    if (keys.find(key) == keys.end()) {
+                        auto preferenceAttributes = parsePreferenceConfig(jsonPreference, type);
+                        // If the parsing of the attributes was successful, commit the map and the keys
+                        auto defaultValue = preferenceAttributes.find("defaultValue");
+                        if (type == "Path" && defaultValue != preferenceAttributes.end()) {
+                            defaultValue->second = rootPath + DIR_SEPARATOR_STR
+                                                   + defaultValue->second;
+                        }
+
+                        if (!preferenceAttributes.empty()) {
+                            preferences.push_back(std::move(preferenceAttributes));
+                            keys.insert(key);
+                        }
+                    }
+                }
+            }
+        } else {
+            JAMI_ERR() << "PluginPreferencesParser:: Failed to parse preferences.json for plugin: "
+                       << preferenceFilePath;
+        }
+    }
+
+    return preferences;
+}
+
+std::map<std::string, std::string>
+PluginPreferencesUtils::getUserPreferencesValuesMap(const std::string& rootPath)
+{
+    const std::string preferencesValuesFilePath = valuesFilePath(rootPath);
+    std::ifstream file(preferencesValuesFilePath, std::ios::binary);
+    std::map<std::string, std::string> rmap;
+
+    // If file is accessible
+    if (file.good()) {
+        std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
+        // Get file size
+        std::string str;
+        file.seekg(0, std::ios::end);
+        size_t fileSize = static_cast<size_t>(file.tellg());
+        // If not empty
+        if (fileSize > 0) {
+            // Read whole file content and put it in the string str
+            str.reserve(static_cast<size_t>(file.tellg()));
+            file.seekg(0, std::ios::beg);
+            str.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+            file.close();
+            try {
+                // Unpack the string
+                msgpack::object_handle oh = msgpack::unpack(str.data(), str.size());
+                // Deserialized object is valid during the msgpack::object_handle instance is alive.
+                msgpack::object deserialized = oh.get();
+                deserialized.convert(rmap);
+            } catch (const std::exception& e) {
+                JAMI_ERR() << e.what();
+            }
+        }
+    }
+    return rmap;
+}
+
+std::map<std::string, std::string>
+PluginPreferencesUtils::getPreferencesValuesMap(const std::string& rootPath)
+{
+    std::map<std::string, std::string> rmap;
+
+    std::vector<std::map<std::string, std::string>> preferences = getPreferences(
+        rootPath);
+    for (auto& preference : preferences) {
+        rmap[preference["key"]] = preference["defaultValue"];
+    }
+
+    for (const auto& pair : getUserPreferencesValuesMap(rootPath)) {
+        rmap[pair.first] = pair.second;
+    }
+
+    rmap.emplace("always", "0");
+
+    return rmap;
+}
+
+bool
+PluginPreferencesUtils::resetPreferencesValuesMap(const std::string& rootPath)
+{
+    bool returnValue = true;
+    std::map<std::string, std::string> pluginPreferencesMap {};
+
+    const std::string preferencesValuesFilePath = valuesFilePath(rootPath);
+    std::ofstream fs(preferencesValuesFilePath, std::ios::binary);
+    if (!fs.good()) {
+        return false;
+    }
+    try {
+        std::lock_guard<std::mutex> guard(fileutils::getFileLock(preferencesValuesFilePath));
+        msgpack::pack(fs, pluginPreferencesMap);
+    } catch (const std::exception& e) {
+        returnValue = false;
+        JAMI_ERR() << e.what();
+    }
+
+    return returnValue;
+}
+} // namespace jami
diff --git a/src/plugin/pluginpreferencesutils.h b/src/plugin/pluginpreferencesutils.h
new file mode 100644
index 0000000000000000000000000000000000000000..9a7ce1a98b65d90f657d048ce495756af2f796c3
--- /dev/null
+++ b/src/plugin/pluginpreferencesutils.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA.
+ */
+
+#pragma once
+
+#include <json/json.h>
+#include <string>
+
+namespace jami {
+
+class PluginPreferencesUtils {
+public:
+    static std::string getPreferencesConfigFilePath(const std::string& rootPath);
+
+    static std::string valuesFilePath(const std::string& rootPath);
+
+    static std::string convertArrayToString(const Json::Value& jsonArray);
+
+    static std::map<std::string, std::string> parsePreferenceConfig(const Json::Value& jsonPreference,
+                                                            const std::string& type);
+
+    static std::vector<std::map<std::string, std::string>> getPreferences(
+        const std::string& rootPath);
+
+    static std::map<std::string, std::string> getUserPreferencesValuesMap(
+        const std::string& rootPath);
+
+    static std::map<std::string, std::string> getPreferencesValuesMap(const std::string& rootPath);
+
+    static bool resetPreferencesValuesMap(const std::string& rootPath);
+private:
+    PluginPreferencesUtils() {}
+    ~PluginPreferencesUtils() {}
+};
+} // namespace jami
diff --git a/src/plugin/streamdata.h b/src/plugin/streamdata.h
index 522a67b3ee23d83294becf71a0da062022761ab8..9746bf428c355f496ead9747cfaa486088c579c4 100644
--- a/src/plugin/streamdata.h
+++ b/src/plugin/streamdata.h
@@ -1,5 +1,7 @@
 /*
- *  Copyright (C) 2004-2019 Savoir-faire Linux Inc.
+ *  Copyright (C) 2020 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -17,10 +19,11 @@
  */
 #pragma once
 #include <string>
+#include <map>
 
 enum class StreamType { audio, video };
 
-struct StreamData
+struct StreamData // for calls
 {
     StreamData(const std::string& i, bool d, StreamType&& t, const std::string& s)
         : id {std::move(i)}
@@ -33,3 +36,24 @@ struct StreamData
     const StreamType type;
     const std::string source;
 };
+
+struct JamiMessage // for chat
+{
+    JamiMessage(const std::string& accId,
+                const std::string& pId,
+                bool direction,
+                std::map<std::string, std::string>& dataMap,
+                bool pPlugin)
+        : accountId {accId}
+        , peerId {pId}
+        , direction {direction}
+        , data {dataMap}
+        , fromPlugin {pPlugin}
+    {}
+
+    std::string accountId; // accountID
+    std::string peerId;    // peer
+    const bool direction; // 0 -> send; 1 -> received
+    std::map<std::string, std::string> data;
+    bool fromPlugin;
+};
\ No newline at end of file
diff --git a/src/sip/sipaccountbase.cpp b/src/sip/sipaccountbase.cpp
index 9f6983d2e3b7b115b4b672b06e7429d25858a4ae..3749b37fdb9616f726b486c5ab5ef82a0bee3af7 100644
--- a/src/sip/sipaccountbase.cpp
+++ b/src/sip/sipaccountbase.cpp
@@ -51,6 +51,7 @@
 #include "manager.h"
 #ifdef ENABLE_PLUGIN
 #include "plugin/jamipluginmanager.h"
+#include "plugin/streamdata.h"
 #endif
 namespace jami {
 
@@ -562,16 +563,16 @@ SIPAccountBase::onTextMessage(const std::string& id,
     }
 
 #ifdef ENABLE_PLUGIN
-    auto& convManager
-        = jami::Manager::instance().getJamiPluginManager().getConversationServicesManager();
-    std::shared_ptr<ConversationMessage> cm = std::make_shared<ConversationMessage>(
-        from, accountID_, const_cast<std::map<std::string, std::string>&>(payloads));
-    convManager.onTextMessage(cm);
-    emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, id, from, cm->data_);
+    auto& pluginChatManager
+        = jami::Manager::instance().getJamiPluginManager().getChatServicesManager();
+    std::shared_ptr<JamiMessage> cm = std::make_shared<JamiMessage>(
+        accountID_, from, true, const_cast<std::map<std::string, std::string>&>(payloads), false);
+    pluginChatManager.publishMessage(cm);
+    emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, id, from, cm->data);
 
     DRing::Message message;
-    message.from = cm->author_;
-    message.payloads = cm->data_;
+    message.from = from;
+    message.payloads = cm->data;
 #else
     emitSignal<DRing::ConfigurationSignal::IncomingAccountMessage>(accountID_, id, from, payloads);
 
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 3cb910f42c7436dfc9f24d42968585044e4111e2..7ff93c7bcf8ac16fff54d778b582a86ef7932df3 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -140,14 +140,14 @@ SIPCall::createCallAVStreams()
     // Preview
     if (auto& localAudio = avformatrtp_->getAudioLocal()) {
         auto previewSubject = std::make_shared<MediaStreamSubject>(audioMap);
-        StreamData microStreamData {getCallId(), 0, StreamType::audio, getPeerNumber()};
+        StreamData microStreamData {getCallId(), false, StreamType::audio, getPeerNumber()};
         createCallAVStream(microStreamData, *localAudio, previewSubject);
     }
 
     // Receive
     if (auto& audioReceive = avformatrtp_->getAudioReceive()) {
         auto receiveSubject = std::make_shared<MediaStreamSubject>(audioMap);
-        StreamData phoneStreamData {getCallId(), 1, StreamType::audio, getPeerNumber()};
+        StreamData phoneStreamData {getCallId(), true, StreamType::audio, getPeerNumber()};
         createCallAVStream(phoneStreamData, (AVMediaStream&) *audioReceive, receiveSubject);
     }
 #ifdef ENABLE_VIDEO
@@ -162,14 +162,14 @@ SIPCall::createCallAVStreams()
         // Preview
         if (auto& videoPreview = videortp_->getVideoLocal()) {
             auto previewSubject = std::make_shared<MediaStreamSubject>(videoMap);
-            StreamData previewStreamData {getCallId(), 0, StreamType::video, getPeerNumber()};
+            StreamData previewStreamData {getCallId(), false, StreamType::video, getPeerNumber()};
             createCallAVStream(previewStreamData, *videoPreview, previewSubject);
         }
 
         // Receive
         if (auto& videoReceive = videortp_->getVideoReceive()) {
             auto receiveSubject = std::make_shared<MediaStreamSubject>(videoMap);
-            StreamData receiveStreamData {getCallId(), 1, StreamType::video, getPeerNumber()};
+            StreamData receiveStreamData {getCallId(), true, StreamType::video, getPeerNumber()};
             createCallAVStream(receiveStreamData, *videoReceive, receiveSubject);
         }
     }