Partial revert "Move unused classes out of stagefright foundataion"
This imports the old foundation code in the standard platform stagefright.
The foundation variant is used in VNDK, we can't change the ABI there.
This reverts commit 5ec3d6ac0c607e89d03ba5a9499e471d8559dc7e.
Change-Id: Iebcf5d89a768fdb830bea90fbf2c2427a4c3d139
Signed-off-by: DennySPb <dennyspb@gmail.com>
Signed-off-by: Luca Stefani <luca.stefani.ge1@gmail.com>
diff --git a/media/libmediaplayerservice/RemoteDisplay.cpp b/media/libmediaplayerservice/RemoteDisplay.cpp
index 0eb4b5d..80cca26 100644
--- a/media/libmediaplayerservice/RemoteDisplay.cpp
+++ b/media/libmediaplayerservice/RemoteDisplay.cpp
@@ -21,7 +21,7 @@
#include <media/IRemoteDisplayClient.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
+#include <media/stagefright/ANetworkSession.h>
namespace android {
diff --git a/media/libstagefright/ANetworkSession.cpp b/media/libstagefright/ANetworkSession.cpp
new file mode 100644
index 0000000..e3b77ec
--- /dev/null
+++ b/media/libstagefright/ANetworkSession.cpp
@@ -0,0 +1,1400 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NetworkSession"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <linux/tcp.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <media/stagefright/ANetworkSession.h>
+#include <media/stagefright/ParsedMessage.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ByteUtils.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+static const size_t kMaxUDPSize = 1500;
+static const int32_t kMaxUDPRetries = 200;
+
+struct ANetworkSession::NetworkThread : public Thread {
+ explicit NetworkThread(ANetworkSession *session);
+
+protected:
+ virtual ~NetworkThread();
+
+private:
+ ANetworkSession *mSession;
+
+ virtual bool threadLoop();
+
+ DISALLOW_EVIL_CONSTRUCTORS(NetworkThread);
+};
+
+struct ANetworkSession::Session : public RefBase {
+ enum Mode {
+ MODE_RTSP,
+ MODE_DATAGRAM,
+ MODE_WEBSOCKET,
+ };
+
+ enum State {
+ CONNECTING,
+ CONNECTED,
+ LISTENING_RTSP,
+ LISTENING_TCP_DGRAMS,
+ DATAGRAM,
+ };
+
+ Session(int32_t sessionID,
+ State state,
+ int s,
+ const sp<AMessage> ¬ify);
+
+ int32_t sessionID() const;
+ int socket() const;
+ sp<AMessage> getNotificationMessage() const;
+
+ bool isRTSPServer() const;
+ bool isTCPDatagramServer() const;
+
+ bool wantsToRead();
+ bool wantsToWrite();
+
+ status_t readMore();
+ status_t writeMore();
+
+ status_t sendRequest(
+ const void *data, ssize_t size, bool timeValid, int64_t timeUs);
+
+ void setMode(Mode mode);
+
+ status_t switchToWebSocketMode();
+
+protected:
+ virtual ~Session();
+
+private:
+ enum {
+ FRAGMENT_FLAG_TIME_VALID = 1,
+ };
+ struct Fragment {
+ uint32_t mFlags;
+ int64_t mTimeUs;
+ sp<ABuffer> mBuffer;
+ };
+
+ int32_t mSessionID;
+ State mState;
+ Mode mMode;
+ int mSocket;
+ sp<AMessage> mNotify;
+ bool mSawReceiveFailure, mSawSendFailure;
+ int32_t mUDPRetries;
+
+ List<Fragment> mOutFragments;
+
+ AString mInBuffer;
+
+ int64_t mLastStallReportUs;
+
+ void notifyError(bool send, status_t err, const char *detail);
+ void notify(NotificationReason reason);
+
+ void dumpFragmentStats(const Fragment &frag);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Session);
+};
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::NetworkThread::NetworkThread(ANetworkSession *session)
+ : mSession(session) {
+}
+
+ANetworkSession::NetworkThread::~NetworkThread() {
+}
+
+bool ANetworkSession::NetworkThread::threadLoop() {
+ mSession->threadLoop();
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::Session::Session(
+ int32_t sessionID,
+ State state,
+ int s,
+ const sp<AMessage> ¬ify)
+ : mSessionID(sessionID),
+ mState(state),
+ mMode(MODE_DATAGRAM),
+ mSocket(s),
+ mNotify(notify),
+ mSawReceiveFailure(false),
+ mSawSendFailure(false),
+ mUDPRetries(kMaxUDPRetries),
+ mLastStallReportUs(-1ll) {
+ if (mState == CONNECTED) {
+ struct sockaddr_in localAddr;
+ socklen_t localAddrLen = sizeof(localAddr);
+
+ int res = getsockname(
+ mSocket, (struct sockaddr *)&localAddr, &localAddrLen);
+ CHECK_GE(res, 0);
+
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ res = getpeername(
+ mSocket, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+ CHECK_GE(res, 0);
+
+ in_addr_t addr = ntohl(localAddr.sin_addr.s_addr);
+ AString localAddrString = AStringPrintf(
+ "%d.%d.%d.%d",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff);
+
+ addr = ntohl(remoteAddr.sin_addr.s_addr);
+ AString remoteAddrString = AStringPrintf(
+ "%d.%d.%d.%d",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff);
+
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatClientConnected);
+ msg->setString("server-ip", localAddrString.c_str());
+ msg->setInt32("server-port", ntohs(localAddr.sin_port));
+ msg->setString("client-ip", remoteAddrString.c_str());
+ msg->setInt32("client-port", ntohs(remoteAddr.sin_port));
+ msg->post();
+ }
+}
+
+ANetworkSession::Session::~Session() {
+ ALOGV("Session %d gone", mSessionID);
+
+ close(mSocket);
+ mSocket = -1;
+}
+
+int32_t ANetworkSession::Session::sessionID() const {
+ return mSessionID;
+}
+
+int ANetworkSession::Session::socket() const {
+ return mSocket;
+}
+
+void ANetworkSession::Session::setMode(Mode mode) {
+ mMode = mode;
+}
+
+status_t ANetworkSession::Session::switchToWebSocketMode() {
+ if (mState != CONNECTED || mMode != MODE_RTSP) {
+ return INVALID_OPERATION;
+ }
+
+ mMode = MODE_WEBSOCKET;
+
+ return OK;
+}
+
+sp<AMessage> ANetworkSession::Session::getNotificationMessage() const {
+ return mNotify;
+}
+
+bool ANetworkSession::Session::isRTSPServer() const {
+ return mState == LISTENING_RTSP;
+}
+
+bool ANetworkSession::Session::isTCPDatagramServer() const {
+ return mState == LISTENING_TCP_DGRAMS;
+}
+
+bool ANetworkSession::Session::wantsToRead() {
+ return !mSawReceiveFailure && mState != CONNECTING;
+}
+
+bool ANetworkSession::Session::wantsToWrite() {
+ return !mSawSendFailure
+ && (mState == CONNECTING
+ || (mState == CONNECTED && !mOutFragments.empty())
+ || (mState == DATAGRAM && !mOutFragments.empty()));
+}
+
+status_t ANetworkSession::Session::readMore() {
+ if (mState == DATAGRAM) {
+ CHECK_EQ(mMode, MODE_DATAGRAM);
+
+ status_t err;
+ do {
+ sp<ABuffer> buf = new ABuffer(kMaxUDPSize);
+
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ ssize_t n;
+ do {
+ n = recvfrom(
+ mSocket, buf->data(), buf->capacity(), 0,
+ (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+ } while (n < 0 && errno == EINTR);
+
+ err = OK;
+ if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ } else {
+ buf->setRange(0, n);
+
+ int64_t nowUs = ALooper::GetNowUs();
+ buf->meta()->setInt64("arrivalTimeUs", nowUs);
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatDatagram);
+
+ uint32_t ip = ntohl(remoteAddr.sin_addr.s_addr);
+ notify->setString(
+ "fromAddr",
+ AStringPrintf(
+ "%u.%u.%u.%u",
+ ip >> 24,
+ (ip >> 16) & 0xff,
+ (ip >> 8) & 0xff,
+ ip & 0xff).c_str());
+
+ notify->setInt32("fromPort", ntohs(remoteAddr.sin_port));
+
+ notify->setBuffer("data", buf);
+ notify->post();
+ }
+ } while (err == OK);
+
+ if (err == -EAGAIN) {
+ err = OK;
+ }
+
+ if (err != OK) {
+ if (!mUDPRetries) {
+ notifyError(false /* send */, err, "Recvfrom failed.");
+ mSawReceiveFailure = true;
+ } else {
+ mUDPRetries--;
+ ALOGE("Recvfrom failed, %d/%d retries left",
+ mUDPRetries, kMaxUDPRetries);
+ err = OK;
+ }
+ } else {
+ mUDPRetries = kMaxUDPRetries;
+ }
+
+ return err;
+ }
+
+ char tmp[512];
+ ssize_t n;
+ do {
+ n = recv(mSocket, tmp, sizeof(tmp), 0);
+ } while (n < 0 && errno == EINTR);
+
+ status_t err = OK;
+
+ if (n > 0) {
+ mInBuffer.append(tmp, n);
+
+#if 0
+ ALOGI("in:");
+ hexdump(tmp, n);
+#endif
+ } else if (n < 0) {
+ err = -errno;
+ } else {
+ err = -ECONNRESET;
+ }
+
+ if (mMode == MODE_DATAGRAM) {
+ // TCP stream carrying 16-bit length-prefixed datagrams.
+
+ while (mInBuffer.size() >= 2) {
+ size_t packetSize = U16_AT((const uint8_t *)mInBuffer.c_str());
+
+ if (mInBuffer.size() < packetSize + 2) {
+ break;
+ }
+
+ sp<ABuffer> packet = new ABuffer(packetSize);
+ memcpy(packet->data(), mInBuffer.c_str() + 2, packetSize);
+
+ int64_t nowUs = ALooper::GetNowUs();
+ packet->meta()->setInt64("arrivalTimeUs", nowUs);
+
+ 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 if (mMode == MODE_RTSP) {
+ 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", 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;
+ }
+#endif
+
+ mInBuffer.erase(0, length);
+
+ if (err != OK) {
+ break;
+ }
+ }
+ } else {
+ CHECK_EQ(mMode, MODE_WEBSOCKET);
+
+ const uint8_t *data = (const uint8_t *)mInBuffer.c_str();
+ // hexdump(data, mInBuffer.size());
+
+ while (mInBuffer.size() >= 2) {
+ size_t offset = 2;
+
+ uint64_t payloadLen = data[1] & 0x7f;
+ if (payloadLen == 126) {
+ if (offset + 2 > mInBuffer.size()) {
+ break;
+ }
+
+ payloadLen = U16_AT(&data[offset]);
+ offset += 2;
+ } else if (payloadLen == 127) {
+ if (offset + 8 > mInBuffer.size()) {
+ break;
+ }
+
+ payloadLen = U64_AT(&data[offset]);
+ offset += 8;
+ }
+
+ uint32_t mask = 0;
+ if (data[1] & 0x80) {
+ // MASK==1
+ if (offset + 4 > mInBuffer.size()) {
+ break;
+ }
+
+ mask = U32_AT(&data[offset]);
+ offset += 4;
+ }
+
+ if (payloadLen > mInBuffer.size() || offset > mInBuffer.size() - payloadLen) {
+ break;
+ }
+
+ // We have the full message.
+
+ sp<ABuffer> packet = new ABuffer(payloadLen);
+ memcpy(packet->data(), &data[offset], payloadLen);
+
+ if (mask != 0) {
+ for (size_t i = 0; i < payloadLen; ++i) {
+ packet->data()[i] =
+ data[offset + i]
+ ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+ }
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("sessionID", mSessionID);
+ notify->setInt32("reason", kWhatWebSocketMessage);
+ notify->setBuffer("data", packet);
+ notify->setInt32("headerByte", data[0]);
+ notify->post();
+
+ mInBuffer.erase(0, offset + payloadLen);
+ }
+ }
+
+ if (err != OK) {
+ notifyError(false /* send */, err, "Recv failed.");
+ mSawReceiveFailure = true;
+ }
+
+ return err;
+}
+
+void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
+#if 0
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
+
+ static const int64_t kMinDelayMs = 0;
+ static const int64_t kMaxDelayMs = 300;
+
+ const char *kPattern = "########################################";
+ size_t kPatternSize = strlen(kPattern);
+
+ int n = (kPatternSize * (delayMs - kMinDelayMs))
+ / (kMaxDelayMs - kMinDelayMs);
+
+ if (n < 0) {
+ n = 0;
+ } else if ((size_t)n > kPatternSize) {
+ n = kPatternSize;
+ }
+
+ ALOGI("[%lld]: (%4lld ms) %s\n",
+ frag.mTimeUs / 1000,
+ delayMs,
+ kPattern + kPatternSize - n);
+#endif
+}
+
+status_t ANetworkSession::Session::writeMore() {
+ if (mState == DATAGRAM) {
+ CHECK(!mOutFragments.empty());
+
+ status_t err;
+ do {
+ const Fragment &frag = *mOutFragments.begin();
+ const sp<ABuffer> &datagram = frag.mBuffer;
+
+ int n;
+ do {
+ n = send(mSocket, datagram->data(), datagram->size(), 0);
+ } while (n < 0 && errno == EINTR);
+
+ err = OK;
+
+ if (n > 0) {
+ if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+ dumpFragmentStats(frag);
+ }
+
+ mOutFragments.erase(mOutFragments.begin());
+ } else if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ }
+ } while (err == OK && !mOutFragments.empty());
+
+ if (err == -EAGAIN) {
+ if (!mOutFragments.empty()) {
+ ALOGI("%zu datagrams remain queued.", mOutFragments.size());
+ }
+ err = OK;
+ }
+
+ if (err != OK) {
+ if (!mUDPRetries) {
+ notifyError(true /* send */, err, "Send datagram failed.");
+ mSawSendFailure = true;
+ } else {
+ mUDPRetries--;
+ ALOGE("Send datagram failed, %d/%d retries left",
+ mUDPRetries, kMaxUDPRetries);
+ err = OK;
+ }
+ } else {
+ mUDPRetries = kMaxUDPRetries;
+ }
+
+ return err;
+ }
+
+ if (mState == CONNECTING) {
+ int err;
+ socklen_t optionLen = sizeof(err);
+ CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
+ CHECK_EQ(optionLen, (socklen_t)sizeof(err));
+
+ if (err != 0) {
+ notifyError(kWhatError, -err, "Connection failed");
+ mSawSendFailure = true;
+
+ return -err;
+ }
+
+ mState = CONNECTED;
+ notify(kWhatConnected);
+
+ return OK;
+ }
+
+ CHECK_EQ(mState, CONNECTED);
+ CHECK(!mOutFragments.empty());
+
+ ssize_t n = -1;
+ while (!mOutFragments.empty()) {
+ const Fragment &frag = *mOutFragments.begin();
+
+ do {
+ n = send(mSocket, frag.mBuffer->data(), frag.mBuffer->size(), 0);
+ } while (n < 0 && errno == EINTR);
+
+ if (n <= 0) {
+ break;
+ }
+
+ frag.mBuffer->setRange(
+ frag.mBuffer->offset() + n, frag.mBuffer->size() - n);
+
+ if (frag.mBuffer->size() > 0) {
+ break;
+ }
+
+ if (frag.mFlags & FRAGMENT_FLAG_TIME_VALID) {
+ dumpFragmentStats(frag);
+ }
+
+ mOutFragments.erase(mOutFragments.begin());
+ }
+
+ status_t err = OK;
+
+ if (n < 0) {
+ err = -errno;
+ } else if (n == 0) {
+ err = -ECONNRESET;
+ }
+
+ if (err != OK) {
+ notifyError(true /* send */, err, "Send failed.");
+ mSawSendFailure = true;
+ }
+
+#if 0
+ int numBytesQueued;
+ int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
+ if (res == 0 && numBytesQueued > 50 * 1024) {
+ if (numBytesQueued > 409600) {
+ ALOGW("!!! numBytesQueued = %d", numBytesQueued);
+ }
+
+ int64_t nowUs = ALooper::GetNowUs();
+
+ if (mLastStallReportUs < 0ll
+ || nowUs > mLastStallReportUs + 100000ll) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatNetworkStall);
+ msg->setSize("numBytesQueued", numBytesQueued);
+ msg->post();
+
+ mLastStallReportUs = nowUs;
+ }
+ }
+#endif
+
+ return err;
+}
+
+status_t ANetworkSession::Session::sendRequest(
+ const void *data, ssize_t size, bool timeValid, int64_t timeUs) {
+ CHECK(mState == CONNECTED || mState == DATAGRAM);
+
+ if (size < 0) {
+ size = strlen((const char *)data);
+ }
+
+ if (size == 0) {
+ return OK;
+ }
+
+ sp<ABuffer> buffer;
+
+ if (mState == CONNECTED && mMode == MODE_DATAGRAM) {
+ CHECK_LE(size, 65535);
+
+ buffer = new ABuffer(size + 2);
+ buffer->data()[0] = size >> 8;
+ buffer->data()[1] = size & 0xff;
+ memcpy(buffer->data() + 2, data, size);
+ } else if (mState == CONNECTED && mMode == MODE_WEBSOCKET) {
+ static const bool kUseMask = false; // Chromium doesn't like it.
+
+ size_t numHeaderBytes = 2 + (kUseMask ? 4 : 0);
+ if (size > 65535) {
+ numHeaderBytes += 8;
+ } else if (size > 125) {
+ numHeaderBytes += 2;
+ }
+
+ buffer = new ABuffer(numHeaderBytes + size);
+ buffer->data()[0] = 0x81; // FIN==1 | opcode=1 (text)
+ buffer->data()[1] = kUseMask ? 0x80 : 0x00;
+
+ if (size > 65535) {
+ buffer->data()[1] |= 127;
+ buffer->data()[2] = 0x00;
+ buffer->data()[3] = 0x00;
+ buffer->data()[4] = 0x00;
+ buffer->data()[5] = 0x00;
+ buffer->data()[6] = (size >> 24) & 0xff;
+ buffer->data()[7] = (size >> 16) & 0xff;
+ buffer->data()[8] = (size >> 8) & 0xff;
+ buffer->data()[9] = size & 0xff;
+ } else if (size > 125) {
+ buffer->data()[1] |= 126;
+ buffer->data()[2] = (size >> 8) & 0xff;
+ buffer->data()[3] = size & 0xff;
+ } else {
+ buffer->data()[1] |= size;
+ }
+
+ if (kUseMask) {
+ uint32_t mask = rand();
+
+ buffer->data()[numHeaderBytes - 4] = (mask >> 24) & 0xff;
+ buffer->data()[numHeaderBytes - 3] = (mask >> 16) & 0xff;
+ buffer->data()[numHeaderBytes - 2] = (mask >> 8) & 0xff;
+ buffer->data()[numHeaderBytes - 1] = mask & 0xff;
+
+ for (size_t i = 0; i < (size_t)size; ++i) {
+ buffer->data()[numHeaderBytes + i] =
+ ((const uint8_t *)data)[i]
+ ^ ((mask >> (8 * (3 - (i % 4)))) & 0xff);
+ }
+ } else {
+ memcpy(buffer->data() + numHeaderBytes, data, size);
+ }
+ } else {
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ }
+
+ Fragment frag;
+
+ frag.mFlags = 0;
+ if (timeValid) {
+ frag.mFlags = FRAGMENT_FLAG_TIME_VALID;
+ frag.mTimeUs = timeUs;
+ }
+
+ frag.mBuffer = buffer;
+
+ mOutFragments.push_back(frag);
+
+ return OK;
+}
+
+void ANetworkSession::Session::notifyError(
+ bool send, status_t err, const char *detail) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", kWhatError);
+ msg->setInt32("send", send);
+ msg->setInt32("err", err);
+ msg->setString("detail", detail);
+ msg->post();
+}
+
+void ANetworkSession::Session::notify(NotificationReason reason) {
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("sessionID", mSessionID);
+ msg->setInt32("reason", reason);
+ msg->post();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ANetworkSession::ANetworkSession()
+ : mNextSessionID(1) {
+ mPipeFd[0] = mPipeFd[1] = -1;
+}
+
+ANetworkSession::~ANetworkSession() {
+ stop();
+}
+
+status_t ANetworkSession::start() {
+ if (mThread != NULL) {
+ return INVALID_OPERATION;
+ }
+
+ int res = pipe(mPipeFd);
+ if (res != 0) {
+ mPipeFd[0] = mPipeFd[1] = -1;
+ return -errno;
+ }
+
+ mThread = new NetworkThread(this);
+
+ status_t err = mThread->run("ANetworkSession", ANDROID_PRIORITY_AUDIO);
+
+ if (err != OK) {
+ mThread.clear();
+
+ close(mPipeFd[0]);
+ close(mPipeFd[1]);
+ mPipeFd[0] = mPipeFd[1] = -1;
+
+ return err;
+ }
+
+ return OK;
+}
+
+status_t ANetworkSession::stop() {
+ if (mThread == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ mThread->requestExit();
+ interrupt();
+ mThread->requestExitAndWait();
+
+ mThread.clear();
+
+ close(mPipeFd[0]);
+ close(mPipeFd[1]);
+ mPipeFd[0] = mPipeFd[1] = -1;
+
+ return OK;
+}
+
+status_t ANetworkSession::createRTSPClient(
+ const char *host, unsigned port, const sp<AMessage> ¬ify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateRTSPClient,
+ NULL /* addr */,
+ 0 /* port */,
+ host,
+ port,
+ notify,
+ sessionID);
+}
+
+status_t ANetworkSession::createRTSPServer(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> ¬ify, int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateRTSPServer,
+ &addr,
+ port,
+ NULL /* remoteHost */,
+ 0 /* remotePort */,
+ notify,
+ sessionID);
+}
+
+status_t ANetworkSession::createUDPSession(
+ unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID) {
+ return createUDPSession(localPort, NULL, 0, notify, sessionID);
+}
+
+status_t ANetworkSession::createUDPSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> ¬ify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateUDPSession,
+ NULL /* addr */,
+ localPort,
+ remoteHost,
+ remotePort,
+ notify,
+ sessionID);
+}
+
+status_t ANetworkSession::createTCPDatagramSession(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> ¬ify, 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> ¬ify,
+ int32_t *sessionID) {
+ return createClientOrServer(
+ kModeCreateTCPDatagramSessionActive,
+ NULL /* addr */,
+ localPort,
+ remoteHost,
+ remotePort,
+ notify,
+ sessionID);
+}
+
+status_t ANetworkSession::destroySession(int32_t sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ mSessions.removeItemsAt(index);
+
+ interrupt();
+
+ return OK;
+}
+
+// static
+status_t ANetworkSession::MakeSocketNonBlocking(int s) {
+ int flags = fcntl(s, F_GETFL, 0);
+ if (flags < 0) {
+ flags = 0;
+ }
+
+ int res = fcntl(s, F_SETFL, flags | O_NONBLOCK);
+ if (res < 0) {
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t ANetworkSession::createClientOrServer(
+ Mode mode,
+ const struct in_addr *localAddr,
+ unsigned port,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> ¬ify,
+ int32_t *sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ *sessionID = 0;
+ status_t err = OK;
+ int s, res;
+ sp<Session> session;
+
+ s = socket(
+ AF_INET,
+ (mode == kModeCreateUDPSession) ? SOCK_DGRAM : SOCK_STREAM,
+ 0);
+
+ if (s < 0) {
+ err = -errno;
+ goto bail;
+ }
+
+ if (mode == kModeCreateRTSPServer
+ || mode == kModeCreateTCPDatagramSessionPassive) {
+ const int yes = 1;
+ res = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ }
+
+ if (mode == kModeCreateUDPSession) {
+ int size = 256 * 1024;
+
+ res = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ res = setsockopt(s, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ } else if (mode == kModeCreateTCPDatagramSessionActive) {
+ int flag = 1;
+ res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ int tos = 224; // VOICE
+ res = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+ }
+
+ err = MakeSocketNonBlocking(s);
+
+ if (err != OK) {
+ goto bail2;
+ }
+
+ struct sockaddr_in addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+ addr.sin_family = AF_INET;
+
+ if (mode == kModeCreateRTSPClient
+ || mode == kModeCreateTCPDatagramSessionActive) {
+ struct hostent *ent= gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ goto bail2;
+ }
+
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ addr.sin_port = htons(remotePort);
+ } else if (localAddr != NULL) {
+ addr.sin_addr = *localAddr;
+ addr.sin_port = htons(port);
+ } else {
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(port);
+ }
+
+ 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);
+ if (errno == EINPROGRESS) {
+ res = 0;
+ }
+ } else {
+ res = bind(s, (const struct sockaddr *)&addr, sizeof(addr));
+
+ if (res == 0) {
+ if (mode == kModeCreateRTSPServer
+ || mode == kModeCreateTCPDatagramSessionPassive) {
+ res = listen(s, 4);
+ } else {
+ CHECK_EQ(mode, kModeCreateUDPSession);
+
+ if (remoteHost != NULL) {
+ struct sockaddr_in remoteAddr;
+ memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+ remoteAddr.sin_family = AF_INET;
+ remoteAddr.sin_port = htons(remotePort);
+
+ struct hostent *ent= gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ goto bail2;
+ }
+
+ remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+ res = connect(
+ s,
+ (const struct sockaddr *)&remoteAddr,
+ sizeof(remoteAddr));
+ }
+ }
+ }
+ }
+
+ if (res < 0) {
+ err = -errno;
+ goto bail2;
+ }
+
+ Session::State state;
+ switch (mode) {
+ case kModeCreateRTSPClient:
+ state = Session::CONNECTING;
+ break;
+
+ case kModeCreateTCPDatagramSessionActive:
+ state = Session::CONNECTING;
+ break;
+
+ case kModeCreateTCPDatagramSessionPassive:
+ state = Session::LISTENING_TCP_DGRAMS;
+ break;
+
+ case kModeCreateRTSPServer:
+ state = Session::LISTENING_RTSP;
+ break;
+
+ default:
+ CHECK_EQ(mode, kModeCreateUDPSession);
+ state = Session::DATAGRAM;
+ break;
+ }
+
+ session = new Session(
+ mNextSessionID++,
+ state,
+ s,
+ notify);
+
+ if (mode == kModeCreateTCPDatagramSessionActive) {
+ session->setMode(Session::MODE_DATAGRAM);
+ } else if (mode == kModeCreateRTSPClient) {
+ session->setMode(Session::MODE_RTSP);
+ }
+
+ mSessions.add(session->sessionID(), session);
+
+ interrupt();
+
+ *sessionID = session->sessionID();
+
+ goto bail;
+
+bail2:
+ close(s);
+ s = -1;
+
+bail:
+ return err;
+}
+
+status_t ANetworkSession::connectUDPSession(
+ int32_t sessionID, const char *remoteHost, unsigned remotePort) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+ int s = session->socket();
+
+ struct sockaddr_in remoteAddr;
+ memset(remoteAddr.sin_zero, 0, sizeof(remoteAddr.sin_zero));
+ remoteAddr.sin_family = AF_INET;
+ remoteAddr.sin_port = htons(remotePort);
+
+ status_t err = OK;
+ struct hostent *ent = gethostbyname(remoteHost);
+ if (ent == NULL) {
+ err = -h_errno;
+ } else {
+ remoteAddr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+
+ int res = connect(
+ s,
+ (const struct sockaddr *)&remoteAddr,
+ sizeof(remoteAddr));
+
+ if (res < 0) {
+ err = -errno;
+ }
+ }
+
+ return err;
+}
+
+status_t ANetworkSession::sendRequest(
+ int32_t sessionID, const void *data, ssize_t size,
+ bool timeValid, int64_t timeUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+
+ status_t err = session->sendRequest(data, size, timeValid, timeUs);
+
+ interrupt();
+
+ return err;
+}
+
+status_t ANetworkSession::switchToWebSocketMode(int32_t sessionID) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mSessions.indexOfKey(sessionID);
+
+ if (index < 0) {
+ return -ENOENT;
+ }
+
+ const sp<Session> session = mSessions.valueAt(index);
+ return session->switchToWebSocketMode();
+}
+
+void ANetworkSession::interrupt() {
+ static const char dummy = 0;
+
+ ssize_t n;
+ do {
+ n = write(mPipeFd[1], &dummy, 1);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) {
+ ALOGW("Error writing to pipe (%s)", strerror(errno));
+ }
+}
+
+void ANetworkSession::threadLoop() {
+ fd_set rs, ws;
+ FD_ZERO(&rs);
+ FD_ZERO(&ws);
+
+ FD_SET(mPipeFd[0], &rs);
+ int maxFd = mPipeFd[0];
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ for (size_t i = 0; i < mSessions.size(); ++i) {
+ const sp<Session> &session = mSessions.valueAt(i);
+
+ int s = session->socket();
+
+ if (s < 0) {
+ continue;
+ }
+
+ if (session->wantsToRead()) {
+ FD_SET(s, &rs);
+ if (s > maxFd) {
+ maxFd = s;
+ }
+ }
+
+ if (session->wantsToWrite()) {
+ FD_SET(s, &ws);
+ if (s > maxFd) {
+ maxFd = s;
+ }
+ }
+ }
+ }
+
+ int res = select(maxFd + 1, &rs, &ws, NULL, NULL /* tv */);
+
+ if (res == 0) {
+ return;
+ }
+
+ if (res < 0) {
+ if (errno == EINTR) {
+ return;
+ }
+
+ ALOGE("select failed w/ error %d (%s)", errno, strerror(errno));
+ return;
+ }
+
+ if (FD_ISSET(mPipeFd[0], &rs)) {
+ char c;
+ ssize_t n;
+ do {
+ n = read(mPipeFd[0], &c, 1);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) {
+ ALOGW("Error reading from pipe (%s)", strerror(errno));
+ }
+
+ --res;
+ }
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ List<sp<Session> > sessionsToAdd;
+
+ for (size_t i = mSessions.size(); res > 0 && i > 0;) {
+ i--;
+ const sp<Session> &session = mSessions.valueAt(i);
+
+ int s = session->socket();
+
+ if (s < 0) {
+ continue;
+ }
+
+ if (FD_ISSET(s, &rs) || FD_ISSET(s, &ws)) {
+ --res;
+ }
+
+ if (FD_ISSET(s, &rs)) {
+ if (session->isRTSPServer() || session->isTCPDatagramServer()) {
+ struct sockaddr_in remoteAddr;
+ socklen_t remoteAddrLen = sizeof(remoteAddr);
+
+ int clientSocket = accept(
+ s, (struct sockaddr *)&remoteAddr, &remoteAddrLen);
+
+ if (clientSocket >= 0) {
+ status_t err = MakeSocketNonBlocking(clientSocket);
+
+ if (err != OK) {
+ ALOGE("Unable to make client socket non blocking, "
+ "failed w/ error %d (%s)",
+ err, strerror(-err));
+
+ close(clientSocket);
+ clientSocket = -1;
+ } else {
+ in_addr_t addr = ntohl(remoteAddr.sin_addr.s_addr);
+
+ ALOGI("incoming connection from %d.%d.%d.%d:%d "
+ "(socket %d)",
+ (addr >> 24),
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ addr & 0xff,
+ ntohs(remoteAddr.sin_port),
+ clientSocket);
+
+ sp<Session> clientSession =
+ new Session(
+ mNextSessionID++,
+ Session::CONNECTED,
+ clientSocket,
+ session->getNotificationMessage());
+
+ clientSession->setMode(
+ session->isRTSPServer()
+ ? Session::MODE_RTSP
+ : Session::MODE_DATAGRAM);
+
+ sessionsToAdd.push_back(clientSession);
+ }
+ } else {
+ ALOGE("accept returned error %d (%s)",
+ errno, strerror(errno));
+ }
+ } else {
+ status_t err = session->readMore();
+ if (err != OK) {
+ ALOGE("readMore on socket %d failed w/ error %d (%s)",
+ s, err, strerror(-err));
+ }
+ }
+ }
+
+ if (FD_ISSET(s, &ws)) {
+ status_t err = session->writeMore();
+ if (err != OK) {
+ ALOGE("writeMore on socket %d failed w/ error %d (%s)",
+ s, err, strerror(-err));
+ }
+ }
+ }
+
+ while (!sessionsToAdd.empty()) {
+ sp<Session> session = *sessionsToAdd.begin();
+ sessionsToAdd.erase(sessionsToAdd.begin());
+
+ mSessions.add(session->sessionID(), session);
+
+ ALOGI("added clientSession %d", session->sessionID());
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index a63d16a..67ab188 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -232,6 +232,7 @@
"ACodecBufferChannel.cpp",
"AHierarchicalStateMachine.cpp",
"AMRWriter.cpp",
+ "ANetworkSession.cpp",
"AudioSource.cpp",
"BufferImpl.cpp",
"CallbackDataSource.cpp",
@@ -263,6 +264,7 @@
"OggWriter.cpp",
"OMXClient.cpp",
"OmxInfoBuilder.cpp",
+ "ParsedMessage.cpp",
"RemoteMediaExtractor.cpp",
"RemoteMediaSource.cpp",
"SimpleDecodingSource.cpp",
diff --git a/media/libstagefright/ParsedMessage.cpp b/media/libstagefright/ParsedMessage.cpp
new file mode 100644
index 0000000..4bfc454
--- /dev/null
+++ b/media/libstagefright/ParsedMessage.cpp
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <media/stagefright/ParsedMessage.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+namespace android {
+
+// static
+sp<ParsedMessage> ParsedMessage::Parse(
+ const char *data, size_t size, bool noMoreData, size_t *length) {
+ sp<ParsedMessage> msg = new ParsedMessage;
+ ssize_t res = msg->parse(data, size, noMoreData);
+
+ if (res < 0) {
+ *length = 0;
+ return NULL;
+ }
+
+ *length = res;
+ return msg;
+}
+
+ParsedMessage::ParsedMessage() {
+}
+
+ParsedMessage::~ParsedMessage() {
+}
+
+bool ParsedMessage::findString(const char *name, AString *value) const {
+ AString key = name;
+ key.tolower();
+
+ ssize_t index = mDict.indexOfKey(key);
+
+ if (index < 0) {
+ value->clear();
+
+ return false;
+ }
+
+ *value = mDict.valueAt(index);
+ return true;
+}
+
+bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
+ AString stringValue;
+
+ if (!findString(name, &stringValue)) {
+ return false;
+ }
+
+ char *end;
+ *value = strtol(stringValue.c_str(), &end, 10);
+
+ if (end == stringValue.c_str() || *end != '\0') {
+ *value = 0;
+ return false;
+ }
+
+ return true;
+}
+
+const char *ParsedMessage::getContent() const {
+ return mContent.c_str();
+}
+
+ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
+ if (size == 0) {
+ return -1;
+ }
+
+ ssize_t lastDictIndex = -1;
+
+ size_t offset = 0;
+ bool headersComplete = false;
+ while (offset < size) {
+ size_t lineEndOffset = offset;
+ while (lineEndOffset + 1 < size
+ && (data[lineEndOffset] != '\r'
+ || data[lineEndOffset + 1] != '\n')) {
+ ++lineEndOffset;
+ }
+
+ if (lineEndOffset + 1 >= size) {
+ return -1;
+ }
+
+ AString line(&data[offset], lineEndOffset - offset);
+
+ if (offset == 0) {
+ // Special handling for the request/status line.
+
+ mDict.add(AString("_"), line);
+ offset = lineEndOffset + 2;
+
+ continue;
+ }
+
+ if (lineEndOffset == offset) {
+ // An empty line separates headers from body.
+ headersComplete = true;
+ offset += 2;
+ break;
+ }
+
+ if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
+ // Support for folded header values.
+
+ if (lastDictIndex >= 0) {
+ // Otherwise it's malformed since the first header line
+ // cannot continue anything...
+
+ AString &value = mDict.editValueAt(lastDictIndex);
+ value.append(line);
+ }
+
+ offset = lineEndOffset + 2;
+ continue;
+ }
+
+ ssize_t colonPos = line.find(":");
+ if (colonPos >= 0) {
+ AString key(line, 0, colonPos);
+ key.trim();
+ key.tolower();
+
+ line.erase(0, colonPos + 1);
+
+ lastDictIndex = mDict.add(key, line);
+ }
+
+ offset = lineEndOffset + 2;
+ }
+
+ if (!headersComplete && (!noMoreData || offset == 0)) {
+ // We either saw the empty line separating headers from body
+ // or we saw at least the status line and know that no more data
+ // is going to follow.
+ return -1;
+ }
+
+ for (size_t i = 0; i < mDict.size(); ++i) {
+ mDict.editValueAt(i).trim();
+ }
+
+ int32_t contentLength;
+ if (!findInt32("content-length", &contentLength) || contentLength < 0) {
+ contentLength = 0;
+ }
+
+ size_t totalLength = offset + contentLength;
+
+ if (size < totalLength) {
+ return -1;
+ }
+
+ mContent.setTo(&data[offset], contentLength);
+
+ return totalLength;
+}
+
+bool ParsedMessage::getRequestField(size_t index, AString *field) const {
+ AString line;
+ CHECK(findString("_", &line));
+
+ size_t prevOffset = 0;
+ size_t offset = 0;
+ for (size_t i = 0; i <= index; ++i) {
+ if (offset >= line.size()) {
+ return false;
+ }
+
+ ssize_t spacePos = line.find(" ", offset);
+
+ if (spacePos < 0) {
+ spacePos = line.size();
+ }
+
+ prevOffset = offset;
+ offset = spacePos + 1;
+ }
+
+ field->setTo(line, prevOffset, offset - prevOffset - 1);
+
+ return true;
+}
+
+bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
+ AString statusCodeString;
+ if (!getRequestField(1, &statusCodeString)) {
+ *statusCode = 0;
+ return false;
+ }
+
+ char *end;
+ *statusCode = strtol(statusCodeString.c_str(), &end, 10);
+
+ if (*end != '\0' || end == statusCodeString.c_str()
+ || (*statusCode) < 100 || (*statusCode) > 999) {
+ *statusCode = 0;
+ return false;
+ }
+
+ return true;
+}
+
+AString ParsedMessage::debugString() const {
+ AString line;
+ CHECK(findString("_", &line));
+
+ line.append("\n");
+
+ for (size_t i = 0; i < mDict.size(); ++i) {
+ const AString &key = mDict.keyAt(i);
+ const AString &value = mDict.valueAt(i);
+
+ if (key == AString("_")) {
+ continue;
+ }
+
+ line.append(key);
+ line.append(": ");
+ line.append(value);
+ line.append("\n");
+ }
+
+ line.append("\n");
+ line.append(mContent);
+
+ return line;
+}
+
+// static
+bool ParsedMessage::GetAttribute(
+ const char *s, const char *key, AString *value) {
+ value->clear();
+
+ size_t keyLen = strlen(key);
+
+ for (;;) {
+ while (isspace(*s)) {
+ ++s;
+ }
+
+ const char *colonPos = strchr(s, ';');
+
+ size_t len =
+ (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+ if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+ value->setTo(&s[keyLen + 1], len - keyLen - 1);
+ return true;
+ }
+
+ if (colonPos == NULL) {
+ return false;
+ }
+
+ s = colonPos + 1;
+ }
+}
+
+// static
+bool ParsedMessage::GetInt32Attribute(
+ const char *s, const char *key, int32_t *value) {
+ AString stringValue;
+ if (!GetAttribute(s, key, &stringValue)) {
+ *value = 0;
+ return false;
+ }
+
+ char *end;
+ *value = strtol(stringValue.c_str(), &end, 10);
+
+ if (end == stringValue.c_str() || *end != '\0') {
+ *value = 0;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/include/media/stagefright/ANetworkSession.h b/media/libstagefright/include/media/stagefright/ANetworkSession.h
new file mode 100644
index 0000000..fd3ebaa
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/ANetworkSession.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef A_NETWORK_SESSION_H_
+
+#define A_NETWORK_SESSION_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+#include <utils/Thread.h>
+
+#include <netinet/in.h>
+
+namespace android {
+
+struct AMessage;
+
+// Helper class to manage a number of live sockets (datagram and stream-based)
+// on a single thread. Clients are notified about activity through AMessages.
+struct ANetworkSession : public RefBase {
+ ANetworkSession();
+
+ status_t start();
+ status_t stop();
+
+ status_t createRTSPClient(
+ const char *host, unsigned port, const sp<AMessage> ¬ify,
+ int32_t *sessionID);
+
+ status_t createRTSPServer(
+ const struct in_addr &addr, unsigned port,
+ const sp<AMessage> ¬ify, int32_t *sessionID);
+
+ status_t createUDPSession(
+ unsigned localPort, const sp<AMessage> ¬ify, int32_t *sessionID);
+
+ status_t createUDPSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> ¬ify,
+ int32_t *sessionID);
+
+ 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> ¬ify, int32_t *sessionID);
+
+ // active
+ status_t createTCPDatagramSession(
+ unsigned localPort,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> ¬ify,
+ int32_t *sessionID);
+
+ status_t destroySession(int32_t sessionID);
+
+ status_t sendRequest(
+ int32_t sessionID, const void *data, ssize_t size = -1,
+ bool timeValid = false, int64_t timeUs = -1ll);
+
+ status_t switchToWebSocketMode(int32_t sessionID);
+
+ enum NotificationReason {
+ kWhatError,
+ kWhatConnected,
+ kWhatClientConnected,
+ kWhatData,
+ kWhatDatagram,
+ kWhatBinaryData,
+ kWhatWebSocketMessage,
+ kWhatNetworkStall,
+ };
+
+protected:
+ virtual ~ANetworkSession();
+
+private:
+ struct NetworkThread;
+ struct Session;
+
+ Mutex mLock;
+ sp<Thread> mThread;
+
+ int32_t mNextSessionID;
+
+ int mPipeFd[2];
+
+ KeyedVector<int32_t, sp<Session> > mSessions;
+
+ enum Mode {
+ kModeCreateUDPSession,
+ kModeCreateTCPDatagramSessionPassive,
+ kModeCreateTCPDatagramSessionActive,
+ kModeCreateRTSPServer,
+ kModeCreateRTSPClient,
+ };
+ status_t createClientOrServer(
+ Mode mode,
+ const struct in_addr *addr,
+ unsigned port,
+ const char *remoteHost,
+ unsigned remotePort,
+ const sp<AMessage> ¬ify,
+ int32_t *sessionID);
+
+ void threadLoop();
+ void interrupt();
+
+ static status_t MakeSocketNonBlocking(int s);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
+};
+
+} // namespace android
+
+#endif // A_NETWORK_SESSION_H_
diff --git a/media/libstagefright/include/media/stagefright/ParsedMessage.h b/media/libstagefright/include/media/stagefright/ParsedMessage.h
new file mode 100644
index 0000000..9d43a93
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/ParsedMessage.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+// Encapsulates an "HTTP/RTSP style" response, i.e. a status line,
+// key/value pairs making up the headers and an optional body/content.
+struct ParsedMessage : public RefBase {
+ static sp<ParsedMessage> Parse(
+ const char *data, size_t size, bool noMoreData, size_t *length);
+
+ bool findString(const char *name, AString *value) const;
+ bool findInt32(const char *name, int32_t *value) const;
+
+ const char *getContent() const;
+
+ bool getRequestField(size_t index, AString *field) const;
+ bool getStatusCode(int32_t *statusCode) const;
+
+ AString debugString() const;
+
+ static bool GetAttribute(const char *s, const char *key, AString *value);
+
+ static bool GetInt32Attribute(
+ const char *s, const char *key, int32_t *value);
+
+
+protected:
+ virtual ~ParsedMessage();
+
+private:
+ KeyedVector<AString, AString> mDict;
+ AString mContent;
+
+ ParsedMessage();
+
+ ssize_t parse(const char *data, size_t size, bool noMoreData);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ParsedMessage);
+};
+
+} // namespace android
diff --git a/media/libstagefright/wifi-display/MediaSender.cpp b/media/libstagefright/wifi-display/MediaSender.cpp
index 6f1dbbf..6e743b5 100644
--- a/media/libstagefright/wifi-display/MediaSender.cpp
+++ b/media/libstagefright/wifi-display/MediaSender.cpp
@@ -24,12 +24,13 @@
#include "source/TSPacketizer.h"
#include <media/IHDCP.h>
-#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
+#include <media/stagefright/ANetworkSession.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferBase.h>
#include <ui/GraphicBuffer.h>
namespace android {
diff --git a/media/libstagefright/wifi-display/rtp/RTPSender.cpp b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
index bfdd5fc..ba01b74 100644
--- a/media/libstagefright/wifi-display/rtp/RTPSender.cpp
+++ b/media/libstagefright/wifi-display/rtp/RTPSender.cpp
@@ -24,9 +24,9 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/ANetworkSession.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index 159b113..96aceb1 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -32,7 +32,7 @@
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/ParsedMessage.h>
+#include <media/stagefright/ParsedMessage.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/Utils.h>
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index c25a675..47047f8 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -21,7 +21,7 @@
#include "VideoFormats.h"
#include <media/stagefright/foundation/AHandler.h>
-#include <media/stagefright/foundation/ANetworkSession.h>
+#include <media/stagefright/ANetworkSession.h>
#include <netinet/in.h>