Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb197f84 authored by Andreas Huber's avatar Andreas Huber
Browse files

Test to measure UDP roundtrip time between two devices on the same network.

Change-Id: I4c9a5190efe18da8b6be7d68bda91df878c4118c
parent 69092192
Loading
Loading
Loading
Loading
+994 −0

File added.

Preview size limit exceeded, changes collapsed.

+109 −0
Original line number Diff line number Diff line
/*
 * 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>

namespace android {

struct AMessage;

struct ANetworkSession : public RefBase {
    ANetworkSession();

    status_t start();
    status_t stop();

    status_t createRTSPClient(
            const char *host, unsigned port, const sp<AMessage> &notify,
            int32_t *sessionID);

    status_t createRTSPServer(
            unsigned port, const sp<AMessage> &notify, int32_t *sessionID);

    status_t createUDPSession(
            unsigned localPort, const sp<AMessage> &notify, int32_t *sessionID);

    status_t createUDPSession(
            unsigned localPort,
            const char *remoteHost,
            unsigned remotePort,
            const sp<AMessage> &notify,
            int32_t *sessionID);

    status_t connectUDPSession(
            int32_t sessionID, const char *remoteHost, unsigned remotePort);

    status_t destroySession(int32_t sessionID);

    status_t sendRequest(
            int32_t sessionID, const void *data, ssize_t size = -1);

    enum NotificationReason {
        kWhatError,
        kWhatConnected,
        kWhatClientConnected,
        kWhatData,
        kWhatDatagram,
        kWhatBinaryData,
    };

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,
        kModeCreateRTSPServer,
        kModeCreateRTSPClient,
    };
    status_t createClientOrServer(
            Mode mode,
            unsigned port,
            const char *remoteHost,
            unsigned remotePort,
            const sp<AMessage> &notify,
            int32_t *sessionID);

    void threadLoop();
    void interrupt();

    static status_t MakeSocketNonBlocking(int s);

    DISALLOW_EVIL_CONSTRUCTORS(ANetworkSession);
};

}  // namespace android

#endif  // A_NETWORK_SESSION_H_
+22 −0
Original line number Diff line number Diff line
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
        udptest.cpp                 \
        ANetworkSession.cpp         \
        ParsedMessage.cpp           \

LOCAL_SHARED_LIBRARIES:= \
        libbinder                       \
        libgui                          \
        libmedia                        \
        libstagefright                  \
        libstagefright_foundation       \
        libutils                        \

LOCAL_MODULE:= udptest

LOCAL_MODULE_TAGS := debug

include $(BUILD_EXECUTABLE)
+284 −0
Original line number Diff line number Diff line
/*
 * 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 "ParsedMessage.h"

#include <ctype.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.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;
    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) {
            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;
    }

    for (size_t i = 0; i < mDict.size(); ++i) {
        mDict.editValueAt(i).trim();
    }

    // Found the end of headers.

    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;
}

void 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) {
        ssize_t spacePos = line.find(" ", offset);

        if (spacePos < 0) {
            spacePos = line.size();
        }

        prevOffset = offset;
        offset = spacePos + 1;
    }

    field->setTo(line, prevOffset, offset - prevOffset - 1);
}

bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
    AString statusCodeString;
    getRequestField(1, &statusCodeString);

    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
+58 −0
Original line number Diff line number Diff line
/*
 * 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 {

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;

    void 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
Loading