diff --git a/daemon/bin/dbus/callmanager-introspec.xml b/daemon/bin/dbus/callmanager-introspec.xml
index 0e67423012d73ebc2bf9ec42a35d94ffc68f5662..912b2c3da42711f9afbb731ee6b668d63e78b795 100644
--- a/daemon/bin/dbus/callmanager-introspec.xml
+++ b/daemon/bin/dbus/callmanager-introspec.xml
@@ -792,5 +792,12 @@
             </tp:docstring>
             </arg>
         </signal>
+
+        <signal name="peerHold" tp:name-for-bindings="peerHold">
+            <tp:added version="2.0.0"/>
+            <arg type="s" name="callID" />
+            <arg type="b" name="peerHolding" />
+        </signal>
+
     </interface>
 </node>
diff --git a/daemon/bin/dbus/dbusclient.cpp b/daemon/bin/dbus/dbusclient.cpp
index a046aecdd2261e147abe79c2270feee27443d65b..268e33203716527b62dd845f9cffef393aab79ab 100644
--- a/daemon/bin/dbus/dbusclient.cpp
+++ b/daemon/bin/dbus/dbusclient.cpp
@@ -174,6 +174,7 @@ DBusClient::initLibrary(int sflphFlags)
         exportable_callback<CallSignal::ZrtpNotSuppOther>(bind(&DBusCallManager::zrtpNotSuppOther, callM, _1)),
         exportable_callback<CallSignal::ZrtpNegotiationFailed>(bind(&DBusCallManager::zrtpNegotiationFailed, callM, _1, _2, _3)),
         exportable_callback<CallSignal::RtcpReportReceived>(bind(&DBusCallManager::onRtcpReportReceived, callM, _1, _2)),
+        exportable_callback<CallSignal::PeerHold>(bind(&DBusCallManager::peerHold, callM, _1, _2))
     };
 
     // Configuration event handlers
diff --git a/daemon/src/account.cpp b/daemon/src/account.cpp
index 2b8c16bb9149a26faa966873fa228abf7732cd5b..b042077de4e856bedda4e3dea9789ae135752ca6 100644
--- a/daemon/src/account.cpp
+++ b/daemon/src/account.cpp
@@ -87,9 +87,6 @@ const char * const Account::HAS_CUSTOM_USER_AGENT_KEY   = "hasCustomUserAgent";
 const char * const Account::PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled";
 const char * const Account::UPNP_ENABLED_KEY            = "upnpEnabled";
 
-constexpr const char* Account::TRUE_STR;
-constexpr const char* Account::FALSE_STR;
-
 Account::Account(const std::string &accountID)
     : accountID_(accountID)
     , username_()
diff --git a/daemon/src/account.h b/daemon/src/account.h
index 7656b379522149887c22b39df141f2dc4c95902e..6f820214729725291b0190b8ef155cda6b2a2037 100644
--- a/daemon/src/account.h
+++ b/daemon/src/account.h
@@ -86,9 +86,6 @@ class VoipLinkException : public std::runtime_error
 class Account : public Serializable, public std::enable_shared_from_this<Account>
 {
     public:
-        constexpr static const char* TRUE_STR = "true";
-        constexpr static const char* FALSE_STR = "false";
-
         Account(const std::string& accountID);
 
         /**
diff --git a/daemon/src/client/signal.cpp b/daemon/src/client/signal.cpp
index 5f74e8d7c1dcd62fc0f518e6cc35e79f75f34b88..ae5e99a988bf09671af644cd05c1aa9970783454 100644
--- a/daemon/src/client/signal.cpp
+++ b/daemon/src/client/signal.cpp
@@ -60,6 +60,7 @@ getSignalHandlers()
         exported_callback<DRing::CallSignal::ZrtpNotSuppOther>(),
         exported_callback<DRing::CallSignal::ZrtpNegotiationFailed>(),
         exported_callback<DRing::CallSignal::RtcpReportReceived>(),
+        exported_callback<DRing::CallSignal::PeerHold>(),
 
         /* Configuration */
         exported_callback<DRing::ConfigurationSignal::VolumeChanged>(),
diff --git a/daemon/src/dring/call_const.h b/daemon/src/dring/call_const.h
index a2026bd316632fa798f7fdc4fec4648f914c8b1a..cf26a8b4e9c033e9bc0fbf889006928afd8b1fab 100644
--- a/daemon/src/dring/call_const.h
+++ b/daemon/src/dring/call_const.h
@@ -43,6 +43,7 @@ constexpr static char CALL_STATE               [] = "CALL_STATE"          ;
 constexpr static char CONF_ID                  [] = "CONF_ID"             ;
 constexpr static char TIMESTAMP_START          [] = "TIMESTAMP_START"     ;
 constexpr static char ACCOUNTID                [] = "ACCOUNTID"           ;
+constexpr static char PEER_HOLDING             [] = "PEER_HOLDING"        ;
 constexpr static char TLS_PEER_CERT            [] = "TLS_PEER_CERT"       ;
 constexpr static char TLS_CIPHER               [] = "TLS_CIPHER"          ;
 
diff --git a/daemon/src/dring/callmanager_interface.h b/daemon/src/dring/callmanager_interface.h
index 989be94a64a304b9d15dbb21b5fc8ad73e83c6d6..d24db1f6f2a510ef56804db255f9002c3a6116b1 100644
--- a/daemon/src/dring/callmanager_interface.h
+++ b/daemon/src/dring/callmanager_interface.h
@@ -198,6 +198,10 @@ struct CallSignal {
                 constexpr static const char* name = "RtcpReportReceived";
                 using cb_type = void(const std::string&, const std::map<std::string, int>&);
         };
+        struct PeerHold {
+                constexpr static const char* name = "PeerHold";
+                using cb_type = void(const std::string&, bool);
+        };
 };
 
 } // namespace DRing
diff --git a/daemon/src/sip/sdp.cpp b/daemon/src/sip/sdp.cpp
index 1ca46c8e22aa37c1e8abe892825778c1013b3f25..2075e74ab7f4b772051dda821a19e04ecf104fbf 100644
--- a/daemon/src/sip/sdp.cpp
+++ b/daemon/src/sip/sdp.cpp
@@ -258,7 +258,7 @@ Sdp::setMediaDescriptorLines(bool audio, bool holding, sip_utils::KeyExchangePro
         addRTCPAttribute(med); // video has its own RTCP
     }
 
-    med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(), holding ? (audio ? "recvonly" : "inactive") : "sendrecv", NULL);
+    med->attr[med->attr_count++] = pjmedia_sdp_attr_create(memPool_.get(), holding ? (audio ? "sendonly" : "inactive") : "sendrecv", NULL);
 
     if (kx == sip_utils::KeyExchangeProtocol::SDES) {
         if (pjmedia_sdp_media_add_attr(med, generateSdesAttribute()) != PJ_SUCCESS)
@@ -586,7 +586,7 @@ Sdp::getMediaSlots(const pjmedia_sdp_session* session, bool remote) const
         descr.addr = std::string(conn->addr.ptr, conn->addr.slen);
         descr.addr.setPort(media->desc.port);
 
-        descr.holding = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "recvonly", nullptr)
+        descr.holding = pjmedia_sdp_attr_find2(media->attr_count, media->attr, "sendonly", nullptr)
                      || pjmedia_sdp_attr_find2(media->attr_count, media->attr, "inactive", nullptr);
 
         // get codecs infos
diff --git a/daemon/src/sip/sipaccount.cpp b/daemon/src/sip/sipaccount.cpp
index d7703763a46867d7c1ac1a864372ca46e0a680d4..9066aa90530741b25652f4c5c9233a567e82ef9e 100644
--- a/daemon/src/sip/sipaccount.cpp
+++ b/daemon/src/sip/sipaccount.cpp
@@ -1751,7 +1751,7 @@ set_opt(const std::map<std::string, std::string> &details, const char *key, bool
     std::map<std::string, std::string>::const_iterator it = details.find(key);
 
     if (it != details.end())
-        val = it->second == Account::TRUE_STR;
+        val = it->second == TRUE_STR;
 }
 
 static void
diff --git a/daemon/src/sip/sipcall.cpp b/daemon/src/sip/sipcall.cpp
index 3dcb4cd9c009afef1c3d6cc67fd38055312eb271..dd4237829347e9e8769332a6fa6c330906a72232 100644
--- a/daemon/src/sip/sipcall.cpp
+++ b/daemon/src/sip/sipcall.cpp
@@ -42,6 +42,7 @@
 #include "sdp.h"
 #include "manager.h"
 #include "array_size.h"
+#include "string_utils.h"
 #include "upnp/upnp_control.h"
 
 #include "audio/audio_rtp_session.h"
@@ -53,6 +54,7 @@
 #endif
 
 #include "dring/call_const.h"
+#include "client/signal.h"
 
 #ifdef RING_VIDEO
 #include "client/videomanager.h"
@@ -775,6 +777,7 @@ SIPCall::startAllMedia()
     }
     auto slots = sdp_->getMediaSlots();
     unsigned ice_comp_id = 0;
+    bool peer_holding {true};
 
     for (const auto& slot : slots) {
         const auto& local = slot.first;
@@ -805,6 +808,8 @@ SIPCall::startAllMedia()
             continue;
         }
 
+        peer_holding &= remote.holding;
+
         if (isSecure() && (not local.crypto || not remote.crypto)) {
             RING_ERR("Can't perform secure call over insecure RTP transport");
             continue;
@@ -825,6 +830,10 @@ SIPCall::startAllMedia()
         } else
             rtp->start();
     }
+    if (peerHolding_ != peer_holding) {
+        peerHolding_ = peer_holding;
+        emitSignal<DRing::CallSignal::PeerHold>(getCallId(), peerHolding_);
+    }
 }
 
 void
@@ -928,6 +937,7 @@ std::map<std::string, std::string>
 SIPCall::getDetails() const
 {
     auto details = Call::getDetails();
+    details.emplace(DRing::Call::Details::PEER_HOLDING,         peerHolding_ ? TRUE_STR : FALSE_STR);
     if (transport_ and transport_->isSecure()) {
         const auto& tlsInfos = transport_->getTlsInfos();
         auto cipher = pj_ssl_cipher_name(tlsInfos.cipher);
diff --git a/daemon/src/sip/sipcall.h b/daemon/src/sip/sipcall.h
index 27fcaafff34d67df9562ec5f7004c61997eedaa8..36b76db139ca1a139f138e1f4498b61d7d5b81fe 100644
--- a/daemon/src/sip/sipcall.h
+++ b/daemon/src/sip/sipcall.h
@@ -243,6 +243,7 @@ class SIPCall : public Call
          * The SDP session
          */
         std::unique_ptr<Sdp> sdp_;
+        bool peerHolding_ {false};
 
         char contactBuffer_[PJSIP_MAX_URL_SIZE] {};
         pj_str_t contactHeader_ {contactBuffer_, 0};
diff --git a/daemon/src/string_utils.h b/daemon/src/string_utils.h
index 8f4daa6cdcd721ad2d967548e258590f299c557a..0e6bb3dcbdab95f18067eb519b83cdef8eb11439 100644
--- a/daemon/src/string_utils.h
+++ b/daemon/src/string_utils.h
@@ -41,6 +41,9 @@
 
 namespace ring {
 
+constexpr static const char* TRUE_STR = "true";
+constexpr static const char* FALSE_STR = "false";
+
 #ifdef __ANDROID__
 
 template <typename T>