From 2763bbd97519d35a43aedf279751e7b1045581dc Mon Sep 17 00:00:00 2001
From: Rider Linden <rider@lindenlab.com>
Date: Fri, 4 Dec 2015 14:27:22 -0800
Subject: Initial changes for Vivox/Azumarill merge.  Lots of temporary code
 and conditional compile switches.   Begin switch from statemachine to
 coroutine.

---
 indra/llcommon/lleventcoro.cpp        |  10 +
 indra/llcommon/lleventcoro.h          |   5 +
 indra/llcommon/llevents.cpp           |  11 +
 indra/llcommon/llevents.h             |  12 +
 indra/llmessage/llexperiencecache.cpp |   5 +-
 indra/newview/lleventpoll.cpp         |   4 +-
 indra/newview/llfloaterperms.cpp      |   5 +-
 indra/newview/llviewerregion.cpp      |   2 +-
 indra/newview/llvoicevivox.cpp        | 740 ++++++++++++++++++++++++++++++++--
 indra/newview/llvoicevivox.h          |  17 +-
 10 files changed, 762 insertions(+), 49 deletions(-)

(limited to 'indra')

diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index c9bfcacedc..1c3fb4325d 100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -41,6 +41,8 @@
 #include "llerror.h"
 #include "llcoros.h"
 
+#include "lleventfilter.h"
+
 namespace
 {
 
@@ -153,6 +155,14 @@ void llcoro::suspend()
     suspendUntilEventOn("mainloop");
 }
 
+void llcoro::suspendUntilTimeout(float seconds)
+{
+    LLEventTimeout timeout;
+
+    timeout.eventAfter(seconds, LLSD());
+    llcoro::suspendUntilEventOn(timeout);
+}
+
 LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
                  const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath)
 {
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index 6fda3d2572..bcc8cdda1d 100755
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -108,6 +108,11 @@ VoidListener<LISTENER> voidlistener(const LISTENER& listener)
  */
 void suspend();
 
+/**
+ * Yield control from a coroutine for at least the specified number of seconds
+ */
+void suspendUntilTimeout(float seconds);
+
 /**
  * Post specified LLSD event on the specified LLEventPump, then suspend for a
  * response on specified other LLEventPump. This is more than mere
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 1c928b3db8..bb3a137815 100755
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -132,6 +132,17 @@ LLEventPump& LLEventPumps::obtain(const std::string& name)
     return *newInstance;
 }
 
+bool LLEventPumps::post(const std::string&name, const LLSD&message)
+{
+    PumpMap::iterator found = mPumpMap.find(name);
+
+    if (found == mPumpMap.end())
+        return false;
+
+    return (*found).second->post(message);
+}
+
+
 void LLEventPumps::flush()
 {
     // Flush every known LLEventPump instance. Leave it up to each instance to
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 0cbd1da32d..8d4fa68350 100755
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -217,6 +217,18 @@ public:
      * an instance without conferring @em ownership.
      */
     LLEventPump& obtain(const std::string& name);
+
+    /**
+     * Find the named LLEventPump instance. If it exists post the message to it.
+     * If the pump does not exist, do nothing.
+     * 
+     * returns the result of the LLEventPump::post. If no pump exists returns false.
+     * 
+     * This is syntactically similar to LLEventPumps::instance().post(name, message),
+     * however if the pump does not already exist it will not be created.
+     */
+    bool post(const std::string&, const LLSD&);
+
     /**
      * Flush all known LLEventPump instances
      */
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index 92fcd38d3b..779d1d9d99 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -396,12 +396,9 @@ void LLExperienceCache::idleCoro()
     const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds
 
     LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL;
-    LLEventTimeout timeout;
-
     do 
     {
-        timeout.eventAfter(SECS_BETWEEN_REQUESTS, LLSD());
-        llcoro::suspendUntilEventOn(timeout);
+        llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS);
 
         if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT))
         {
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
index 72e159bcec..7178042b32 100755
--- a/indra/newview/lleventpoll.cpp
+++ b/indra/newview/lleventpoll.cpp
@@ -197,7 +197,6 @@ namespace Details
                     // request. Calculate a timeout and wait for it to expire(sleep)
                     // before trying again.  The sleep time is increased by 5 seconds
                     // for each consecutive error.
-                    LLEventTimeout timeout;
                     ++errorCount;
 
                     F32 waitToRetry = EVENT_POLL_ERROR_RETRY_SECONDS
@@ -206,8 +205,7 @@ namespace Details
                     LL_WARNS("LLEventPollImpl") << "<" << counter << "> Retrying in " << waitToRetry <<
                         " seconds, error count is now " << errorCount << LL_ENDL;
 
-                    timeout.eventAfter(waitToRetry, LLSD());
-                    llcoro::suspendUntilEventOn(timeout);
+                    llcoro::suspendUntilTimeout(waitToRetry);
                     
                     if (mDone)
                         break;
diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp
index cb67787af3..b0b2770c6e 100755
--- a/indra/newview/llfloaterperms.cpp
+++ b/indra/newview/llfloaterperms.cpp
@@ -232,8 +232,6 @@ void LLFloaterPermsDefault::updateCapCoro(std::string url)
 
         if (!status)
         {
-            LLEventTimeout timeout;
-
             const std::string& reason = status.toString();
             // Do not display the same error more than once in a row
             if (reason != previousReason)
@@ -244,8 +242,7 @@ void LLFloaterPermsDefault::updateCapCoro(std::string url)
                 LLNotificationsUtil::add("DefaultObjectPermissions", args);
             }
 
-            timeout.eventAfter(RETRY_TIMEOUT, LLSD());
-            llcoro::suspendUntilEventOn(timeout);
+            llcoro::suspendUntilTimeout(RETRY_TIMEOUT);
             if (retryCount < MAX_HTTP_RETRIES)
                 continue;
 
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index b0280ef3e0..a4109d5885 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -310,7 +310,7 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle)
                 << "Capability '" << iter->first << "' is '" << iter->second << "'" << LL_ENDL;
         }
 
-#if 0
+#if 1
         log_capabilities(mCapabilities);
 #endif
 
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index c6c7d588eb..66d23f7919 100755
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -66,6 +66,7 @@
 #include "llnotificationsutil.h"
 
 #include "llcorehttputil.h"
+#include "lleventfilter.h"
 
 #include "stringize.h"
 
@@ -377,7 +378,9 @@ void LLVivoxVoiceClient::connectorCreate()
 	std::string loglevel = "0";
 	
 	// Transition to stateConnectorStarted when the connector handle comes back.
+#if 0
 	setState(stateConnectorStarting);
+#endif
 
 	std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
 		
@@ -435,6 +438,7 @@ void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID
 	mAccountName = nameFromID(agentID);
 }
 
+#if 0
 void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
 {
 	LLViewerRegion *region = gAgent.getRegion();
@@ -453,7 +457,7 @@ void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
 		{
             LLCoros::instance().launch("LLVivoxVoiceClient::voiceAccountProvisionCoro",
                 boost::bind(&LLVivoxVoiceClient::voiceAccountProvisionCoro, this, url, retries));
-			setState(stateConnectorStart);		
+//			setState(stateConnectorStart);		
 		}
 	}
 }
@@ -465,20 +469,39 @@ void LLVivoxVoiceClient::voiceAccountProvisionCoro(std::string url, S32 retries)
         httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
     LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
     LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+    int retryCount(0);
 
-    httpOpts->setRetries(retries);
 
-    LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
+    LLSD result;
+        
+    do 
+    {
+        result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
 
-    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
-    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+        LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+        LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
 
-    if (!status)
-    {
-        LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
-        giveUp();
-        return;
-    }
+        if (status == LLCore::HttpStatus(404))
+        {
+            if (++retryCount > retries)
+            {
+                LL_WARNS("Voice") << "Could not access voice provision cap after " << retries << " attempts." << LL_ENDL;
+                giveUp();
+                return;
+            }
+            LL_WARNS("Voice") << "Provision CAP 404.  Retrying in 1.0" << LL_ENDL;
+            llcoro::suspendUntilTimeout(1.0);
+
+            continue;
+        }
+        else if (!status)
+        {
+            LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
+            giveUp();
+            return;
+        }
+        break;
+    } while (true);
 
     std::string voice_sip_uri_hostname;
     std::string voice_account_server_uri;
@@ -492,11 +515,12 @@ void LLVivoxVoiceClient::voiceAccountProvisionCoro(std::string url, S32 retries)
     if (result.has("voice_account_server_name"))
         voice_account_server_uri = result["voice_account_server_name"].asString();
 
-    login(result["username"].asString(), result["password"].asString(),
+    setLoginInfo(result["username"].asString(), result["password"].asString(),
         voice_sip_uri_hostname, voice_account_server_uri);
 }
+#endif
 
-void LLVivoxVoiceClient::login(
+void LLVivoxVoiceClient::setLoginInfo(
 	const std::string& account_name,
 	const std::string& password,
 	const std::string& voice_sip_uri_hostname,
@@ -701,10 +725,24 @@ void LLVivoxVoiceClient::stateMachine()
 		case stateDisabled:
 			if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty()))
 			{
+#if 1
+                LLCoros::instance().launch("LLVivoxVoiceClient::startAndConnectSession",
+                    boost::bind(&LLVivoxVoiceClient::startAndConnectSession, this));
+#else
 				setState(stateStart);
+#endif
 			}
 		break;
-		
+
+//--------------------------------------------------------------------------
+#if 1
+        case stateStart:
+        case stateDaemonLaunched:
+        case stateConnecting:
+        case stateConnected:
+            // moved to coroutine LLVivoxVoiceClient::startAndLaunchDaemon
+            break;
+#else
 		//MARK: stateStart
 		case stateStart:
 			if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
@@ -861,30 +899,47 @@ void LLVivoxVoiceClient::stateMachine()
 
 			setState(stateIdle);
 		break;
+#endif
+//--------------------------------------------------------------------------
 
 		//MARK: stateIdle
 		case stateIdle:
 			// This is the idle state where we're connected to the daemon but haven't set up a connector yet.
 			if(mTuningMode)
 			{
-				mTuningExitState = stateIdle;
-				setState(stateMicTuningStart);
-			}
+#if 1
+                LLCoros::instance().launch("LLVivoxVoiceClient::performMicTuning",
+                    boost::bind(&LLVivoxVoiceClient::performMicTuning, this, stateIdle));
+#else
+                mTuningExitState = stateIdle;
+                setState(stateMicTuningStart);
+#endif
+            }
 			else if(!mVoiceEnabled && mIsInitialized)
 			{
 				// We never started up the connector.  This will shut down the daemon.
 				setState(stateConnectorStopped);
 			}
+#if 0
 			else if(!mAccountName.empty())
 			{
 				if ( mAccountPassword.empty() )
 				{
-					requestVoiceAccountProvision();
+					requestVoiceAccountProvision(5);
 				}
 			}
+#endif
 		break;
 
-		//MARK: stateMicTuningStart
+//--------------------------------------------------------------------------
+#if 1
+        case stateMicTuningStart:
+        case stateMicTuningRunning:
+        case stateMicTuningStop:
+            // moved to coroutine LLVivoxVoiceClient::performMicTuning
+            break;
+#else
+        //MARK: stateMicTuningStart
 		case stateMicTuningStart:
 			if(mUpdateTimer.hasExpired())
 			{
@@ -985,6 +1040,8 @@ void LLVivoxVoiceClient::stateMachine()
 			
 		}
 		break;
+#endif
+//--------------------------------------------------------------------------
 
 		//MARK: stateCaptureBufferPaused
 		case stateCaptureBufferPaused:
@@ -1075,7 +1132,16 @@ void LLVivoxVoiceClient::stateMachine()
 			}
 		break;
 
-			//MARK: stateConnectorStart
+//-------------------------------------------------------------------------
+#if 1
+        case stateConnectorStart:
+        case stateConnectorStarting:	
+        case stateConnectorStarted:	
+            // moved to establishVoiceConnection
+            break;
+
+#else
+		//MARK: stateConnectorStart
 		case stateConnectorStart:
 			if(!mVoiceEnabled && mIsInitialized)
 			{
@@ -1106,7 +1172,18 @@ void LLVivoxVoiceClient::stateMachine()
 				setState(stateNeedsLogin);
 			}
 		break;
-				
+#endif
+//-------------------------------------------------------------------------
+
+#if 1
+        case stateLoginRetry:
+        case stateLoginRetryWait:
+        case stateNeedsLogin:
+        case stateLoggingIn:
+        case stateLoggedIn:
+            // moved to loginToVivox
+            break;
+#else
 		//MARK: stateLoginRetry
 		case stateLoginRetry:
 			if(mLoginRetryCount == 0)
@@ -1194,7 +1271,14 @@ void LLVivoxVoiceClient::stateMachine()
 			sendLocalAudioUpdates();
 				
 		break;
+#endif
 
+#if 1
+        case stateVoiceFontsWait:		// Await voice font list
+        case stateVoiceFontsReceived:	// Voice font list received
+            // moved to retrieveVoiceFonts
+            break;
+#else
 		//MARK: stateVoiceFontsWait
 		case stateVoiceFontsWait:		// Await voice font list
 			// accountGetSessionFontsResponse() will transition from here to
@@ -1207,15 +1291,17 @@ void LLVivoxVoiceClient::stateMachine()
 			// Set up the timer to check for expiring voice fonts
 			mVoiceFontExpiryTimer.start();
 			mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
-
 #if USE_SESSION_GROUPS			
-			// create the main session group
-			setState(stateCreatingSessionGroup);
-			sessionGroupCreateSendMessage();
+            // create the main session group
+            setState(stateCreatingSessionGroup);
+            sessionGroupCreateSendMessage();
 #else
-			setState(stateNoChannel);				
+            setState(stateNoChannel);				
 #endif
-		break;
+            break;
+
+#endif
+
 		
 		//MARK: stateCreatingSessionGroup
 		case stateCreatingSessionGroup:
@@ -1260,8 +1346,13 @@ void LLVivoxVoiceClient::stateMachine()
 			}
 			else if(mTuningMode)
 			{
-				mTuningExitState = stateNoChannel;
-				setState(stateMicTuningStart);
+#if 1
+                LLCoros::instance().launch("LLVivoxVoiceClient::performMicTuning",
+                    boost::bind(&LLVivoxVoiceClient::performMicTuning, this, stateNoChannel));
+#else
+                mTuningExitState = stateNoChannel;
+                setState(stateMicTuningStart);
+#endif
 			}
 			else if(mCaptureBufferMode)
 			{
@@ -1528,7 +1619,8 @@ void LLVivoxVoiceClient::stateMachine()
 			}
 		break;
 		
-		//MARK: stateConnectorStopping
+//-------------------------------------------------------------------------
+        //MARK: stateConnectorStopping
 		case stateConnectorStopping:	// waiting for connector stop
 			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
 			mShutdownComplete = true;
@@ -1539,7 +1631,8 @@ void LLVivoxVoiceClient::stateMachine()
 			setState(stateDisableCleanup);
 		break;
 
-		//MARK: stateConnectorFailed
+//-------------------------------------------------------------------------
+        //MARK: stateConnectorFailed
 		case stateConnectorFailed:
 			setState(stateConnectorFailedWaiting);
 		break;
@@ -1550,6 +1643,7 @@ void LLVivoxVoiceClient::stateMachine()
 				setState(stateDisableCleanup);
 			}
 		break;
+//-------------------------------------------------------------------------
 
 		//MARK: stateLoginFailed
 		case stateLoginFailed:
@@ -1609,6 +1703,537 @@ void LLVivoxVoiceClient::stateMachine()
 	}
 }
 
+//=========================================================================
+// the following are methods to support the coroutine implementation of the 
+// voice connection and processing.  They should only be called in the context 
+// of a coroutine.
+// 
+// calls to setState() in these are historical and used because some of the other
+// query routines will ask what state the state machine is in.
+// 
+
+bool LLVivoxVoiceClient::startAndConnectSession()
+{
+    if (!startAndLaunchDaemon())
+    {
+        setState(stateJail);
+        return false;
+    }
+
+    if (!provisionVoiceAccount())
+    {
+        giveUp();
+        return false;
+    }
+
+    if (!establishVoiceConnection())
+    {
+        if (getState() != stateConnectorFailed)
+        {
+            setState(stateLoggedOut);
+        }
+        giveUp();
+        return false;
+    }
+
+
+    if (!loginToVivox())
+    {
+        setState(stateLoginFailed);
+        return false;
+    }
+
+    if (LLVoiceClient::instance().getVoiceEffectEnabled())
+    {
+        retrieveVoiceFonts();
+
+        // Request the set of available voice fonts.
+        refreshVoiceEffectLists(true);
+    }
+    else
+    {
+        // If voice effects are disabled, pretend we've received them and carry on.
+        setState(stateNoChannel);
+    }
+
+#if USE_SESSION_GROUPS			
+    // create the main session group
+    setState(stateCreatingSessionGroup);
+    sessionGroupCreateSendMessage();
+#else
+    setState(stateNoChannel);
+#endif
+
+    return true;
+}
+
+bool LLVivoxVoiceClient::startAndLaunchDaemon()
+{
+    //---------------------------------------------------------------------
+    setState(stateStart);
+
+    if (gSavedSettings.getBOOL("CmdLineDisableVoice"))
+    {
+        // Voice is locked out, we must not launch the vivox daemon.
+        setState(stateJail);
+        return false;
+    }
+
+    if (!isGatewayRunning() && gSavedSettings.getBOOL("EnableVoiceChat"))
+    {
+#ifndef VIVOXDAEMON_REMOTEHOST
+        // Launch the voice daemon
+
+        // *FIX:Mani - Using the executable dir instead 
+        // of mAppRODataDir, the working directory from which the app
+        // is launched.
+        //std::string exe_path = gDirUtilp->getAppRODataDir();
+        std::string exe_path = gDirUtilp->getExecutableDir();
+        exe_path += gDirUtilp->getDirDelimiter();
+#if LL_WINDOWS
+        exe_path += "SLVoice.exe";
+#elif LL_DARWIN
+        exe_path += "../Resources/SLVoice";
+#else
+        exe_path += "SLVoice";
+#endif
+        // See if the vivox executable exists
+        llstat s;
+        if (!LLFile::stat(exe_path, &s))
+        {
+            // vivox executable exists.  Build the command line and launch the daemon.
+            LLProcess::Params params;
+            params.executable = exe_path;
+
+            std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
+            std::string shutdown_timeout = gSavedSettings.getString("VivoxShutdownTimeout");
+            if (loglevel.empty())
+            {
+                loglevel = "-1";	// turn logging off completely, was 0 for error level logging.
+            }
+
+            params.args.add("-ll");
+            params.args.add(loglevel);
+
+            std::string log_folder = gSavedSettings.getString("VivoxLogDirectory");
+
+            if (log_folder.empty())
+            {
+                log_folder = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
+            }
+
+            params.args.add("-lf");
+            params.args.add(log_folder);
+
+            if (!shutdown_timeout.empty())
+            {
+                params.args.add("-st");
+                params.args.add(shutdown_timeout);
+            }
+            params.cwd = gDirUtilp->getAppRODataDir();
+            sGatewayPtr = LLProcess::create(params);
+
+            mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort"));
+        }
+        else
+        {
+            LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
+            return false;
+        }
+#else
+        // SLIM SDK: port changed from 44124 to 44125.
+        // We can connect to a client gateway running on another host.  This is useful for testing.
+        // To do this, launch the gateway on a nearby host like this:
+        //  vivox-gw.exe -p tcp -i 0.0.0.0:44125
+        // and put that host's IP address here.
+        mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort"));
+#endif
+
+        mUpdateTimer.start();
+        mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
+
+        // Dirty the states we'll need to sync with the daemon when it comes up.
+        mMuteMicDirty = true;
+        mMicVolumeDirty = true;
+        mSpeakerVolumeDirty = true;
+        mSpeakerMuteDirty = true;
+        // These only need to be set if they're not default (i.e. empty string).
+        mCaptureDeviceDirty = !mCaptureDevice.empty();
+        mRenderDeviceDirty = !mRenderDevice.empty();
+
+        mMainSessionGroupHandle.clear();
+    }
+
+    //---------------------------------------------------------------------
+    llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+    setState(stateDaemonLaunched);
+
+    LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL;
+
+    int connectAttempt = 0;
+
+    while (!mConnected)
+    {
+        ++connectAttempt;
+        LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << " (#" << connectAttempt << ")" << LL_ENDL;
+        closeSocket();
+        if (!mSocket)
+        {
+            mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);
+        }
+
+        mConnected = mSocket->blockingConnect(mDaemonHost);
+    }
+
+    //---------------------------------------------------------------------
+    llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+    setState(stateConnecting);
+
+
+    while (!mPump)
+    {   // Can't do this until we have the pump available.
+        llcoro::suspend();
+    }
+
+    
+    // MBW -- Note to self: pumps and pipes examples in
+    //  indra/test/io.cpp
+    //  indra/test/llpipeutil.{cpp|h}
+
+    // Attach the pumps and pipes
+
+    LLPumpIO::chain_t readChain;
+
+    readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
+    readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
+
+    mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
+
+    //---------------------------------------------------------------------
+    llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+    setState(stateConnected);
+
+    // Initial devices query
+    getCaptureDevicesSendMessage();
+    getRenderDevicesSendMessage();
+
+    mLoginRetryCount = 0;
+
+    setState(stateIdle);
+
+    return true;
+}
+
+bool LLVivoxVoiceClient::provisionVoiceAccount()
+{
+
+    while (!gAgent.getRegion())
+    {
+        // *TODO* Set up a call back on agent that sends a message to a pump we can use to wake up.
+        llcoro::suspend();
+    }
+
+    LLViewerRegion *region = gAgent.getRegion();
+
+    while (!region->capabilitiesReceived())
+    {
+        // *TODO* Pump a message for wake up.
+        llcoro::suspend();
+    }
+
+    std::string url = region->getCapability("ProvisionVoiceAccountRequest");
+
+    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("voiceAccountProvision", httpPolicy));
+    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+    LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
+    int retryCount(0);
+
+    LLSD result;
+
+    do
+    {
+        result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
+
+        LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+        LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+        if (status == LLCore::HttpStatus(404))
+        {
+            if (++retryCount > 5)
+            {
+                LL_WARNS("Voice") << "Could not access voice provision cap after 5 attempts." << LL_ENDL;
+                return false;
+            }
+            LL_WARNS("Voice") << "Provision CAP 404.  Retrying in 1.0" << LL_ENDL;
+            llcoro::suspendUntilTimeout(1.0);
+
+            continue;
+        }
+        else if (!status)
+        {
+            LL_WARNS("Voice") << "Unable to provision voice account." << LL_ENDL;
+            return false;
+        }
+        break;
+    } while (true);
+
+    std::string voiceSipUriHostname;
+    std::string voiceAccountServerUri;
+    std::string voiceUserName = result["username"].asString();
+    std::string voicePassword = result["password"].asString();
+
+    //LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << dumpResponse() << LL_ENDL;
+
+    if (result.has("voice_sip_uri_hostname"))
+        voiceSipUriHostname = result["voice_sip_uri_hostname"].asString();
+
+    // this key is actually misnamed -- it will be an entire URI, not just a hostname.
+    if (result.has("voice_account_server_name"))
+        voiceAccountServerUri = result["voice_account_server_name"].asString();
+
+    setLoginInfo(voiceUserName, voicePassword, voiceSipUriHostname, voiceAccountServerUri);
+
+    return true;
+}
+
+
+bool LLVivoxVoiceClient::establishVoiceConnection()
+{
+    LLEventPump &voiceConnectPump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+    if (!mVoiceEnabled && mIsInitialized)
+        return false;
+
+    setState(stateConnectorStart);
+
+    connectorCreate();
+
+    setState(stateConnectorStarting);
+
+    LLSD result;
+    do
+    {
+        result = llcoro::suspendUntilEventOn(voiceConnectPump);
+    } 
+    while (!result.has("connector"));
+
+    if (!result["connector"])
+    {
+
+        setState(stateConnectorFailed);
+        return false;
+    }
+
+    setState(stateConnectorStarted);
+    if (!mVoiceEnabled && mIsInitialized)
+        return false;
+
+    return true;
+}
+
+bool LLVivoxVoiceClient::loginToVivox()
+{
+    int loginRetryCount(0);
+    LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+    do 
+    {
+        setState(stateLoggingIn);
+        loginSendMessage();
+
+        LLSD result;
+        do
+        {
+            result = llcoro::suspendUntilEventOn(voicePump);
+        } while (!result.has("login"));
+
+        if (result["login"])
+            break;
+
+        if (!loginRetryCount)
+        {   // on first retry notify user
+            notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
+        }
+
+        if ((++loginRetryCount > MAX_LOGIN_RETRIES) || (!result["login_retry"]))        
+        {
+            LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
+            LLSD args;
+            std::stringstream errs;
+            errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
+            args["HOSTID"] = errs.str();
+            mTerminateDaemon = true;
+            if (LLGridManager::getInstance()->isSystemGrid())
+            {
+                LLNotificationsUtil::add("NoVoiceConnect", args);
+            }
+            else
+            {
+                LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);
+            }
+
+            setState(stateLoginFailed);
+            return false;
+        }
+
+        LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
+        setState(stateLoginRetryWait);
+        llcoro::suspendUntilTimeout(LOGIN_RETRY_SECONDS);
+    } while (true);
+
+    setState(stateLoggedIn);
+    notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
+
+
+    // Set up the mute list observer if it hasn't been set up already.
+    if ((!sMuteListListener_listening))
+    {
+        LLMuteList::getInstance()->addObserver(&mutelist_listener);
+        sMuteListListener_listening = true;
+    }
+
+    // Set the initial state of mic mute, local speaker volume, etc.
+    sendLocalAudioUpdates();
+
+    return true;
+}
+
+bool LLVivoxVoiceClient::retrieveVoiceFonts()
+{
+    LLEventPump &voicePump = LLEventPumps::instance().obtain("vivoxClientPump");
+
+    // Request the set of available voice fonts.
+    setState(stateVoiceFontsWait);
+    refreshVoiceEffectLists(true);
+
+    LLSD result;
+    do 
+    {
+        result = llcoro::suspendUntilEventOn(voicePump);
+
+        if (result.has("voice_fonts"))
+            break;
+    } while (true);
+
+
+    mVoiceFontExpiryTimer.start();
+    mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL);
+
+    setState(stateNoChannel);
+
+    return result["voice_fonts"].asBoolean();
+}
+
+bool LLVivoxVoiceClient::performMicTuning(LLVivoxVoiceClient::state exitState)
+{
+    //---------------------------------------------------------------------
+    setState(stateMicTuningStart);
+
+    while (!mUpdateTimer.hasExpired())
+    {   // do not start mic tuning before the update timer has expired.
+        llcoro::suspend();
+    }
+
+    while (mTuningMode)
+    {
+
+        if (mCaptureDeviceDirty || mRenderDeviceDirty)
+        {
+            // These can't be changed while in tuning mode.  Set them before starting.
+            std::ostringstream stream;
+
+            buildSetCaptureDevice(stream);
+            buildSetRenderDevice(stream);
+
+            if (!stream.str().empty())
+            {
+                writeString(stream.str());
+            }
+
+            llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+        }
+
+        // loop mic back to render device.
+        //setMuteMic(0);						// make sure the mic is not muted
+        std::ostringstream stream;
+
+        stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
+            << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
+            << "<Value>false</Value>"
+            << "</Request>\n\n\n";
+
+        // Dirty the mute mic state so that it will get reset when we finishing previewing
+        mMuteMicDirty = true;
+        mTuningSpeakerVolumeDirty = true;
+
+        writeString(stream.str());
+        tuningCaptureStartSendMessage(1);  // 1-loop, zero, don't loop
+
+        //---------------------------------------------------------------------
+        setState(stateMicTuningRunning);
+        llcoro::suspend();
+
+        while (mTuningMode && !mCaptureDeviceDirty && !mRenderDeviceDirty)
+        {
+            // process mic/speaker volume changes
+            if (mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
+            {
+                std::ostringstream stream;
+
+                if (mTuningMicVolumeDirty)
+                {
+                    LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
+                    stream
+                        << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
+                        << "<Level>" << mTuningMicVolume << "</Level>"
+                        << "</Request>\n\n\n";
+                }
+
+                if (mTuningSpeakerVolumeDirty)
+                {
+                    stream
+                        << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
+                        << "<Level>" << mTuningSpeakerVolume << "</Level>"
+                        << "</Request>\n\n\n";
+                }
+
+                mTuningMicVolumeDirty = false;
+                mTuningSpeakerVolumeDirty = false;
+
+                if (!stream.str().empty())
+                {
+                    writeString(stream.str());
+                }
+            }
+            llcoro::suspend();
+        }
+
+        //---------------------------------------------------------------------
+        setState(stateMicTuningStop);
+
+        // transition out of mic tuning
+        tuningCaptureStopSendMessage();
+        if (mCaptureDeviceDirty || mRenderDeviceDirty)
+        {
+            llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS);
+        }
+    }
+
+    setState(mTuningExitState);
+
+    // if we exited just to change devices, this will keep us from re-entering too fast.
+    mUpdateTimer.start();
+    mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+
+    //---------------------------------------------------------------------
+    setState(exitState);
+    return true;
+}
+
+//=========================================================================
+
 void LLVivoxVoiceClient::closeSocket(void)
 {
 	mSocket.reset();
@@ -2645,10 +3270,16 @@ void LLVivoxVoiceClient::sendLocalAudioUpdates()
 
 void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
 {	
+#if 1
+    LLSD result = LLSD::emptyMap();
+#endif
+
 	if(statusCode != 0)
 	{
 		LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
+#if 0
 		setState(stateConnectorFailed);
+#endif
 		LLSD args;
 		std::stringstream errs;
 		errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000";
@@ -2662,6 +3293,10 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
 		{
 			LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);	
 		}
+
+#if 1
+        result["connector"] = LLSD::Boolean(false);
+#endif
 	}
 	else
 	{
@@ -2670,16 +3305,28 @@ void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &st
 		mVoiceVersion.serverVersion = versionID;
 		mConnectorHandle = connectorHandle;
 		mTerminateDaemon = false;
+#if 1
+        result["connector"] = LLSD::Boolean(true);
+#else
 		if(getState() == stateConnectorStarting)
 		{
 			setState(stateConnectorStarted);
 		}
+#endif
 	}
+
+#if 1
+    LLEventPumps::instance().post("vivoxClientPump", result);
+#endif
 }
 
 void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
 { 
-	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
+#if 1
+    LLSD result = LLSD::emptyMap();
+#endif
+
+    LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
 	
 	// Status code of 20200 means "bad password".  We may want to special-case that at some point.
 	
@@ -2687,24 +3334,40 @@ void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString
 	{
 		// Login failure which is probably caused by the delay after a user's password being updated.
 		LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
+#if 1
+        result["login"] = LLSD::Boolean(false);
+        result["login_retry"] = LLSD::Boolean(true);
+#else
 		setState(stateLoginRetry);
+#endif
 	}
 	else if(statusCode != 0)
 	{
 		LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
-		setState(stateLoginFailed);
+#if 1
+        result["login"] = LLSD::Boolean(false);
+        result["login_retry"] = LLSD::Boolean(false);
+#else
+        setState(stateLoginFailed);
+#endif
 	}
 	else
 	{
 		// Login succeeded, move forward.
 		mAccountHandle = accountHandle;
 		mNumberOfAliases = numberOfAliases;
+        result["login"] = LLSD::Boolean(true);
 		// This needs to wait until the AccountLoginStateChangeEvent is received.
 //		if(getState() == stateLoggingIn)
 //		{
 //			setState(stateLoggedIn);
 //		}
 	}
+
+#if 1
+    LLEventPumps::instance().post("vivoxClientPump", result);
+#endif
+
 }
 
 void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
@@ -2910,7 +3573,7 @@ void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
 {
 	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
 	
-#if USE_SESSION_GROUPS
+#if  USE_SESSION_GROUPS
 	if(mMainSessionGroupHandle.empty())
 	{
 		// This is the first (i.e. "main") session group.  Save its handle.
@@ -5970,11 +6633,20 @@ void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session)
 
 void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString)
 {
+#if 1
+    LLSD result = LLSD::emptyMap();
+
+    result["voice_fonts"] = LLSD::Boolean(true);
+
+    LLEventPumps::instance().post("vivoxClientPump", result);
+
+#else
 	// Voice font list entries were updated via addVoiceFont() during parsing.
 	if(getState() == stateVoiceFontsWait)
 	{
 		setState(stateVoiceFontsReceived);
 	}
+#endif
 
 	notifyVoiceFontObservers();
 	mVoiceFontsReceived = true;
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index 2f671306b1..5c9a1b73a3 100755
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -433,8 +433,8 @@ protected:
 	void connectorShutdown();	
 	void closeSocket(void);	
 	
-	void requestVoiceAccountProvision(S32 retries = 3);
-	void login(
+//	void requestVoiceAccountProvision(S32 retries = 3);
+	void setLoginInfo(
 			   const std::string& account_name,
 			   const std::string& password,
 			   const std::string& voice_sip_uri_hostname,
@@ -639,11 +639,22 @@ protected:
 
 private:
     
-    void voiceAccountProvisionCoro(std::string url, S32 retries);
+//  void voiceAccountProvisionCoro(std::string url, S32 retries);
     void parcelVoiceInfoRequestCoro(std::string url);
 
 	LLVoiceVersionInfo mVoiceVersion;
 
+    // Coroutine support methods
+    bool startAndConnectSession();
+
+    bool startAndLaunchDaemon();
+    bool provisionVoiceAccount();
+    bool establishVoiceConnection();
+    bool loginToVivox();
+    bool retrieveVoiceFonts();
+
+    bool performMicTuning(state exitState);
+
 	/// Clean up objects created during a voice session.
 	void cleanUp();
 
-- 
cgit v1.2.3