Merge "Camera2: ZSL: Do regular capture if AE is not satisfied." into jb-mr1-dev
diff --git a/include/media/IRemoteDisplayClient.h b/include/media/IRemoteDisplayClient.h
index 553ad36..252b401 100644
--- a/include/media/IRemoteDisplayClient.h
+++ b/include/media/IRemoteDisplayClient.h
@@ -59,8 +59,6 @@
 
     // Indicates that a connection could not be established to the remote display
     // or an unrecoverable error occurred and the connection was severed.
-    // The media server should continue listening for connection attempts from the
-    // remote display.
     virtual void onDisplayError(int32_t error) = 0; // one-way
 };
 
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index f3f2d1e..2eae7df 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -1202,7 +1202,7 @@
 //----------------------------------------------------------------------------
 // EqualizerLimitBandLevels()
 //----------------------------------------------------------------------------
-// Purpose: limit all EQ band gains to a value less than MAX_BAND_GAIN_DB while
+// Purpose: limit all EQ band gains to a value less than 0 dB while
 //          preserving the relative band levels.
 //
 // Inputs:
@@ -1214,7 +1214,6 @@
 void EqualizerLimitBandLevels(EffectContext *pContext) {
     LVM_ControlParams_t     ActiveParams;              /* Current control Parameters */
     LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;     /* Function call status */
-    LVM_EQNB_BandDef_t      *BandDef;
 
     /* Get the current settings */
     LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
@@ -1223,28 +1222,78 @@
     //ALOGV("\tEqualizerLimitBandLevels just Got -> %d\n",
     //          ActiveParams.pEQNB_BandDefinition[band].Gain);
 
-    int gainCorrection = 0;
+    // Apply a volume correction to avoid clipping in the EQ based on 2 factors:
+    // - the maximum EQ band gain: the volume correction is such that the total of volume + max
+    // band gain is <= 0 dB
+    // - the average gain in all bands weighted by their proximity to max gain band.
+    int maxGain = 0;
+    int avgGain = 0;
+    int avgCount = 0;
     for (int i = 0; i < FIVEBAND_NUMBANDS; i++) {
-        int level = pContext->pBundledContext->bandGaindB[i] + ActiveParams.VC_EffectLevel;
-        if (level > MAX_BAND_GAIN_DB) {
-            int correction = MAX_BAND_GAIN_DB -level;
-            if (correction < gainCorrection) {
-                gainCorrection = correction;
+        if (pContext->pBundledContext->bandGaindB[i] >= maxGain) {
+            int tmpMaxGain = pContext->pBundledContext->bandGaindB[i];
+            int tmpAvgGain = 0;
+            int tmpAvgCount = 0;
+            for (int j = 0; j < FIVEBAND_NUMBANDS; j++) {
+                int gain = pContext->pBundledContext->bandGaindB[j];
+                // skip current band and gains < 0 dB
+                if (j == i || gain < 0)
+                    continue;
+                // no need to continue if one band not processed yet has a higher gain than current
+                // max
+                if (gain > tmpMaxGain) {
+                    // force skipping "if (tmpAvgGain >= avgGain)" below as tmpAvgGain is not
+                    // meaningful in this case
+                    tmpAvgGain = -1;
+                    break;
+                }
+
+                int weight = 1;
+                if (j < (i + 2) && j > (i - 2))
+                    weight = 4;
+                tmpAvgGain += weight * gain;
+                tmpAvgCount += weight;
+            }
+            if (tmpAvgGain >= avgGain) {
+                maxGain = tmpMaxGain;
+                avgGain = tmpAvgGain;
+                avgCount = tmpAvgCount;
             }
         }
+        ActiveParams.pEQNB_BandDefinition[i].Frequency = EQNB_5BandPresetsFrequencies[i];
+        ActiveParams.pEQNB_BandDefinition[i].QFactor   = EQNB_5BandPresetsQFactors[i];
+        ActiveParams.pEQNB_BandDefinition[i].Gain = pContext->pBundledContext->bandGaindB[i];
     }
 
-    /* Set local EQ parameters */
-    BandDef = ActiveParams.pEQNB_BandDefinition;
-    for (int i=0; i < FIVEBAND_NUMBANDS; i++) {
-        ActiveParams.pEQNB_BandDefinition[i].Gain = pContext->pBundledContext->bandGaindB[i] +
-                gainCorrection;
+    int gainCorrection = 0;
+    if (maxGain + pContext->pBundledContext->volume > 0) {
+        gainCorrection = maxGain + pContext->pBundledContext->volume;
     }
+    if (avgCount) {
+        gainCorrection += avgGain/avgCount;
+    }
+
+    ALOGV("EqualizerLimitBandLevels() gainCorrection %d maxGain %d avgGain %d avgCount %d",
+            gainCorrection, maxGain, avgGain, avgCount);
+
+    ActiveParams.VC_EffectLevel  = pContext->pBundledContext->volume - gainCorrection;
+    if (ActiveParams.VC_EffectLevel < -96) {
+        ActiveParams.VC_EffectLevel = -96;
+    }
+
     /* Activate the initial settings */
     LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
     LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "EqualizerLimitBandLevels")
     //ALOGV("\tEqualizerLimitBandLevels just Set -> %d\n",
     //          ActiveParams.pEQNB_BandDefinition[band].Gain);
+
+    //ALOGV("\tEqualizerLimitBandLevels just set (-96dB -> 0dB)   -> %d\n",ActiveParams.VC_EffectLevel );
+    if(pContext->pBundledContext->firstVolume == LVM_TRUE){
+        LvmStatus = LVM_SetVolumeNoSmoothing(pContext->pBundledContext->hInstance, &ActiveParams);
+        LVM_ERROR_CHECK(LvmStatus, "LVM_SetVolumeNoSmoothing", "LvmBundle_process")
+        ALOGV("\tLVM_VOLUME: Disabling Smoothing for first volume change to remove spikes/clicks");
+        pContext->pBundledContext->firstVolume = LVM_FALSE;
+    }
 }
 
 
@@ -1418,25 +1467,12 @@
     //ALOGV("\tEqualizerSetPreset(%d)", preset);
     pContext->pBundledContext->CurPreset = preset;
 
-    LVM_ControlParams_t     ActiveParams;              /* Current control Parameters */
-    LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;     /* Function call status */
-
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "EqualizerSetPreset")
-    //ALOGV("\tEqualizerSetPreset Succesfully returned from LVM_GetControlParameters\n");
-
     //ActiveParams.pEQNB_BandDefinition = &BandDefs[0];
     for (int i=0; i<FIVEBAND_NUMBANDS; i++)
     {
-        ActiveParams.pEQNB_BandDefinition[i].Frequency = EQNB_5BandPresetsFrequencies[i];
-        ActiveParams.pEQNB_BandDefinition[i].QFactor   = EQNB_5BandPresetsQFactors[i];
         pContext->pBundledContext->bandGaindB[i] =
                 EQNB_5BandSoftPresets[i + preset * FIVEBAND_NUMBANDS];
     }
-    /* Activate the new settings */
-    LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "EqualizerSetPreset")
 
     EqualizerLimitBandLevels(pContext);
 
@@ -1483,39 +1519,14 @@
 
 int VolumeSetVolumeLevel(EffectContext *pContext, int16_t level){
 
-    LVM_ControlParams_t     ActiveParams;              /* Current control Parameters */
-    LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;     /* Function call status */
+    if (level > 0 || level < -9600) {
+        return -EINVAL;
+    }
 
-    //ALOGV("\tVolumeSetVolumeLevel Level to be set is %d %d\n", level, (LVM_INT16)(level/100));
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetVolumeLevel")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-    //ALOGV("\tVolumeSetVolumeLevel Succesfully returned from LVM_GetControlParameters got: %d\n",
-    //ActiveParams.VC_EffectLevel);
-
-    /* Volume parameters */
-    ActiveParams.VC_EffectLevel  = (LVM_INT16)(level/100);
-    //ALOGV("\tVolumeSetVolumeLevel() (-96dB -> 0dB)   -> %d\n", ActiveParams.VC_EffectLevel );
-
-    /* Activate the initial settings */
-    LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VolumeSetVolumeLevel")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    //ALOGV("\tVolumeSetVolumeLevel Succesfully called LVM_SetControlParameters\n");
-
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetVolumeLevel")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    //ALOGV("\tVolumeSetVolumeLevel just set (-96dB -> 0dB)   -> %d\n",ActiveParams.VC_EffectLevel );
-    if(pContext->pBundledContext->firstVolume == LVM_TRUE){
-        LvmStatus = LVM_SetVolumeNoSmoothing(pContext->pBundledContext->hInstance, &ActiveParams);
-        LVM_ERROR_CHECK(LvmStatus, "LVM_SetVolumeNoSmoothing", "LvmBundle_process")
-        ALOGV("\tLVM_VOLUME: Disabling Smoothing for first volume change to remove spikes/clicks");
-        pContext->pBundledContext->firstVolume = LVM_FALSE;
+    if (pContext->pBundledContext->bMuteEnabled == LVM_TRUE) {
+        pContext->pBundledContext->levelSaved = level / 100;
+    } else {
+        pContext->pBundledContext->volume = level / 100;
     }
 
     EqualizerLimitBandLevels(pContext);
@@ -1535,20 +1546,11 @@
 
 int VolumeGetVolumeLevel(EffectContext *pContext, int16_t *level){
 
-    //ALOGV("\tVolumeGetVolumeLevel start");
-
-    LVM_ControlParams_t     ActiveParams;                           /* Current control Parameters */
-    LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;                /* Function call status */
-
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeGetVolumeLevel")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    //ALOGV("\tVolumeGetVolumeLevel() (-96dB -> 0dB) -> %d\n", ActiveParams.VC_EffectLevel );
-    //ALOGV("\tVolumeGetVolumeLevel Succesfully returned from LVM_GetControlParameters\n");
-
-    *level = ActiveParams.VC_EffectLevel*100;     // Convert dB to millibels
-    //ALOGV("\tVolumeGetVolumeLevel end");
+    if (pContext->pBundledContext->bMuteEnabled == LVM_TRUE) {
+        *level = pContext->pBundledContext->levelSaved * 100;
+    } else {
+        *level = pContext->pBundledContext->volume * 100;
+    }
     return 0;
 }    /* end VolumeGetVolumeLevel */
 
@@ -1568,32 +1570,16 @@
 
     pContext->pBundledContext->bMuteEnabled = mute;
 
-    LVM_ControlParams_t     ActiveParams;              /* Current control Parameters */
-    LVM_ReturnStatus_en     LvmStatus=LVM_SUCCESS;     /* Function call status */
-
-    /* Get the current settings */
-    LvmStatus = LVM_GetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_GetControlParameters", "VolumeSetMute")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
-
-    //ALOGV("\tVolumeSetMute Succesfully returned from LVM_GetControlParameters\n");
-    //ALOGV("\tVolumeSetMute to %d, level was %d\n", mute, ActiveParams.VC_EffectLevel );
-
     /* Set appropriate volume level */
     if(pContext->pBundledContext->bMuteEnabled == LVM_TRUE){
-        pContext->pBundledContext->levelSaved = ActiveParams.VC_EffectLevel;
-        ActiveParams.VC_EffectLevel           = -96;
+        pContext->pBundledContext->levelSaved = pContext->pBundledContext->volume;
+        pContext->pBundledContext->volume = -96;
     }else{
-        ActiveParams.VC_EffectLevel  = pContext->pBundledContext->levelSaved;
+        pContext->pBundledContext->volume = pContext->pBundledContext->levelSaved;
     }
 
-    /* Activate the initial settings */
-    LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance, &ActiveParams);
-    LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "VolumeSetMute")
-    if(LvmStatus != LVM_SUCCESS) return -EINVAL;
+    EqualizerLimitBandLevels(pContext);
 
-    //ALOGV("\tVolumeSetMute Succesfully called LVM_SetControlParameters\n");
-    //ALOGV("\tVolumeSetMute end");
     return 0;
 }    /* end setMute */
 
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 9c58ecd..330bb32 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -38,7 +38,6 @@
 #define VOLUME_CUP_LOAD_ARM9E      0      // Expressed in 0.1 MIPS
 #define BUNDLE_MEM_USAGE           25     // Expressed in kB
 //#define LVM_PCM
-#define MAX_BAND_GAIN_DB           4
 
 #ifndef OPENSL_ES_H_
 static const effect_uuid_t SL_IID_VOLUME_ = { 0x09e8ede0, 0xddde, 0x11db, 0xb4f6,
@@ -97,6 +96,7 @@
     LVM_INT16                       *workBuffer;
     int                             frameCount;
     int32_t                         bandGaindB[FIVEBAND_NUMBANDS];
+    int                             volume;
     #ifdef LVM_PCM
     FILE                            *PcmInPtr;
     FILE                            *PcmOutPtr;
diff --git a/media/libstagefright/wifi-display/ANetworkSession.cpp b/media/libstagefright/wifi-display/ANetworkSession.cpp
index 435e72f..90db758 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.cpp
+++ b/media/libstagefright/wifi-display/ANetworkSession.cpp
@@ -56,7 +56,8 @@
     enum State {
         CONNECTING,
         CONNECTED,
-        LISTENING,
+        LISTENING_RTSP,
+        LISTENING_TCP_DGRAMS,
         DATAGRAM,
     };
 
@@ -69,7 +70,8 @@
     int socket() const;
     sp<AMessage> getNotificationMessage() const;
 
-    bool isListening() const;
+    bool isRTSPServer() const;
+    bool isTCPDatagramServer() const;
 
     bool wantsToRead();
     bool wantsToWrite();
@@ -79,12 +81,15 @@
 
     status_t sendRequest(const void *data, ssize_t size);
 
+    void setIsRTSPConnection(bool yesno);
+
 protected:
     virtual ~Session();
 
 private:
     int32_t mSessionID;
     State mState;
+    bool mIsRTSPConnection;
     int mSocket;
     sp<AMessage> mNotify;
     bool mSawReceiveFailure, mSawSendFailure;
@@ -123,6 +128,7 @@
         const sp<AMessage> &notify)
     : mSessionID(sessionID),
       mState(state),
+      mIsRTSPConnection(false),
       mSocket(s),
       mNotify(notify),
       mSawReceiveFailure(false),
@@ -184,12 +190,20 @@
     return mSocket;
 }
 
+void ANetworkSession::Session::setIsRTSPConnection(bool yesno) {
+    mIsRTSPConnection = yesno;
+}
+
 sp<AMessage> ANetworkSession::Session::getNotificationMessage() const {
     return mNotify;
 }
 
-bool ANetworkSession::Session::isListening() const {
-    return mState == LISTENING;
+bool ANetworkSession::Session::isRTSPServer() const {
+    return mState == LISTENING_RTSP;
+}
+
+bool ANetworkSession::Session::isTCPDatagramServer() const {
+    return mState == LISTENING_TCP_DGRAMS;
 }
 
 bool ANetworkSession::Session::wantsToRead() {
@@ -284,70 +298,93 @@
         err = -ECONNRESET;
     }
 
-    for (;;) {
-        size_t length;
+    if (!mIsRTSPConnection) {
+        // TCP stream carrying 16-bit length-prefixed datagrams.
 
-        if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
-            if (mInBuffer.size() < 4) {
+        while (mInBuffer.size() >= 2) {
+            size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
+
+            if (mInBuffer.size() < packetSize + 2) {
                 break;
             }
 
-            length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
+            sp<ABuffer> packet = new ABuffer(packetSize);
+            memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
 
-            if (mInBuffer.size() < 4 + length) {
+            sp<AMessage> notify = mNotify->dup();
+            notify->setInt32("sessionID", mSessionID);
+            notify->setInt32("reason", kWhatDatagram);
+            notify->setBuffer("data", packet);
+            notify->post();
+
+            mInBuffer.erase(0, packetSize + 2);
+        }
+    } else {
+        for (;;) {
+            size_t length;
+
+            if (mInBuffer.size() > 0 && mInBuffer.c_str()[0] == '$') {
+                if (mInBuffer.size() < 4) {
+                    break;
+                }
+
+                length = U16_AT((const uint8_t *)mInBuffer.c_str() + 2);
+
+                if (mInBuffer.size() < 4 + length) {
+                    break;
+                }
+
+                sp<AMessage> notify = mNotify->dup();
+                notify->setInt32("sessionID", mSessionID);
+                notify->setInt32("reason", kWhatBinaryData);
+                notify->setInt32("channel", mInBuffer.c_str()[1]);
+
+                sp<ABuffer> data = new ABuffer(length);
+                memcpy(data->data(), mInBuffer.c_str() + 4, length);
+
+                int64_t nowUs = ALooper::GetNowUs();
+                data->meta()->setInt64("arrivalTimeUs", nowUs);
+
+                notify->setBuffer("data", data);
+                notify->post();
+
+                mInBuffer.erase(0, 4 + length);
+                continue;
+            }
+
+            sp<ParsedMessage> msg =
+                ParsedMessage::Parse(
+                        mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
+
+            if (msg == NULL) {
                 break;
             }
 
             sp<AMessage> notify = mNotify->dup();
             notify->setInt32("sessionID", mSessionID);
-            notify->setInt32("reason", kWhatBinaryData);
-            notify->setInt32("channel", mInBuffer.c_str()[1]);
-
-            sp<ABuffer> data = new ABuffer(length);
-            memcpy(data->data(), mInBuffer.c_str() + 4, length);
-
-            int64_t nowUs = ALooper::GetNowUs();
-            data->meta()->setInt64("arrivalTimeUs", nowUs);
-
-            notify->setBuffer("data", data);
+            notify->setInt32("reason", kWhatData);
+            notify->setObject("data", msg);
             notify->post();
 
-            mInBuffer.erase(0, 4 + length);
-            continue;
-        }
-
-        sp<ParsedMessage> msg =
-            ParsedMessage::Parse(
-                    mInBuffer.c_str(), mInBuffer.size(), err != OK, &length);
-
-        if (msg == NULL) {
-            break;
-        }
-
-        sp<AMessage> notify = mNotify->dup();
-        notify->setInt32("sessionID", mSessionID);
-        notify->setInt32("reason", kWhatData);
-        notify->setObject("data", msg);
-        notify->post();
-
 #if 1
-        // XXX The (old) dongle sends the wrong content length header on a
-        // SET_PARAMETER request that signals a "wfd_idr_request".
-        // (17 instead of 19).
-        const char *content = msg->getContent();
-        if (content
-                && !memcmp(content, "wfd_idr_request\r\n", 17)
-                && length >= 19
-                && mInBuffer.c_str()[length] == '\r'
-                && mInBuffer.c_str()[length + 1] == '\n') {
-            length += 2;
-        }
+            // XXX The (old) dongle sends the wrong content length header on a
+            // SET_PARAMETER request that signals a "wfd_idr_request".
+            // (17 instead of 19).
+            const char *content = msg->getContent();
+            if (content
+                    && !memcmp(content, "wfd_idr_request\r\n", 17)
+                    && length >= 19
+                    && mInBuffer.c_str()[length] == '\r'
+                    && mInBuffer.c_str()[length + 1] == '\n') {
+                length += 2;
+            }
 #endif
 
-        mInBuffer.erase(0, length);
+            mInBuffer.erase(0, length);
 
-        if (err != OK) {
-            break;
+            if (err != OK) {
+                break;
+            }
         }
     }
 
@@ -408,7 +445,7 @@
             notifyError(kWhatError, -err, "Connection failed");
             mSawSendFailure = true;
 
-            return UNKNOWN_ERROR;
+            return -err;
         }
 
         mState = CONNECTED;
@@ -451,6 +488,16 @@
 status_t ANetworkSession::Session::sendRequest(const void *data, ssize_t size) {
     CHECK(mState == CONNECTED || mState == DATAGRAM);
 
+    if (mState == CONNECTED && !mIsRTSPConnection) {
+        CHECK_LE(size, 65535);
+
+        uint8_t prefix[2];
+        prefix[0] = size >> 8;
+        prefix[1] = size & 0xff;
+
+        mOutBuffer.append((const char *)prefix, sizeof(prefix));
+    }
+
     mOutBuffer.append(
             (const char *)data,
             (size >= 0) ? size : strlen((const char *)data));
@@ -585,6 +632,35 @@
             sessionID);
 }
 
+status_t ANetworkSession::createTCPDatagramSession(
+        const struct in_addr &addr, unsigned port,
+        const sp<AMessage> &notify, int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateTCPDatagramSessionPassive,
+            &addr,
+            port,
+            NULL /* remoteHost */,
+            0 /* remotePort */,
+            notify,
+            sessionID);
+}
+
+status_t ANetworkSession::createTCPDatagramSession(
+        unsigned localPort,
+        const char *remoteHost,
+        unsigned remotePort,
+        const sp<AMessage> &notify,
+        int32_t *sessionID) {
+    return createClientOrServer(
+            kModeCreateTCPDatagramSessionActive,
+            NULL /* addr */,
+            localPort,
+            remoteHost,
+            remotePort,
+            notify,
+            sessionID);
+}
+
 status_t ANetworkSession::destroySession(int32_t sessionID) {
     Mutex::Autolock autoLock(mLock);
 
@@ -641,7 +717,8 @@
         goto bail;
     }
 
-    if (mode == kModeCreateRTSPServer) {
+    if (mode == kModeCreateRTSPServer
+            || mode == kModeCreateTCPDatagramSessionPassive) {
         const int yes = 1;
         res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
 
@@ -679,7 +756,8 @@
     memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
     addr.sin_family = AF_INET;
 
-    if (mode == kModeCreateRTSPClient) {
+    if (mode == kModeCreateRTSPClient
+            || mode == kModeCreateTCPDatagramSessionActive) {
         struct hostent *ent= gethostbyname(remoteHost);
         if (ent == NULL) {
             err = -h_errno;
@@ -696,7 +774,17 @@
         addr.sin_port = htons(port);
     }
 
-    if (mode == kModeCreateRTSPClient) {
+    if (mode == kModeCreateRTSPClient
+            || mode == kModeCreateTCPDatagramSessionActive) {
+        in_addr_t x = ntohl(addr.sin_addr.s_addr);
+        ALOGI("connecting socket %d to %d.%d.%d.%d:%d",
+              s,
+              (x >> 24),
+              (x >> 16) & 0xff,
+              (x >> 8) & 0xff,
+              x & 0xff,
+              ntohs(addr.sin_port));
+
         res = connect(s, (const struct sockaddr *)&addr, sizeof(addr));
 
         CHECK_LT(res, 0);
@@ -707,7 +795,8 @@
         res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
 
         if (res == 0) {
-            if (mode == kModeCreateRTSPServer) {
+            if (mode == kModeCreateRTSPServer
+                    || mode == kModeCreateTCPDatagramSessionPassive) {
                 res = listen(s, 4);
             } else {
                 CHECK_EQ(mode, kModeCreateUDPSession);
@@ -746,8 +835,16 @@
             state = Session::CONNECTING;
             break;
 
+        case kModeCreateTCPDatagramSessionActive:
+            state = Session::CONNECTING;
+            break;
+
+        case kModeCreateTCPDatagramSessionPassive:
+            state = Session::LISTENING_TCP_DGRAMS;
+            break;
+
         case kModeCreateRTSPServer:
-            state = Session::LISTENING;
+            state = Session::LISTENING_RTSP;
             break;
 
         default:
@@ -762,6 +859,12 @@
             s,
             notify);
 
+    if (mode == kModeCreateTCPDatagramSessionActive) {
+        session->setIsRTSPConnection(false);
+    } else if (mode == kModeCreateRTSPClient) {
+        session->setIsRTSPConnection(true);
+    }
+
     mSessions.add(session->sessionID(), session);
 
     interrupt();
@@ -797,7 +900,7 @@
     remoteAddr.sin_port = htons(remotePort);
 
     status_t err = OK;
-    struct hostent *ent= gethostbyname(remoteHost);
+    struct hostent *ent = gethostbyname(remoteHost);
     if (ent == NULL) {
         err = -h_errno;
     } else {
@@ -932,7 +1035,7 @@
             }
 
             if (FD_ISSET(s, &rs)) {
-                if (session->isListening()) {
+                if (session->isRTSPServer() || session->isTCPDatagramServer()) {
                     struct sockaddr_in remoteAddr;
                     socklen_t remoteAddrLen = sizeof(remoteAddr);
 
@@ -969,6 +1072,9 @@
                                         clientSocket,
                                         session->getNotificationMessage());
 
+                            clientSession->setIsRTSPConnection(
+                                    session->isRTSPServer());
+
                             sessionsToAdd.push_back(clientSession);
                         }
                     } else {
diff --git a/media/libstagefright/wifi-display/ANetworkSession.h b/media/libstagefright/wifi-display/ANetworkSession.h
index 8d961ee..c1acdcc 100644
--- a/media/libstagefright/wifi-display/ANetworkSession.h
+++ b/media/libstagefright/wifi-display/ANetworkSession.h
@@ -58,6 +58,19 @@
     status_t connectUDPSession(
             int32_t sessionID, const char *remoteHost, unsigned remotePort);
 
+    // passive
+    status_t createTCPDatagramSession(
+            const struct in_addr &addr, unsigned port,
+            const sp<AMessage> &notify, int32_t *sessionID);
+
+    // active
+    status_t createTCPDatagramSession(
+            unsigned localPort,
+            const char *remoteHost,
+            unsigned remotePort,
+            const sp<AMessage> &notify,
+            int32_t *sessionID);
+
     status_t destroySession(int32_t sessionID);
 
     status_t sendRequest(
@@ -90,6 +103,8 @@
 
     enum Mode {
         kModeCreateUDPSession,
+        kModeCreateTCPDatagramSessionPassive,
+        kModeCreateTCPDatagramSessionActive,
         kModeCreateRTSPServer,
         kModeCreateRTSPClient,
     };
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index f6f7030..e961518 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -46,8 +46,8 @@
 
 #include <OMX_IVCommon.h>
 
-//#define FAKE_VIDEO      1
-#define USE_SERIALIZER  0
+//#define FAKE_VIDEO            1
+#define USE_SERIALIZER          0
 
 namespace android {
 
@@ -171,20 +171,26 @@
 WifiDisplaySource::PlaybackSession::PlaybackSession(
         const sp<ANetworkSession> &netSession,
         const sp<AMessage> &notify,
+        const in_addr &interfaceAddr,
         bool legacyMode)
     : mNetSession(netSession),
       mNotify(notify),
+      mInterfaceAddr(interfaceAddr),
       mLegacyMode(legacyMode),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
       mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
       mPrevTimeUs(-1ll),
-      mUseInterleavedTCP(false),
+      mTransportMode(TRANSPORT_UDP),
       mRTPChannel(0),
       mRTCPChannel(0),
       mRTPPort(0),
       mRTPSessionID(0),
       mRTCPSessionID(0),
+      mClientRTPPort(0),
+      mClientRTCPPort(0),
+      mRTPConnected(false),
+      mRTCPConnected(false),
       mRTPSeqNo(0),
       mLastNTPTime(0),
       mLastRTPTime(0),
@@ -208,15 +214,18 @@
 
 status_t WifiDisplaySource::PlaybackSession::init(
         const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-        bool useInterleavedTCP) {
+        TransportMode transportMode) {
+    mClientIP = clientIP;
+
     status_t err = setupPacketizer();
 
     if (err != OK) {
         return err;
     }
 
-    if (useInterleavedTCP) {
-        mUseInterleavedTCP = true;
+    mTransportMode = transportMode;
+
+    if (transportMode == TRANSPORT_TCP_INTERLEAVED) {
         mRTPChannel = clientRtp;
         mRTCPChannel = clientRtcp;
         mRTPPort = 0;
@@ -227,19 +236,38 @@
         return OK;
     }
 
-    mUseInterleavedTCP = false;
     mRTPChannel = 0;
     mRTCPChannel = 0;
 
+    if (mTransportMode == TRANSPORT_TCP) {
+        // XXX This is wrong, we need to allocate sockets here, we only
+        // need to do this because the dongles are not establishing their
+        // end until after PLAY instead of before SETUP.
+        mRTPPort = 20000;
+        mRTPSessionID = 0;
+        mRTCPSessionID = 0;
+        mClientRTPPort = clientRtp;
+        mClientRTCPPort = clientRtcp;
+
+        updateLiveness();
+        return OK;
+    }
+
     int serverRtp;
 
     sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
     sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
     for (serverRtp = 15550;; serverRtp += 2) {
         int32_t rtpSession;
-        err = mNetSession->createUDPSession(
-                    serverRtp, clientIP, clientRtp,
-                    rtpNotify, &rtpSession);
+        if (mTransportMode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                        serverRtp, clientIP, clientRtp,
+                        rtpNotify, &rtpSession);
+        } else {
+            err = mNetSession->createTCPDatagramSession(
+                        serverRtp, clientIP, clientRtp,
+                        rtpNotify, &rtpSession);
+        }
 
         if (err != OK) {
             ALOGI("failed to create RTP socket on port %d", serverRtp);
@@ -258,9 +286,15 @@
         }
 
         int32_t rtcpSession;
-        err = mNetSession->createUDPSession(
-                serverRtp + 1, clientIP, clientRtcp,
-                rtcpNotify, &rtcpSession);
+        if (mTransportMode == TRANSPORT_UDP) {
+            err = mNetSession->createUDPSession(
+                    serverRtp + 1, clientIP, clientRtcp,
+                    rtcpNotify, &rtcpSession);
+        } else {
+            err = mNetSession->createTCPDatagramSession(
+                    serverRtp + 1, clientIP, clientRtcp,
+                    rtcpNotify, &rtcpSession);
+        }
 
         if (err == OK) {
             mRTPPort = serverRtp;
@@ -308,6 +342,42 @@
 status_t WifiDisplaySource::PlaybackSession::play() {
     updateLiveness();
 
+    return OK;
+}
+
+status_t WifiDisplaySource::PlaybackSession::finishPlay() {
+    // XXX Give the dongle 3 secs to bind its sockets.
+    (new AMessage(kWhatFinishPlay, id()))->post(3000000ll);
+    return OK;
+}
+
+status_t WifiDisplaySource::PlaybackSession::onFinishPlay() {
+    if (mTransportMode != TRANSPORT_TCP) {
+        return onFinishPlay2();
+    }
+
+    sp<AMessage> rtpNotify = new AMessage(kWhatRTPNotify, id());
+
+    status_t err = mNetSession->createTCPDatagramSession(
+                mRTPPort, mClientIP.c_str(), mClientRTPPort,
+                rtpNotify, &mRTPSessionID);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (mClientRTCPPort >= 0) {
+        sp<AMessage> rtcpNotify = new AMessage(kWhatRTCPNotify, id());
+
+        err = mNetSession->createTCPDatagramSession(
+                mRTPPort + 1, mClientIP.c_str(), mClientRTCPPort,
+                rtcpNotify, &mRTCPSessionID);
+    }
+
+    return err;
+}
+
+status_t WifiDisplaySource::PlaybackSession::onFinishPlay2() {
     if (mRTCPSessionID != 0) {
         scheduleSendSR();
     }
@@ -328,6 +398,10 @@
         }
     }
 
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatSessionEstablished);
+    notify->post();
+
     return OK;
 }
 
@@ -445,6 +519,32 @@
                     break;
                 }
 
+                case ANetworkSession::kWhatConnected:
+                {
+                    CHECK_EQ(mTransportMode, TRANSPORT_TCP);
+
+                    int32_t sessionID;
+                    CHECK(msg->findInt32("sessionID", &sessionID));
+
+                    if (sessionID == mRTPSessionID) {
+                        CHECK(!mRTPConnected);
+                        mRTPConnected = true;
+                        ALOGI("RTP Session now connected.");
+                    } else if (sessionID == mRTCPSessionID) {
+                        CHECK(!mRTCPConnected);
+                        mRTCPConnected = true;
+                        ALOGI("RTCP Session now connected.");
+                    } else {
+                        TRESPASS();
+                    }
+
+                    if (mRTPConnected
+                            && (mClientRTCPPort < 0 || mRTCPConnected)) {
+                        onFinishPlay2();
+                    }
+                    break;
+                }
+
                 default:
                     TRESPASS();
             }
@@ -610,6 +710,12 @@
             break;
         }
 
+        case kWhatFinishPlay:
+        {
+            onFinishPlay();
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -956,15 +1062,14 @@
     addSR(buffer);
     addSDES(buffer);
 
-    if (mUseInterleavedTCP) {
+    if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
         sp<AMessage> notify = mNotify->dup();
         notify->setInt32("what", kWhatBinaryData);
         notify->setInt32("channel", mRTCPChannel);
         notify->setBuffer("data", buffer);
         notify->post();
     } else {
-        mNetSession->sendRequest(
-                mRTCPSessionID, buffer->data(), buffer->size());
+        sendPacket(mRTCPSessionID, buffer->data(), buffer->size());
     }
 
     ++mNumSRsSent;
@@ -1011,7 +1116,7 @@
         mLastRTPTime = rtpTime;
         mLastNTPTime = GetNowNTP();
 
-        if (mUseInterleavedTCP) {
+        if (mTransportMode == TRANSPORT_TCP_INTERLEAVED) {
             sp<AMessage> notify = mNotify->dup();
             notify->setInt32("what", kWhatBinaryData);
 
@@ -1022,8 +1127,7 @@
             notify->setBuffer("data", data);
             notify->post();
         } else {
-            mNetSession->sendRequest(
-                    mRTPSessionID, rtp, mTSQueue->size());
+            sendPacket(mRTPSessionID, rtp, mTSQueue->size());
 
             mTotalBytesSent += mTSQueue->size();
             int64_t delayUs = ALooper::GetNowUs() - mFirstPacketTimeUs;
@@ -1144,8 +1248,7 @@
             uint16_t bufferSeqNo = buffer->int32Data() & 0xffff;
 
             if (bufferSeqNo == seqNo) {
-                mNetSession->sendRequest(
-                        mRTPSessionID, buffer->data(), buffer->size());
+                sendPacket(mRTPSessionID, buffer->data(), buffer->size());
 
                 found = true;
                 break;
@@ -1172,5 +1275,10 @@
     }
 }
 
+status_t WifiDisplaySource::PlaybackSession::sendPacket(
+        int32_t sessionID, const void *data, size_t size) {
+    return mNetSession->sendRequest(sessionID, data, size);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 8b6ddee..0047842 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -38,11 +38,17 @@
     PlaybackSession(
             const sp<ANetworkSession> &netSession,
             const sp<AMessage> &notify,
+            const struct in_addr &interfaceAddr,
             bool legacyMode);
 
+    enum TransportMode {
+        TRANSPORT_UDP,
+        TRANSPORT_TCP_INTERLEAVED,
+        TRANSPORT_TCP,
+    };
     status_t init(
             const char *clientIP, int32_t clientRtp, int32_t clientRtcp,
-            bool useInterleavedTCP);
+            TransportMode transportMode);
 
     status_t destroy();
 
@@ -52,6 +58,7 @@
     void updateLiveness();
 
     status_t play();
+    status_t finishPlay();
     status_t pause();
 
     sp<ISurfaceTexture> getSurfaceTexture();
@@ -63,6 +70,7 @@
     enum {
         kWhatSessionDead,
         kWhatBinaryData,
+        kWhatSessionEstablished,
     };
 
 protected:
@@ -79,6 +87,7 @@
         kWhatSerializerNotify,
         kWhatConverterNotify,
         kWhatUpdateSurface,
+        kWhatFinishPlay,
     };
 
     static const int64_t kSendSRIntervalUs = 10000000ll;
@@ -87,6 +96,7 @@
 
     sp<ANetworkSession> mNetSession;
     sp<AMessage> mNotify;
+    in_addr mInterfaceAddr;
     bool mLegacyMode;
 
     int64_t mLastLifesignUs;
@@ -102,7 +112,9 @@
     sp<ABuffer> mTSQueue;
     int64_t mPrevTimeUs;
 
-    bool mUseInterleavedTCP;
+    TransportMode mTransportMode;
+
+    AString mClientIP;
 
     // in TCP mode
     int32_t mRTPChannel;
@@ -113,6 +125,10 @@
     int32_t mRTPSessionID;
     int32_t mRTCPSessionID;
 
+    int32_t mClientRTPPort;
+    int32_t mClientRTCPPort;
+    bool mRTPConnected;
+    bool mRTCPConnected;
 
     uint32_t mRTPSeqNo;
 
@@ -160,6 +176,10 @@
     status_t parseRTCP(const sp<ABuffer> &buffer);
     status_t parseTSFB(const uint8_t *data, size_t size);
 
+    status_t sendPacket(int32_t sessionID, const void *data, size_t size);
+    status_t onFinishPlay();
+    status_t onFinishPlay2();
+
     DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
 };
 
diff --git a/media/libstagefright/wifi-display/source/Serializer.cpp b/media/libstagefright/wifi-display/source/Serializer.cpp
index 78dc4e6..598fd3e 100644
--- a/media/libstagefright/wifi-display/source/Serializer.cpp
+++ b/media/libstagefright/wifi-display/source/Serializer.cpp
@@ -286,8 +286,7 @@
     }
 
     if (err == OK) {
-        // XXX add a 5 second delay for the client to get ready.
-        schedulePoll(5000000ll);
+        schedulePoll();
     }
 
     return err;
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 53adb87..8fead96 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -31,7 +31,7 @@
 #include <media/stagefright/MediaErrors.h>
 
 #include <arpa/inet.h>
-#include <netinet/in.h>
+#include <cutils/properties.h>
 
 namespace android {
 
@@ -41,6 +41,7 @@
     : mNetSession(netSession),
       mClient(client),
       mSessionID(0),
+      mClientSessionID(0),
       mReaperPending(false),
       mNextCSeq(1) {
 }
@@ -114,14 +115,12 @@
                 port = kWifiDisplayDefaultPort;
             }
 
-            struct in_addr addr;
-
             if (err == OK) {
-                if (inet_aton(iface.c_str(), &addr) != 0) {
+                if (inet_aton(iface.c_str(), &mInterfaceAddr) != 0) {
                     sp<AMessage> notify = new AMessage(kWhatRTSPNotify, id());
 
                     err = mNetSession->createRTSPServer(
-                            addr, port, notify, &mSessionID);
+                            mInterfaceAddr, port, notify, &mSessionID);
                 } else {
                     err = -EINVAL;
                 }
@@ -158,7 +157,11 @@
 
                     mNetSession->destroySession(sessionID);
 
-                    mClientInfos.removeItem(sessionID);
+                    if (sessionID == mClientSessionID) {
+                        mClientSessionID = -1;
+
+                        disconnectClient(UNKNOWN_ERROR);
+                    }
                     break;
                 }
 
@@ -167,16 +170,32 @@
                     int32_t sessionID;
                     CHECK(msg->findInt32("sessionID", &sessionID));
 
-                    ClientInfo info;
-                    CHECK(msg->findString("client-ip", &info.mRemoteIP));
-                    CHECK(msg->findString("server-ip", &info.mLocalIP));
-                    CHECK(msg->findInt32("server-port", &info.mLocalPort));
-                    info.mPlaybackSessionID = -1;
+                    if (mClientSessionID > 0) {
+                        ALOGW("A client tried to connect, but we already "
+                              "have one.");
+
+                        mNetSession->destroySession(sessionID);
+                        break;
+                    }
+
+                    CHECK(msg->findString("client-ip", &mClientInfo.mRemoteIP));
+                    CHECK(msg->findString("server-ip", &mClientInfo.mLocalIP));
+
+                    if (mClientInfo.mRemoteIP == mClientInfo.mLocalIP) {
+                        // Disallow connections from the local interface
+                        // for security reasons.
+                        mNetSession->destroySession(sessionID);
+                        break;
+                    }
+
+                    CHECK(msg->findInt32(
+                                "server-port", &mClientInfo.mLocalPort));
+                    mClientInfo.mPlaybackSessionID = -1;
+
+                    mClientSessionID = sessionID;
 
                     ALOGI("We now have a client (%d) connected.", sessionID);
 
-                    mClientInfos.add(sessionID, info);
-
                     status_t err = sendM1(sessionID);
                     CHECK_EQ(err, (status_t)OK);
                     break;
@@ -199,20 +218,7 @@
             uint32_t replyID;
             CHECK(msg->senderAwaitsResponse(&replyID));
 
-            for (size_t i = mPlaybackSessions.size(); i-- > 0;) {
-                sp<PlaybackSession> playbackSession =
-                    mPlaybackSessions.valueAt(i);
-
-                mPlaybackSessions.removeItemsAt(i);
-
-                playbackSession->destroy();
-                looper()->unregisterHandler(playbackSession->id());
-                playbackSession.clear();
-            }
-
-            if (mClient != NULL) {
-                mClient->onDisplayDisconnected();
-            }
+            disconnectClient(OK);
 
             status_t err = OK;
 
@@ -226,21 +232,17 @@
         {
             mReaperPending = false;
 
-            for (size_t i = mPlaybackSessions.size(); i-- > 0;) {
-                const sp<PlaybackSession> &playbackSession =
-                    mPlaybackSessions.valueAt(i);
-
-                if (playbackSession->getLastLifesignUs()
-                        + kPlaybackSessionTimeoutUs < ALooper::GetNowUs()) {
-                    ALOGI("playback session %d timed out, reaping.",
-                            mPlaybackSessions.keyAt(i));
-
-                    looper()->unregisterHandler(playbackSession->id());
-                    mPlaybackSessions.removeItemsAt(i);
-                }
+            if (mClientSessionID == 0
+                    || mClientInfo.mPlaybackSession == NULL) {
+                break;
             }
 
-            if (!mPlaybackSessions.isEmpty()) {
+            if (mClientInfo.mPlaybackSession->getLastLifesignUs()
+                    + kPlaybackSessionTimeoutUs < ALooper::GetNowUs()) {
+                ALOGI("playback session timed out, reaping.");
+
+                disconnectClient(-ETIMEDOUT);
+            } else {
                 scheduleReaper();
             }
             break;
@@ -254,44 +256,44 @@
             int32_t what;
             CHECK(msg->findInt32("what", &what));
 
-            ssize_t index = mPlaybackSessions.indexOfKey(playbackSessionID);
-            if (index >= 0) {
-                const sp<PlaybackSession> &playbackSession =
-                    mPlaybackSessions.valueAt(index);
+            if (what == PlaybackSession::kWhatSessionDead) {
+                ALOGI("playback session wants to quit.");
 
-                if (what == PlaybackSession::kWhatSessionDead) {
-                    ALOGI("playback sessions %d wants to quit.",
-                          playbackSessionID);
-
-                    looper()->unregisterHandler(playbackSession->id());
-                    mPlaybackSessions.removeItemsAt(index);
-                } else {
-                    CHECK_EQ(what, PlaybackSession::kWhatBinaryData);
-
-                    int32_t channel;
-                    CHECK(msg->findInt32("channel", &channel));
-
-                    sp<ABuffer> data;
-                    CHECK(msg->findBuffer("data", &data));
-
-                    CHECK_LE(channel, 0xffu);
-                    CHECK_LE(data->size(), 0xffffu);
-
-                    int32_t sessionID;
-                    CHECK(msg->findInt32("sessionID", &sessionID));
-
-                    char header[4];
-                    header[0] = '$';
-                    header[1] = channel;
-                    header[2] = data->size() >> 8;
-                    header[3] = data->size() & 0xff;
-
-                    mNetSession->sendRequest(
-                            sessionID, header, sizeof(header));
-
-                    mNetSession->sendRequest(
-                            sessionID, data->data(), data->size());
+                disconnectClient(UNKNOWN_ERROR);
+            } else if (what == PlaybackSession::kWhatSessionEstablished) {
+                if (mClient != NULL) {
+                    mClient->onDisplayConnected(
+                            mClientInfo.mPlaybackSession->getSurfaceTexture(),
+                            mClientInfo.mPlaybackSession->width(),
+                            mClientInfo.mPlaybackSession->height(),
+                            0 /* flags */);
                 }
+            } else {
+                CHECK_EQ(what, PlaybackSession::kWhatBinaryData);
+
+                int32_t channel;
+                CHECK(msg->findInt32("channel", &channel));
+
+                sp<ABuffer> data;
+                CHECK(msg->findBuffer("data", &data));
+
+                CHECK_LE(channel, 0xffu);
+                CHECK_LE(data->size(), 0xffffu);
+
+                int32_t sessionID;
+                CHECK(msg->findInt32("sessionID", &sessionID));
+
+                char header[4];
+                header[0] = '$';
+                header[1] = channel;
+                header[2] = data->size() >> 8;
+                header[3] = data->size() & 0xff;
+
+                mNetSession->sendRequest(
+                        sessionID, header, sizeof(header));
+
+                mNetSession->sendRequest(
+                        sessionID, data->data(), data->size());
             }
             break;
         }
@@ -301,7 +303,7 @@
             int32_t sessionID;
             CHECK(msg->findInt32("sessionID", &sessionID));
 
-            if (mClientInfos.indexOfKey(sessionID) < 0) {
+            if (mClientSessionID != sessionID) {
                 // Obsolete event, client is already gone.
                 break;
             }
@@ -392,15 +394,25 @@
     //   max-hres (none or 2 byte)
     //   max-vres (none or 2 byte)
 
-    const ClientInfo &info = mClientInfos.valueFor(sessionID);
+    CHECK_EQ(sessionID, mClientSessionID);
+
+    AString transportString = "UDP";
+
+    char val[PROPERTY_VALUE_MAX];
+    if (property_get("media.wfd.enable-tcp", val, NULL)
+            && (!strcasecmp("true", val) || !strcmp("1", val))) {
+        ALOGI("Using TCP transport.");
+        transportString = "TCP";
+    }
 
     AString body = StringPrintf(
         "wfd_video_formats: "
         "30 00 02 02 00000040 00000000 00000000 00 0000 0000 00 none none\r\n"
         "wfd_audio_codecs: AAC 00000001 00\r\n"  // 2 ch AAC 48kHz
         "wfd_presentation_URL: rtsp://%s:%d/wfd1.0/streamid=0 none\r\n"
-        "wfd_client_rtp_ports: RTP/AVP/UDP;unicast 19000 0 mode=play\r\n",
-        info.mLocalIP.c_str(), info.mLocalPort);
+        "wfd_client_rtp_ports: RTP/AVP/%s;unicast 19000 0 mode=play\r\n",
+        mClientInfo.mLocalIP.c_str(), mClientInfo.mLocalPort,
+        transportString.c_str());
 
     AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
     AppendCommonResponse(&request, mNextCSeq);
@@ -455,8 +467,9 @@
     AString request = "GET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
     AppendCommonResponse(&request, mNextCSeq);
 
-    const ClientInfo &info = mClientInfos.valueFor(sessionID);
-    request.append(StringPrintf("Session: %d\r\n", info.mPlaybackSessionID));
+    CHECK_EQ(sessionID, mClientSessionID);
+    request.append(
+            StringPrintf("Session: %d\r\n", mClientInfo.mPlaybackSessionID));
     request.append("\r\n");  // Empty body
 
     status_t err =
@@ -534,11 +547,10 @@
         int32_t sessionID, const sp<ParsedMessage> &msg) {
     // If only the response was required to include a "Session:" header...
 
-    const ClientInfo &info = mClientInfos.valueFor(sessionID);
+    CHECK_EQ(sessionID, mClientSessionID);
 
-    ssize_t index = mPlaybackSessions.indexOfKey(info.mPlaybackSessionID);
-    if (index >= 0) {
-        mPlaybackSessions.valueAt(index)->updateLiveness();
+    if (mClientInfo.mPlaybackSession != NULL) {
+        mClientInfo.mPlaybackSession->updateLiveness();
 
         scheduleKeepAlive(sessionID);
     }
@@ -712,8 +724,8 @@
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
-    ClientInfo *info = &mClientInfos.editValueFor(sessionID);
-    if (info->mPlaybackSessionID != -1) {
+    CHECK_EQ(sessionID, mClientSessionID);
+    if (mClientInfo.mPlaybackSessionID != -1) {
         // We only support a single playback session per client.
         // This is due to the reversed keep-alive design in the wfd specs...
         sendErrorResponse(sessionID, "400 Bad Request", cseq);
@@ -726,20 +738,40 @@
         return;
     }
 
-    bool useInterleavedTCP = false;
+    PlaybackSession::TransportMode transportMode =
+        PlaybackSession::TRANSPORT_UDP;
 
     int clientRtp, clientRtcp;
     if (transport.startsWith("RTP/AVP/TCP;")) {
         AString interleaved;
-        if (!ParsedMessage::GetAttribute(
+        if (ParsedMessage::GetAttribute(
                     transport.c_str(), "interleaved", &interleaved)
-                || sscanf(interleaved.c_str(), "%d-%d",
-                          &clientRtp, &clientRtcp) != 2) {
-            sendErrorResponse(sessionID, "400 Bad Request", cseq);
-            return;
-        }
+                && sscanf(interleaved.c_str(), "%d-%d",
+                          &clientRtp, &clientRtcp) == 2) {
+            transportMode = PlaybackSession::TRANSPORT_TCP_INTERLEAVED;
+        } else {
+            bool badRequest = false;
 
-        useInterleavedTCP = true;
+            AString clientPort;
+            if (!ParsedMessage::GetAttribute(
+                        transport.c_str(), "client_port", &clientPort)) {
+                badRequest = true;
+            } else if (sscanf(clientPort.c_str(), "%d-%d",
+                              &clientRtp, &clientRtcp) == 2) {
+            } else if (sscanf(clientPort.c_str(), "%d", &clientRtp) == 1) {
+                // No RTCP.
+                clientRtcp = -1;
+            } else {
+                badRequest = true;
+            }
+
+            if (badRequest) {
+                sendErrorResponse(sessionID, "400 Bad Request", cseq);
+                return;
+            }
+
+            transportMode = PlaybackSession::TRANSPORT_TCP;
+        }
     } else if (transport.startsWith("RTP/AVP;unicast;")
             || transport.startsWith("RTP/AVP/UDP;unicast;")) {
         bool badRequest = false;
@@ -780,7 +812,8 @@
 
     sp<PlaybackSession> playbackSession =
         new PlaybackSession(
-                mNetSession, notify, mClient == NULL /* legacyMode */);
+                mNetSession, notify, mInterfaceAddr,
+                mClient == NULL /* legacyMode */);
 
     looper()->registerHandler(playbackSession);
 
@@ -798,10 +831,10 @@
     }
 
     status_t err = playbackSession->init(
-            info->mRemoteIP.c_str(),
+            mClientInfo.mRemoteIP.c_str(),
             clientRtp,
             clientRtcp,
-            useInterleavedTCP);
+            transportMode);
 
     if (err != OK) {
         looper()->unregisterHandler(playbackSession->id());
@@ -819,14 +852,13 @@
             return;
     }
 
-    mPlaybackSessions.add(playbackSessionID, playbackSession);
-
-    info->mPlaybackSessionID = playbackSessionID;
+    mClientInfo.mPlaybackSessionID = playbackSessionID;
+    mClientInfo.mPlaybackSession = playbackSession;
 
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
 
-    if (useInterleavedTCP) {
+    if (transportMode == PlaybackSession::TRANSPORT_TCP_INTERLEAVED) {
         response.append(
                 StringPrintf(
                     "Transport: RTP/AVP/TCP;interleaved=%d-%d;",
@@ -834,17 +866,24 @@
     } else {
         int32_t serverRtp = playbackSession->getRTPPort();
 
+        AString transportString = "UDP";
+        if (transportMode == PlaybackSession::TRANSPORT_TCP) {
+            transportString = "TCP";
+        }
+
         if (clientRtcp >= 0) {
             response.append(
                     StringPrintf(
-                        "Transport: RTP/AVP;unicast;client_port=%d-%d;"
+                        "Transport: RTP/AVP/%s;unicast;client_port=%d-%d;"
                         "server_port=%d-%d\r\n",
+                        transportString.c_str(),
                         clientRtp, clientRtcp, serverRtp, serverRtp + 1));
         } else {
             response.append(
                     StringPrintf(
-                        "Transport: RTP/AVP;unicast;client_port=%d;"
+                        "Transport: RTP/AVP/%s;unicast;client_port=%d;"
                         "server_port=%d\r\n",
+                        transportString.c_str(),
                         clientRtp, serverRtp));
         }
     }
@@ -882,13 +921,7 @@
     err = mNetSession->sendRequest(sessionID, response.c_str());
     CHECK_EQ(err, (status_t)OK);
 
-    if (mClient != NULL) {
-        mClient->onDisplayConnected(
-                playbackSession->getSurfaceTexture(),
-                playbackSession->width(),
-                playbackSession->height(),
-                0 /* flags */);
-    }
+    playbackSession->finishPlay();
 }
 
 void WifiDisplaySource::onPauseRequest(
@@ -928,15 +961,15 @@
         return;
     }
 
-    looper()->unregisterHandler(playbackSession->id());
-    mPlaybackSessions.removeItem(playbackSessionID);
-
     AString response = "RTSP/1.0 200 OK\r\n";
     AppendCommonResponse(&response, cseq, playbackSessionID);
+    response.append("Connection: close\r\n");
     response.append("\r\n");
 
     status_t err = mNetSession->sendRequest(sessionID, response.c_str());
     CHECK_EQ(err, (status_t)OK);
+
+    disconnectClient(UNKNOWN_ERROR);
 }
 
 void WifiDisplaySource::onGetParameterRequest(
@@ -970,19 +1003,10 @@
     sp<PlaybackSession> playbackSession =
         findPlaybackSession(data, &playbackSessionID);
 
-#if 1
-    // XXX the older dongles do not include a "Session:" header in this request.
-    if (playbackSession == NULL) {
-        CHECK_EQ(mPlaybackSessions.size(), 1u);
-        playbackSessionID = mPlaybackSessions.keyAt(0);
-        playbackSession = mPlaybackSessions.valueAt(0);
-    }
-#else
     if (playbackSession == NULL) {
         sendErrorResponse(sessionID, "454 Session Not Found", cseq);
         return;
     }
-#endif
 
     // XXX check that the parameter is about that.
     playbackSession->requestIDRFrame();
@@ -1041,37 +1065,42 @@
 }
 
 int32_t WifiDisplaySource::makeUniquePlaybackSessionID() const {
-    for (;;) {
-        int32_t playbackSessionID = rand();
-
-        if (playbackSessionID == -1) {
-            // reserved.
-            continue;
-        }
-
-        for (size_t i = 0; i < mPlaybackSessions.size(); ++i) {
-            if (mPlaybackSessions.keyAt(i) == playbackSessionID) {
-                continue;
-            }
-        }
-
-        return playbackSessionID;
-    }
+    return rand();
 }
 
 sp<WifiDisplaySource::PlaybackSession> WifiDisplaySource::findPlaybackSession(
         const sp<ParsedMessage> &data, int32_t *playbackSessionID) const {
     if (!data->findInt32("session", playbackSessionID)) {
-        *playbackSessionID = 0;
+        // XXX the older dongles do not always include a "Session:" header.
+        *playbackSessionID = mClientInfo.mPlaybackSessionID;
+        return mClientInfo.mPlaybackSession;
+    }
+
+    if (*playbackSessionID != mClientInfo.mPlaybackSessionID) {
         return NULL;
     }
 
-    ssize_t index = mPlaybackSessions.indexOfKey(*playbackSessionID);
-    if (index < 0) {
-        return NULL;
+    return mClientInfo.mPlaybackSession;
+}
+
+void WifiDisplaySource::disconnectClient(status_t err) {
+    if (mClientSessionID != 0) {
+        if (mClientInfo.mPlaybackSession != NULL) {
+            looper()->unregisterHandler(mClientInfo.mPlaybackSession->id());
+            mClientInfo.mPlaybackSession.clear();
+        }
+
+        mNetSession->destroySession(mClientSessionID);
+        mClientSessionID = 0;
     }
 
-    return mPlaybackSessions.valueAt(index);
+    if (mClient != NULL) {
+        if (err != OK) {
+            mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
+        } else {
+            mClient->onDisplayDisconnected();
+        }
+    }
 }
 
 }  // namespace android
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index 99eb4f5..3c8d50f 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -22,6 +22,8 @@
 
 #include <media/stagefright/foundation/AHandler.h>
 
+#include <netinet/in.h>
+
 namespace android {
 
 struct IRemoteDisplayClient;
@@ -78,16 +80,19 @@
 
     sp<ANetworkSession> mNetSession;
     sp<IRemoteDisplayClient> mClient;
+    struct in_addr mInterfaceAddr;
     int32_t mSessionID;
 
+    int32_t mClientSessionID;
+
     struct ClientInfo {
         AString mRemoteIP;
         AString mLocalIP;
         int32_t mLocalPort;
         int32_t mPlaybackSessionID;
+        sp<PlaybackSession> mPlaybackSession;
     };
-    // by sessionID.
-    KeyedVector<int32_t, ClientInfo> mClientInfos;
+    ClientInfo mClientInfo;
 
     bool mReaperPending;
 
@@ -95,8 +100,6 @@
 
     KeyedVector<ResponseID, HandleRTSPResponseFunc> mResponseHandlers;
 
-    KeyedVector<int32_t, sp<PlaybackSession> > mPlaybackSessions;
-
     status_t sendM1(int32_t sessionID);
     status_t sendM3(int32_t sessionID);
     status_t sendM4(int32_t sessionID);
@@ -179,6 +182,12 @@
     sp<PlaybackSession> findPlaybackSession(
             const sp<ParsedMessage> &data, int32_t *playbackSessionID) const;
 
+    // Disconnects the current client and shuts down its playback session
+    // (if any). The reason for the disconnection is OK for orderly shutdown
+    // or a nonzero error code.
+    // A listener is notified accordingly.
+    void disconnectClient(status_t err);
+
     DISALLOW_EVIL_CONSTRUCTORS(WifiDisplaySource);
 };
 
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
index e24db0b..a7d19aa 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp
@@ -174,8 +174,7 @@
         SharedParameters::Lock l(client->getParameters());
         entry = frame.find(ANDROID_STATS_FACE_RECTANGLES);
         if (entry.count == 0) {
-            ALOGE("%s: Camera %d: Unable to read face rectangles",
-                    __FUNCTION__, client->getCameraId());
+            // No faces this frame
             return res;
         }
         metadata.number_of_faces = entry.count / 4;
@@ -220,7 +219,13 @@
 
         faces.setCapacity(metadata.number_of_faces);
 
-        for (int i = 0; i < metadata.number_of_faces; i++) {
+        size_t maxFaces = metadata.number_of_faces;
+        for (size_t i = 0; i < maxFaces; i++) {
+            if (faceScores[i] == 0) {
+                metadata.number_of_faces--;
+                continue;
+            }
+
             camera_face_t face;
 
             face.rect[0] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 0]);
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index c07a6ac..c6d7747 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -1380,8 +1380,11 @@
             &reqControlMode, 1);
     if (res != OK) return res;
 
-    uint8_t reqSceneMode = enableFaceDetect ?
-            (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY : sceneMode;
+    uint8_t reqSceneMode =
+            (sceneMode !=
+                    (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) ? sceneMode :
+            enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY :
+            (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED;
     res = request->update(ANDROID_CONTROL_SCENE_MODE,
             &reqSceneMode, 1);
     if (res != OK) return res;