| /* |
| * Copyright 2013, 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_NEBUG 0 |
| #define LOG_TAG "nettest" |
| #include <utils/Log.h> |
| |
| #include "ANetworkSession.h" |
| #include "TimeSyncer.h" |
| |
| #include <binder/ProcessState.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AHandler.h> |
| #include <media/stagefright/foundation/ALooper.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/DataSource.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/NuMediaExtractor.h> |
| #include <media/stagefright/Utils.h> |
| |
| namespace android { |
| |
| struct TestHandler : public AHandler { |
| TestHandler(const sp<ANetworkSession> &netSession); |
| |
| void listen(int32_t port); |
| void connect(const char *host, int32_t port); |
| |
| protected: |
| virtual ~TestHandler(); |
| virtual void onMessageReceived(const sp<AMessage> &msg); |
| |
| private: |
| enum { |
| kTimeSyncerPort = 8123, |
| }; |
| |
| enum { |
| kWhatListen, |
| kWhatConnect, |
| kWhatTimeSyncerNotify, |
| kWhatNetNotify, |
| kWhatSendMore, |
| kWhatStop, |
| }; |
| |
| sp<ANetworkSession> mNetSession; |
| sp<TimeSyncer> mTimeSyncer; |
| |
| int32_t mServerSessionID; |
| int32_t mSessionID; |
| |
| int64_t mTimeOffsetUs; |
| bool mTimeOffsetValid; |
| |
| int32_t mCounter; |
| |
| int64_t mMaxDelayMs; |
| |
| void dumpDelay(int32_t counter, int64_t delayMs); |
| |
| DISALLOW_EVIL_CONSTRUCTORS(TestHandler); |
| }; |
| |
| TestHandler::TestHandler(const sp<ANetworkSession> &netSession) |
| : mNetSession(netSession), |
| mServerSessionID(0), |
| mSessionID(0), |
| mTimeOffsetUs(-1ll), |
| mTimeOffsetValid(false), |
| mCounter(0), |
| mMaxDelayMs(-1ll) { |
| } |
| |
| TestHandler::~TestHandler() { |
| } |
| |
| void TestHandler::listen(int32_t port) { |
| sp<AMessage> msg = new AMessage(kWhatListen, id()); |
| msg->setInt32("port", port); |
| msg->post(); |
| } |
| |
| void TestHandler::connect(const char *host, int32_t port) { |
| sp<AMessage> msg = new AMessage(kWhatConnect, id()); |
| msg->setString("host", host); |
| msg->setInt32("port", port); |
| msg->post(); |
| } |
| |
| void TestHandler::dumpDelay(int32_t counter, int64_t delayMs) { |
| 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; |
| } |
| |
| if (delayMs > mMaxDelayMs) { |
| mMaxDelayMs = delayMs; |
| } |
| |
| ALOGI("[%d] (%4lld ms / %4lld ms) %s", |
| counter, |
| delayMs, |
| mMaxDelayMs, |
| kPattern + kPatternSize - n); |
| } |
| |
| void TestHandler::onMessageReceived(const sp<AMessage> &msg) { |
| switch (msg->what()) { |
| case kWhatListen: |
| { |
| sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id()); |
| mTimeSyncer = new TimeSyncer(mNetSession, notify); |
| looper()->registerHandler(mTimeSyncer); |
| |
| notify = new AMessage(kWhatNetNotify, id()); |
| |
| int32_t port; |
| CHECK(msg->findInt32("port", &port)); |
| |
| struct in_addr ifaceAddr; |
| ifaceAddr.s_addr = INADDR_ANY; |
| |
| CHECK_EQ((status_t)OK, |
| mNetSession->createTCPDatagramSession( |
| ifaceAddr, |
| port, |
| notify, |
| &mServerSessionID)); |
| break; |
| } |
| |
| case kWhatConnect: |
| { |
| sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id()); |
| mTimeSyncer = new TimeSyncer(mNetSession, notify); |
| looper()->registerHandler(mTimeSyncer); |
| mTimeSyncer->startServer(kTimeSyncerPort); |
| |
| AString host; |
| CHECK(msg->findString("host", &host)); |
| |
| int32_t port; |
| CHECK(msg->findInt32("port", &port)); |
| |
| notify = new AMessage(kWhatNetNotify, id()); |
| |
| CHECK_EQ((status_t)OK, |
| mNetSession->createTCPDatagramSession( |
| 0 /* localPort */, |
| host.c_str(), |
| port, |
| notify, |
| &mSessionID)); |
| break; |
| } |
| |
| case kWhatNetNotify: |
| { |
| int32_t reason; |
| CHECK(msg->findInt32("reason", &reason)); |
| |
| switch (reason) { |
| case ANetworkSession::kWhatConnected: |
| { |
| ALOGI("kWhatConnected"); |
| |
| (new AMessage(kWhatSendMore, id()))->post(); |
| break; |
| } |
| |
| case ANetworkSession::kWhatClientConnected: |
| { |
| ALOGI("kWhatClientConnected"); |
| |
| CHECK_EQ(mSessionID, 0); |
| CHECK(msg->findInt32("sessionID", &mSessionID)); |
| |
| AString clientIP; |
| CHECK(msg->findString("client-ip", &clientIP)); |
| |
| mTimeSyncer->startClient(clientIP.c_str(), kTimeSyncerPort); |
| break; |
| } |
| |
| case ANetworkSession::kWhatDatagram: |
| { |
| sp<ABuffer> packet; |
| CHECK(msg->findBuffer("data", &packet)); |
| |
| CHECK_EQ(packet->size(), 12u); |
| |
| int32_t counter = U32_AT(packet->data()); |
| int64_t timeUs = U64_AT(packet->data() + 4); |
| |
| if (mTimeOffsetValid) { |
| timeUs -= mTimeOffsetUs; |
| int64_t nowUs = ALooper::GetNowUs(); |
| int64_t delayMs = (nowUs - timeUs) / 1000ll; |
| |
| dumpDelay(counter, delayMs); |
| } else { |
| ALOGI("received %d", counter); |
| } |
| break; |
| } |
| |
| case ANetworkSession::kWhatError: |
| { |
| ALOGE("kWhatError"); |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| break; |
| } |
| |
| case kWhatTimeSyncerNotify: |
| { |
| CHECK(msg->findInt64("offset", &mTimeOffsetUs)); |
| mTimeOffsetValid = true; |
| break; |
| } |
| |
| case kWhatSendMore: |
| { |
| uint8_t buffer[4 + 8]; |
| buffer[0] = mCounter >> 24; |
| buffer[1] = (mCounter >> 16) & 0xff; |
| buffer[2] = (mCounter >> 8) & 0xff; |
| buffer[3] = mCounter & 0xff; |
| |
| int64_t nowUs = ALooper::GetNowUs(); |
| |
| buffer[4] = nowUs >> 56; |
| buffer[5] = (nowUs >> 48) & 0xff; |
| buffer[6] = (nowUs >> 40) & 0xff; |
| buffer[7] = (nowUs >> 32) & 0xff; |
| buffer[8] = (nowUs >> 24) & 0xff; |
| buffer[9] = (nowUs >> 16) & 0xff; |
| buffer[10] = (nowUs >> 8) & 0xff; |
| buffer[11] = nowUs & 0xff; |
| |
| ++mCounter; |
| |
| CHECK_EQ((status_t)OK, |
| mNetSession->sendRequest( |
| mSessionID, |
| buffer, |
| sizeof(buffer), |
| true /* timeValid */, |
| nowUs)); |
| |
| msg->post(100000ll); |
| break; |
| } |
| |
| case kWhatStop: |
| { |
| if (mSessionID != 0) { |
| mNetSession->destroySession(mSessionID); |
| mSessionID = 0; |
| } |
| |
| if (mServerSessionID != 0) { |
| mNetSession->destroySession(mServerSessionID); |
| mServerSessionID = 0; |
| } |
| |
| looper()->stop(); |
| break; |
| } |
| |
| default: |
| TRESPASS(); |
| } |
| } |
| |
| } // namespace android |
| |
| static void usage(const char *me) { |
| fprintf(stderr, |
| "usage: %s -c host:port\tconnect to remote host\n" |
| " -l port \tlisten\n", |
| me); |
| } |
| |
| int main(int argc, char **argv) { |
| using namespace android; |
| |
| // srand(time(NULL)); |
| |
| ProcessState::self()->startThreadPool(); |
| |
| DataSource::RegisterDefaultSniffers(); |
| |
| int32_t connectToPort = -1; |
| AString connectToHost; |
| |
| int32_t listenOnPort = -1; |
| |
| int res; |
| while ((res = getopt(argc, argv, "hc:l:")) >= 0) { |
| switch (res) { |
| case 'c': |
| { |
| const char *colonPos = strrchr(optarg, ':'); |
| |
| if (colonPos == NULL) { |
| usage(argv[0]); |
| exit(1); |
| } |
| |
| connectToHost.setTo(optarg, colonPos - optarg); |
| |
| char *end; |
| connectToPort = strtol(colonPos + 1, &end, 10); |
| |
| if (*end != '\0' || end == colonPos + 1 |
| || connectToPort < 0 || connectToPort > 65535) { |
| fprintf(stderr, "Illegal port specified.\n"); |
| exit(1); |
| } |
| break; |
| } |
| |
| case 'l': |
| { |
| char *end; |
| listenOnPort = strtol(optarg, &end, 10); |
| |
| if (*end != '\0' || end == optarg |
| || listenOnPort < 0 || listenOnPort > 65535) { |
| fprintf(stderr, "Illegal port specified.\n"); |
| exit(1); |
| } |
| break; |
| } |
| |
| case '?': |
| case 'h': |
| usage(argv[0]); |
| exit(1); |
| } |
| } |
| |
| if ((listenOnPort < 0 && connectToPort < 0) |
| || (listenOnPort >= 0 && connectToPort >= 0)) { |
| fprintf(stderr, |
| "You need to select either client or server mode.\n"); |
| exit(1); |
| } |
| |
| sp<ANetworkSession> netSession = new ANetworkSession; |
| netSession->start(); |
| |
| sp<ALooper> looper = new ALooper; |
| |
| sp<TestHandler> handler = new TestHandler(netSession); |
| looper->registerHandler(handler); |
| |
| if (listenOnPort) { |
| handler->listen(listenOnPort); |
| } |
| |
| if (connectToPort >= 0) { |
| handler->connect(connectToHost.c_str(), connectToPort); |
| } |
| |
| looper->start(true /* runOnCallingThread */); |
| |
| return 0; |
| } |