Move existing active audio session to inactive before modifying current session to active

When there is already a active session running, and if request received to modify another opened session to active, then current active session is moved to inactive first and then requested opened session is moved to active.
If moving existing active session operation fails, then current modifySession request is rejected with NO_RESOURCES error

Updated UnitTests for changes

Bug: 300873164
Bug: 289749054
Test: Verified VoWifi calls, handovers, swap calls, DSDA calls
      atest ImsMediaNativeTests
Change-Id: I589c1094c342a4525522978ce8150aa816c088df
diff --git a/service/src/com/android/telephony/imsmedia/AudioSession.java b/service/src/com/android/telephony/imsmedia/AudioSession.java
index c3331d9..20a6781 100644
--- a/service/src/com/android/telephony/imsmedia/AudioSession.java
+++ b/service/src/com/android/telephony/imsmedia/AudioSession.java
@@ -469,6 +469,9 @@
 
     private void handleModifySessionRespose(AudioConfig config, int error) {
         try {
+            if (error != ImsMediaSession.RESULT_SUCCESS) {
+                Log.e(TAG, "modifySession failed with error: " + error);
+            }
             mCallback.onModifySessionResponse(config, error);
         }  catch (RemoteException e) {
             Log.e(TAG, "Failed to notify modifySessionResponse: " + e);
diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp
index b7ce3db..b3b51bd 100644
--- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp
+++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp
@@ -108,22 +108,22 @@
     {
         if ((config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE ||
                     config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY ||
-                    config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_ONLY) &&
-                isOtherSessionActive(sessionId))
+                    config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_ONLY))
         {
-            return RESULT_NO_RESOURCES;
+            if (!deactivateOtherSessionIfActive(sessionId))
+            {
+                return RESULT_NO_RESOURCES;
+            }
+        }
+
+        if ((session->second)->IsGraphAlreadyExist(config) ||
+                (session->second)->getGraphSize(kStreamRtpTx) == 0)
+        {
+            return (session->second)->startGraph(config);
         }
         else
         {
-            if ((session->second)->IsGraphAlreadyExist(config) ||
-                    (session->second)->getGraphSize(kStreamRtpTx) == 0)
-            {
-                return (session->second)->startGraph(config);
-            }
-            else
-            {
-                return (session->second)->addGraph(config, false);
-            }
+            return (session->second)->addGraph(config, false);
         }
     }
     else
@@ -566,7 +566,7 @@
     }
 }
 
-bool AudioManager::isOtherSessionActive(const int sessionId)
+bool AudioManager::deactivateOtherSessionIfActive(const int sessionId)
 {
     for (auto const& session : mSessions)
     {
@@ -577,9 +577,15 @@
                     state == kSessionStateSending)
             {
                 IMLOGE1("[modifySession] Another session id[%d] is active", session.first);
-                return true;
+                if ((session.second)->deactivate())
+                {
+                    IMLOGI1("[modifySession] Moved session id[%d] to inactive", session.first);
+                    return true;
+                }
+                IMLOGE1("[modifySession] Failed to move session id[%d] to inactive", session.first);
+                return false;
             }
         }
     }
-    return false;
-}
+    return true;
+}
\ No newline at end of file
diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp
index dd763e3..c06ddc3 100644
--- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp
+++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSession.cpp
@@ -657,4 +657,34 @@
         default:
             break;
     }
+}
+
+bool AudioSession::deactivate()
+{
+    IMLOGI0("[deactivate]");
+
+    for (auto& graph : mListGraphRtpTx)
+    {
+        if (graph != nullptr)
+        {
+            graph->stop();
+        }
+    }
+
+    for (auto& graph : mListGraphRtpRx)
+    {
+        if (graph != nullptr)
+        {
+            graph->stop();
+        }
+    }
+
+    SessionState state = getState();
+    if (state == kSessionStateActive || state == kSessionStateReceiving ||
+            state == kSessionStateSending)
+    {
+        return false;
+    }
+
+    return true;
 }
\ No newline at end of file
diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h
index d0f9fce..17a1ebd 100644
--- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h
+++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h
@@ -71,7 +71,7 @@
     virtual void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold);
     virtual void SendInternalEvent(
             uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB);
-    virtual bool isOtherSessionActive(const int sessionId);
+    virtual bool deactivateOtherSessionIfActive(const int sessionId);
 
     static AudioManager* sManager;
     std::unordered_map<int, std::unique_ptr<AudioSession>> mSessions;
diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h
index ac29bfd..40e8313 100644
--- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h
+++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioSession.h
@@ -111,6 +111,13 @@
      */
     void sendRtpHeaderExtension(std::list<RtpHeaderExtension>* listExtension);
 
+    /**
+     * @brief Move session to inactive. i.e., Rtp Tx and Rtp Rx graphs are stopped
+     *
+     * @return bool false if failed to move session to inactive
+     */
+    bool deactivate();
+
 private:
     std::list<AudioStreamGraphRtpTx*> mListGraphRtpTx;
     std::list<AudioStreamGraphRtpRx*> mListGraphRtpRx;
diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSessionTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSessionTest.cpp
index bd4a601..524e721 100644
--- a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSessionTest.cpp
+++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioSessionTest.cpp
@@ -245,6 +245,14 @@
     EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
     EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
     EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateActive);
+
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
 }
 
 TEST_F(AudioSessionTest, testStartAndHoldResumeWithDifferentRemoteAddress)
@@ -334,4 +342,84 @@
     EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
     EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
     EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+}
+
+TEST_F(AudioSessionTest, testDeactivateActiveSession)
+{
+    session->setLocalEndPoint(socketRtpFd, socketRtcpFd);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateActive);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    EXPECT_EQ(session->deactivate(), true);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+    EXPECT_EQ(session->getState(), kSessionStateSuspended);
+}
+
+TEST_F(AudioSessionTest, testDeactivateSendonlySession)
+{
+    session->setLocalEndPoint(socketRtpFd, socketRtcpFd);
+    config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_SEND_ONLY);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateSending);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    EXPECT_EQ(session->deactivate(), true);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+    EXPECT_EQ(session->getState(), kSessionStateSuspended);
+}
+
+TEST_F(AudioSessionTest, testDeactivateReceiveonlySession)
+{
+    session->setLocalEndPoint(socketRtpFd, socketRtcpFd);
+    config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateReceiving);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    EXPECT_EQ(session->deactivate(), true);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+    EXPECT_EQ(session->getState(), kSessionStateSuspended);
+}
+
+TEST_F(AudioSessionTest, testDeactivateAndResumeSession)
+{
+    session->setLocalEndPoint(socketRtpFd, socketRtcpFd);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateActive);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    EXPECT_EQ(session->deactivate(), true);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+    EXPECT_EQ(session->getState(), kSessionStateSuspended);
+
+    config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_INACTIVE);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateSuspended);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
+
+    config.setMediaDirection(RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE);
+    EXPECT_EQ(session->startGraph(&config), RESULT_SUCCESS);
+    EXPECT_EQ(session->getState(), kSessionStateActive);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpTx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtpRx), 1);
+    EXPECT_EQ(session->getGraphSize(kStreamRtcp), 1);
 }
\ No newline at end of file