blob: 2afaa91470427ca2901766bad3b8ccdc87177373 [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/emulation/AdbGuestPipe.h"
#include "android/base/async/ThreadLooper.h"
#include "android/base/Log.h"
#include "android/base/sockets/ScopedSocket.h"
#include "android/base/sockets/SocketUtils.h"
#include "android/base/StringFormat.h"
#include "android/base/threads/Thread.h"
#include "android/emulation/testing/TestAndroidPipeDevice.h"
#include <gtest/gtest.h>
// Because gtest includes Windows headers that declare the ERROR
// macro that prevents LOG(ERROR) from compiling!!
#ifdef _WIN32
#undef ERROR
#endif
#include <memory>
#include <vector>
namespace android {
namespace emulation {
using android::base::ScopedSocket;
using android::base::StringFormat;
using android::base::StringView;
using TestGuest = TestAndroidPipeDevice::Guest;
namespace {
// A Mock AdbHostAgent that will be used during testing. It doesn't depend
// on any TCP ports.
class MockAdbHostAgent : public AdbHostAgent {
public:
MockAdbHostAgent() {
auto service = new AdbGuestPipe::Service(this);
mGuestAgent = service;
AndroidPipe::Service::add(service);
}
~MockAdbHostAgent() {
if (mThread.get()) {
mThread->wait(nullptr);
}
AndroidPipe::Service::resetAll();
}
// AdbHostAgent overrides.
void setAgent(AdbGuestAgent* guestAgent) { mGuestAgent = guestAgent; }
virtual void startListening() override { mListening = true; }
virtual void stopListening() override { mListening = false; }
virtual void notifyServer() override { mServerNotificationCount++; }
// Accessors.
virtual bool isListening() const { return mListening; }
virtual int serverNotificationCount() const {
return mServerNotificationCount;
}
// Create a socket pair and a thread that will push |data| into it
// before trying to read a single byte from one end of the pair.
// The other end is passed to a new active guest.
void createFakeConnection(StringView data) {
CHECK(mListening);
if (mThread.get()) {
mThread->wait(nullptr);
}
mThread.reset(new ConnectorThread(data));
mListening = false;
mGuestAgent->onHostConnection(mThread->releaseOutSocket());
mThread->start();
}
private:
// A small thread that will connect to a given port and send some
// data through the socket. It will then try to read a single byte
// before exiting.
class ConnectorThread : public android::base::Thread {
public:
ConnectorThread(StringView data) : Thread(), mData(data) {
int inSocket, outSocket;
if (android::base::socketCreatePair(&inSocket, &outSocket) < 0) {
PLOG(ERROR) << "Could not create socket pair";
return;
}
// Make the sockets blocking for this test to work.
android::base::socketSetBlocking(inSocket);
android::base::socketSetBlocking(outSocket);
mInSocket.reset(inSocket);
mOutSocket.reset(outSocket);
}
bool valid() const { return mInSocket.valid() && mOutSocket.valid(); }
int releaseOutSocket() { return mOutSocket.release(); }
virtual intptr_t main() override {
if (mData.size() > 0) {
if (!android::base::socketSendAll(
mInSocket.get(), mData.c_str(), mData.size())) {
DPLOG(ERROR) << "I/O error when sending data";
return -1;
}
}
char buf[1] = {};
ssize_t len = android::base::socketRecv(mInSocket.get(), buf,
sizeof(buf));
if (len < 0) {
DPLOG(ERROR) << "I/O error when receiving data";
} else if (len == 0) {
DLOG(ERROR) << "Disconnected";
}
mInSocket.close();
return 0;
}
private:
ScopedSocket mInSocket;
ScopedSocket mOutSocket;
std::string mData;
};
AdbGuestAgent* mGuestAgent = nullptr;
bool mListening = false;
int mServerNotificationCount = 0;
std::unique_ptr<ConnectorThread> mThread;
};
// The socket associated with |guest| is in non-blocking mode, which
// means that read() attempts will sometimes return PIPE_ERROR_AGAIN.
// This function handles these by waiting a little, giving some time
// slices for the ConnectorThread to write properly.
int blockingRead(TestGuest* guest, void* buffer, size_t size) {
int ret = guest->read(buffer, size);
if (ret != PIPE_ERROR_AGAIN) {
return ret;
}
android::base::ThreadLooper::get()->runWithTimeoutMs(2);
return guest->read(buffer, size);
}
} // namespace
TEST(AdbGuestPipe, createService) {
AndroidPipe::Service::resetAll();
// Create a host agent and a new service and register it.
MockAdbHostAgent adbHost;
}
TEST(AdbGuestPipe, createOneGuest) {
// NOTE: This does Service::resetAll() on creation and destruction for us.
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_EQ(6, guest->write("accept", 6));
static constexpr StringView kMessage = "Hello World!";
adbHost.createFakeConnection(kMessage);
char reply[3] = {};
EXPECT_EQ(2, guest->read(reply, 2));
EXPECT_STREQ("ok", reply);
EXPECT_EQ(5, guest->write("start", 5));
char buffer[kMessage.size() + 1] = {};
const ssize_t expectedSize = static_cast<ssize_t>(kMessage.size());
EXPECT_EQ(expectedSize, blockingRead(guest, buffer, kMessage.size()));
EXPECT_STREQ(kMessage.c_str(), buffer);
EXPECT_EQ(1, guest->write("x", 1));
}
TEST(AdbGuestPipe, createGuestWithBadAcceptCommand) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
// This write should fail with PIPE_ERROR_IO because the other end
// is waiting for an 'accept'.
EXPECT_EQ(PIPE_ERROR_IO, guest->write("ACCEPT", 6));
}
TEST(AdbGuestPipe, createGuestWithCloseOnAccept) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
// Force-close the connection.
guest->close();
}
TEST(AdbGuestPipe, createGuestWithCloseBeforeConnection) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a guest, and verify that this invokes startListening on the
// guest agent.
std::unique_ptr<TestGuest> guest(TestGuest::create());
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_FALSE(adbHost.isListening());
EXPECT_EQ(6, guest->write("accept", 6));
EXPECT_TRUE(adbHost.isListening());
guest->close();
EXPECT_FALSE(adbHost.isListening());
}
TEST(AdbGuestPipe, createGuestWithCloseOnReply) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
AndroidPipe::Service::add(new AdbGuestPipe::Service(&adbHost));
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_EQ(6, guest->write("accept", 6));
static constexpr StringView kMessage = "Hello World!";
adbHost.createFakeConnection(kMessage);
// Force-close the connection.
guest->close();
}
TEST(AdbGuestPipe, createGuestWithBadStartCommand) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_EQ(6, guest->write("accept", 6));
static constexpr StringView kMessage = "Hello World!";
adbHost.createFakeConnection(kMessage);
char reply[3] = {};
EXPECT_EQ(2, guest->read(reply, 2));
EXPECT_STREQ("ok", reply);
// This write should fail with PIPE_ERROR_IO because the other end
// is waiting for a 'start'. NOTE: This will result in a broken pipe
// error for the connector thread waiting for a reply.
EXPECT_EQ(PIPE_ERROR_IO, guest->write("START", 5));
}
TEST(AdbGuestPipe, createGuestWithCloseOnStart) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_EQ(6, guest->write("accept", 6));
static constexpr StringView kMessage = "Hello World!";
adbHost.createFakeConnection(kMessage);
char reply[3] = {};
EXPECT_EQ(2, guest->read(reply, 2));
EXPECT_STREQ("ok", reply);
// Force-close the connection.
guest->close();
}
TEST(AdbGuestPipe, createGuestWhichClosesTheConnection) {
// NOTE: This does Service::resetAll() on creation and destruction for us.
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
// Create a new guest connection from the test device.
auto guest = TestGuest::create();
EXPECT_TRUE(guest);
EXPECT_EQ(0, guest->connect("qemud:adb"));
EXPECT_EQ(6, guest->write("accept", 6));
static constexpr StringView kMessage = "Hello World!";
adbHost.createFakeConnection(kMessage);
char reply[3] = {};
EXPECT_EQ(2, guest->read(reply, 2));
EXPECT_STREQ("ok", reply);
EXPECT_EQ(5, guest->write("start", 5));
// Read only partial bytes from the connection.
char buffer[kMessage.size() + 1] = {};
const ssize_t expectedSize = static_cast<ssize_t>(kMessage.size()) / 2;
EXPECT_EQ(expectedSize, blockingRead(guest, buffer, expectedSize));
// Force-close the connection now.
guest->close();
}
TEST(AdbGuestPipe, createMultipleGuestConnections) {
TestAndroidPipeDevice testDevice;
MockAdbHostAgent adbHost;
const int kCount = 8;
// Create kCount guests that all connect at the same time.
std::unique_ptr<TestGuest> guests[kCount];
for (int n = 0; n < kCount; ++n) {
guests[n].reset(TestGuest::create());
EXPECT_TRUE(guests[n].get());
EXPECT_EQ(0, guests[n]->connect("qemud:adb"));
EXPECT_FALSE(adbHost.isListening());
}
// Create kCount threads that will send data to each guest in succession.
for (int n = 0; n < kCount; ++n) {
auto guest = guests[n].get();
EXPECT_EQ(6, guest->write("accept", 6));
EXPECT_TRUE(adbHost.isListening());
std::string message = StringFormat("Hello %d", n + 1);
adbHost.createFakeConnection(message);
char reply[3] = {};
EXPECT_EQ(2, guest->read(reply, 2)) << n + 1;
EXPECT_STREQ("ok", reply) << n + 1;
EXPECT_EQ(5, guest->write("start", 5)) << n + 1;
EXPECT_FALSE(adbHost.isListening());
std::string buffer;
buffer.resize(message.size());
EXPECT_EQ(static_cast<ssize_t>(message.size()),
blockingRead(guest, &buffer[0], message.size()))
<< n + 1;
EXPECT_EQ(message, buffer);
EXPECT_EQ(1, guest->write("x", 1)) << n + 1;
guest->close();
}
EXPECT_FALSE(adbHost.isListening());
}
} // namespace emulation
} // namespace android