blob: 7d02ecc57de9925be3b45606a26bf1a8c1f0afd8 [file] [log] [blame]
/*
* Copyright 2009, The Android Open Source Project
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define LOG_TAG "wdsclient"
#include "AdbConnection.h"
#include "ClientUtils.h"
#include "Device.h"
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <utils/Log.h>
void AdbConnection::close() {
if (m_fd != -1) {
shutdown(m_fd, SHUT_RDWR);
::close(m_fd);
m_fd = -1;
}
}
// Default adb port
#define ADB_PORT 5037
bool AdbConnection::connect() {
// Some commands (host:devices for example) close the connection so we call
// connect after the response.
close();
m_fd = socket(PF_INET, SOCK_STREAM, 0);
if (m_fd < 0) {
log_errno("Failed to create socket for connecting to adb");
return false;
}
// Create the socket address struct
sockaddr_in adb;
createTcpSocket(adb, ADB_PORT);
// Connect to adb
if (::connect(m_fd, (sockaddr*) &adb, sizeof(adb)) < 0) {
log_errno("Failed to connect to adb");
return false;
}
// Connected
return true;
}
// Adb protocol stuff
#define MAX_COMMAND_LENGTH 1024
#define PAYLOAD_LENGTH 4
#define PAYLOAD_FORMAT "%04X"
bool AdbConnection::sendRequest(const char* fmt, ...) const {
if (m_fd == -1) {
ALOGE("Connection is closed");
return false;
}
// Build the command (service)
char buf[MAX_COMMAND_LENGTH];
va_list args;
va_start(args, fmt);
int res = vsnprintf(buf, MAX_COMMAND_LENGTH, fmt, args);
va_end(args);
ALOGV("Sending command: %04X%.*s", res, res, buf);
// Construct the payload length
char payloadLen[PAYLOAD_LENGTH + 1];
snprintf(payloadLen, sizeof(payloadLen), PAYLOAD_FORMAT, res);
// First, send the payload length
if (send(m_fd, payloadLen, PAYLOAD_LENGTH, 0) < 0) {
log_errno("Failure when sending payload");
return false;
}
// Send the actual command
if (send(m_fd, buf, res, 0) < 0) {
log_errno("Failure when sending command");
return false;
}
// Check for the OKAY from adb
return checkOkayResponse();
}
static void printFailureMessage(int fd) {
// Grab the payload length
char lenStr[PAYLOAD_LENGTH + 1];
int payloadLen = recv(fd, lenStr, sizeof(lenStr) - 1, 0);
ALOG_ASSERT(payloadLen == PAYLOAD_LENGTH, "Incorrect payload size");
lenStr[PAYLOAD_LENGTH] = 0;
// Parse the hex payload
payloadLen = strtol(lenStr, NULL, 16);
if (payloadLen < 0)
return;
// Grab the message
char* msg = new char[payloadLen + 1]; // include null-terminator
int res = recv(fd, msg, payloadLen, 0);
if (res < 0) {
log_errno("Failure reading failure message from adb");
return;
} else if (res != payloadLen) {
ALOGE("Incorrect payload length %d - expected %d", res, payloadLen);
return;
}
msg[res] = 0;
// Tell somebody about it
ALOGE("Received failure from adb: %s", msg);
// Cleanup
delete[] msg;
}
#define ADB_RESPONSE_LENGTH 4
bool AdbConnection::checkOkayResponse() const {
ALOG_ASSERT(m_fd != -1, "Connection has been closed!");
char buf[ADB_RESPONSE_LENGTH];
int res = recv(m_fd, buf, sizeof(buf), 0);
if (res < 0) {
log_errno("Failure reading response from adb");
return false;
}
// Check for a response other than OKAY/FAIL
if ((res == ADB_RESPONSE_LENGTH) && (strncmp(buf, "OKAY", res) == 0)) {
ALOGV("Command OKAY");
return true;
} else if (strncmp(buf, "FAIL", ADB_RESPONSE_LENGTH) == 0) {
// Something happened, print out the reason for failure
printFailureMessage(m_fd);
return false;
}
ALOGE("Incorrect response from adb - '%.*s'", res, buf);
return false;
}
void AdbConnection::clearDevices() {
for (unsigned i = 0; i < m_devices.size(); i++)
delete m_devices.editItemAt(i);
m_devices.clear();
}
const DeviceList& AdbConnection::getDeviceList() {
// Clear the current device list
clearDevices();
if (m_fd == -1) {
ALOGE("Connection is closed");
return m_devices;
}
// Try to send the device list request
if (!sendRequest("host:devices")) {
ALOGE("Failed to get device list from adb");
return m_devices;
}
// Get the payload length
char lenStr[PAYLOAD_LENGTH + 1];
int res = recv(m_fd, lenStr, sizeof(lenStr) - 1, 0);
if (res < 0) {
log_errno("Failure to read payload size of device list");
return m_devices;
}
lenStr[PAYLOAD_LENGTH] = 0;
// Parse the hex payload
int payloadLen = strtol(lenStr, NULL, 16);
if (payloadLen < 0)
return m_devices;
// Grab the list of devices. The format is as follows:
// <serialno><tab><state><newline>
char* msg = new char[payloadLen + 1];
res = recv(m_fd, msg, payloadLen, 0);
if (res < 0) {
log_errno("Failure reading the device list");
return m_devices;
} else if (res != payloadLen) {
ALOGE("Incorrect payload length %d - expected %d", res, payloadLen);
return m_devices;
}
msg[res] = 0;
char serial[32];
char state[32];
int numRead;
char* ptr = msg;
while (sscanf(ptr, "%31s\t%31s\n%n", serial, state, &numRead) > 1) {
Device::DeviceType t = Device::DEVICE;
static const char emulator[] = "emulator-";
if (strncmp(serial, emulator, sizeof(emulator) - 1) == 0)
t = Device::EMULATOR;
ALOGV("Adding device %s (%s)", serial, state);
m_devices.add(new Device(serial, t, this));
// Reset for the next line
ptr += numRead;
}
// Cleanup
delete[] msg;
return m_devices;
}