blob: 9d82251cd18fabfafc22177df06b5ac7dc6e10e4 [file] [log] [blame]
/*
* Copyright (C) 2011 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 "qemu-common.h"
#include "android/globals.h" /* for android_hw */
#include "android/hw-qemud.h"
#include "android/utils/misc.h"
#include "android/utils/system.h"
#include "android/utils/debug.h"
#include "android/adb-server.h"
#include "android/adb-qemud.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(adbclient,__VA_ARGS__)
#define DD(...) VERBOSE_PRINT(adb,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(adbclient)
#define DD_ACTIVE VERBOSE_CHECK(adb)
#define QB(b, s) quote_bytes((const char*)b, (s < 32) ? s : 32)
#define SERVICE_NAME "adb"
#define DEBUG_SERVICE_NAME "adb-debug"
/* Maximum length of the message that can be received from the guest. */
#define ADB_MAX_MSG_LEN 8
/* Enumerates ADB client state values. */
typedef enum AdbClientState {
/* Waiting on a connection from ADB host. */
ADBC_STATE_WAIT_ON_HOST,
/* ADB host is connected. Waiting on the transport initialization completion
* in the guest. */
ADBC_STATE_HOST_CONNECTED,
/* Connection between ADB host and ADB guest is fully established. */
ADBC_STATE_CONNECTED,
/* ADB host has been disconnected. */
ADBC_STATE_HOST_DISCONNECTED,
/* ADB guest has been disconnected. */
ADBC_STATE_GUEST_DISCONNECTED,
} AdbClientState;
/* ADB client descriptor. */
typedef struct AdbClient AdbClient;
struct AdbClient {
/* Opaque pointer returned from adb_server_register_guest API. */
void* opaque;
/* QEMUD client pipe for this client. */
QemudClient* qemud_client;
/* Connection state. */
AdbClientState state;
/* Buffer, collecting accept / stop messages from client. */
char msg_buffer[ADB_MAX_MSG_LEN];
/* Current position in message buffer. */
int msg_cur;
};
/* ADB debugging client descriptor. */
typedef struct AdbDbgClient AdbDbgClient;
struct AdbDbgClient {
/* QEMUD client pipe for this client. */
QemudClient* qemud_client;
};
/********************************************************************************
* ADB host communication.
*******************************************************************************/
/* A callback that is invoked when the host is connected.
* Param:
* opaque - AdbClient instance.
* connection - An opaque pointer that identifies connection with the ADB host.
*/
static void
_adb_on_host_connected(void* opaque, void* connection)
{
AdbClient* const adb_client = (AdbClient*)opaque;
if (adb_client->state == ADBC_STATE_WAIT_ON_HOST) {
D("ADB client %p(o=%p) is connected to the host %p",
adb_client, adb_client->opaque, connection);
/* Bump the state up. */
adb_client->state = ADBC_STATE_HOST_CONNECTED;
/* Notify the ADB guest that host has been connected.This will unblock
* the guest from a 'read', then guest will register the transport, and
* will send 'setart' request, indicating that it is ready to receive
* data from the host. */
qemud_client_send(adb_client->qemud_client, (const uint8_t*)"ok", 2);
} else {
D("Unexpected ADB host connection while state is %d", adb_client->state);
}
}
/* A callback that is invoked when the host gets disconnected.
* Param:
* opaque - AdbClient instance.
* connection - An opaque pointer that identifies connection with the ADB host.
*/
static void
_adb_on_host_disconnect(void* opaque, void* connection)
{
AdbClient* const adb_client = (AdbClient*)opaque;
D("ADB client %p(o=%p) is disconnected from the host %p",
adb_client, adb_client->opaque, connection);
adb_client->state = ADBC_STATE_HOST_DISCONNECTED;
}
/* A callback that is invoked when the host sends data.
* Param:
* opaque - AdbClient instance.
* connection - An opaque pointer that identifies connection with the ADB host.
* buff, size - Buffer containing the host data.
*/
static void
_adb_on_host_data(void* opaque, void* connection, const void* buff, int size)
{
AdbClient* const adb_client = (AdbClient*)opaque;
D("ADB client %p(o=%p) received from the host %p %d bytes in %s",
adb_client, adb_client->opaque, connection, size, QB(buff, size));
if (adb_client->state == ADBC_STATE_CONNECTED) {
/* Dispatch data down to the guest. */
qemud_client_send(adb_client->qemud_client, (const uint8_t*)buff, size);
} else {
D("Unexpected data from ADB host %p while client %p(o=%p) is in state %d",
connection, adb_client, adb_client->opaque, adb_client->state);
}
}
/* ADB guest API required for adb_server_register_guest */
static AdbGuestRoutines _adb_client_routines = {
/* A callback that is invoked when the host is connected. */
_adb_on_host_connected,
/* A callback that is invoked when the host gets disconnected. */
_adb_on_host_disconnect,
/* A callback that is invoked when the host sends data. */
_adb_on_host_data,
};
/********************************************************************************
* ADB guest communication.
*******************************************************************************/
/* Allocates AdbClient instance. */
static AdbClient*
_adb_client_new(void)
{
AdbClient* adb_client;
ANEW0(adb_client);
return adb_client;
}
/* Frees AdbClient instance, allocated with _adb_client_new */
static void
_adb_client_free(AdbClient* adb_client)
{
if (adb_client != NULL) {
free(adb_client);
}
}
/* A callback that is invoked when ADB guest sends data to the service.
* Param:
* opaque - AdbClient instance.
* msg, msglen - Message received from the ADB guest.
* client - adb QEMUD client.
*/
static void
_adb_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client)
{
AdbClient* const adb_client = (AdbClient*)opaque;
D("ADB client %p(o=%p) received from guest %d bytes in %s",
adb_client, adb_client->opaque, msglen, QB(msg, msglen));
if (adb_client->state == ADBC_STATE_CONNECTED) {
/* Connection is fully established. Dispatch the message to the host. */
adb_server_on_guest_message(adb_client->opaque, msg, msglen);
return;
}
/*
* At this point we expect either "accept", or "start" messages. Depending
* on the state of the pipe (although small) these messages could be broken
* into pieces. So, simply checking msg for "accept", or "start" may not
* work. Lets collect them first in internal buffer, and then will see.
*/
/* Make sure tha message doesn't overflow the buffer. */
if ((msglen + adb_client->msg_cur) > sizeof(adb_client->msg_buffer)) {
D("Unexpected message in ADB client.");
adb_client->msg_cur = 0;
return;
}
/* Append to current message. */
memcpy(adb_client->msg_buffer + adb_client->msg_cur, msg, msglen);
adb_client->msg_cur += msglen;
/* Properly dispatch the message, depending on the client state. */
switch (adb_client->state) {
case ADBC_STATE_WAIT_ON_HOST:
/* At this state the only message that is allowed is 'accept' */
if (adb_client->msg_cur == 6 &&
!memcmp(adb_client->msg_buffer, "accept", 6)) {
adb_client->msg_cur = 0;
/* Register ADB guest connection with the ADB server. */
adb_client->opaque =
adb_server_register_guest(adb_client, &_adb_client_routines);
if (adb_client->opaque == NULL) {
D("Unable to register ADB guest with the ADB server.");
/* KO the guest. */
qemud_client_send(adb_client->qemud_client,
(const uint8_t*)"ko", 2);
}
} else {
D("Unexpected guest request while waiting on ADB host to connect.");
}
break;
case ADBC_STATE_HOST_CONNECTED:
/* At this state the only message that is allowed is 'start' */
if (adb_client->msg_cur &&
!memcmp(adb_client->msg_buffer, "start", 5)) {
adb_client->msg_cur = 0;
adb_client->state = ADBC_STATE_CONNECTED;
adb_server_complete_connection(adb_client->opaque);
} else {
D("Unexpected request while waiting on connection to start.");
}
break;
default:
D("Unexpected ADB guest request '%s' while client state is %d.",
QB(msg, msglen), adb_client->state);
break;
}
}
/* A callback that is invoked when ADB guest disconnects from the service. */
static void
_adb_client_close(void* opaque)
{
AdbClient* const adb_client = (AdbClient*)opaque;
D("ADB client %p(o=%p) is disconnected from the guest.",
adb_client, adb_client->opaque);
adb_client->state = ADBC_STATE_GUEST_DISCONNECTED;
if (adb_client->opaque != NULL) {
/* Close connection with the host. */
adb_server_on_guest_closed(adb_client->opaque);
}
_adb_client_free(adb_client);
}
/* A callback that is invoked when ADB daemon running inside the guest connects
* to the service.
* Client parameters are ignored here. Typically they contain the ADB port number
* which is always 5555 for the device / emulated system.
*/
static QemudClient*
_adb_service_connect(void* opaque,
QemudService* serv,
int channel,
const char* client_param)
{
/* Create new QEMUD client for the connection with ADB daemon. */
AdbClient* const adb_client = _adb_client_new();
D("Connecting ADB guest: '%s'", client_param ? client_param : "<null>");
adb_client->qemud_client =
qemud_client_new(serv, channel, client_param, adb_client,
_adb_client_recv, _adb_client_close, NULL, NULL);
if (adb_client->qemud_client == NULL) {
D("Unable to create QEMUD client for ADB guest.");
_adb_client_free(adb_client);
return NULL;
}
return adb_client->qemud_client;
}
/********************************************************************************
* Debugging ADB guest communication.
*******************************************************************************/
/* Allocates AdbDbgClient instance. */
static AdbDbgClient*
_adb_dbg_client_new(void)
{
AdbDbgClient* adb_dbg_client;
ANEW0(adb_dbg_client);
return adb_dbg_client;
}
/* Frees AdbDbgClient instance, allocated with _adb_dbg_client_new */
static void
_adb_dbg_client_free(AdbDbgClient* adb_dbg_client)
{
if (adb_dbg_client != NULL) {
free(adb_dbg_client);
}
}
/* A callback that is invoked when ADB debugging guest sends data to the service.
* Param:
* opaque - AdbDbgClient instance.
* msg, msglen - Message received from the ADB guest.
* client - adb-debug QEMUD client.
*/
static void
_adb_dbg_client_recv(void* opaque, uint8_t* msg, int msglen, QemudClient* client)
{
if (DD_ACTIVE) {
fprintf(stderr, "ADB: %s", (const char*)msg);
}
}
/* A callback that is invoked when ADB debugging guest disconnects from the
* service. */
static void
_adb_dbg_client_close(void* opaque)
{
AdbDbgClient* const adb_dbg_client = (AdbDbgClient*)opaque;
DD("ADB debugging client %p is disconnected from the guest.", adb_dbg_client);
_adb_dbg_client_free(adb_dbg_client);
}
/* A callback that is invoked when ADB daemon running inside the guest connects
* to the debugging service.
* Client parameters are ignored here.
*/
static QemudClient*
_adb_debug_service_connect(void* opaque,
QemudService* serv,
int channel,
const char* client_param)
{
/* Create new QEMUD client for the connection with ADB debugger. */
AdbDbgClient* const adb_dbg_client = _adb_dbg_client_new();
DD("Connecting ADB debugging guest: '%s'",
client_param ? client_param : "<null>");
adb_dbg_client->qemud_client =
qemud_client_new(serv, channel, client_param, adb_dbg_client,
_adb_dbg_client_recv, _adb_dbg_client_close, NULL, NULL);
if (adb_dbg_client->qemud_client == NULL) {
DD("Unable to create QEMUD client for ADB debugging guest.");
_adb_dbg_client_free(adb_dbg_client);
return NULL;
}
return adb_dbg_client->qemud_client;
}
/********************************************************************************
* ADB service API.
*******************************************************************************/
void
android_adb_service_init(void)
{
static int _inited = 0;
if (!adb_server_is_initialized()) {
return;
}
if (!_inited) {
/* Register main ADB service. */
QemudService* serv = qemud_service_register(SERVICE_NAME, 0, NULL,
_adb_service_connect,
NULL, NULL);
if (serv == NULL) {
derror("%s: Could not register '%s' service",
__FUNCTION__, SERVICE_NAME);
return;
}
D("%s: Registered '%s' qemud service", __FUNCTION__, SERVICE_NAME);
/* Register debugging ADB service. */
serv = qemud_service_register(DEBUG_SERVICE_NAME, 0, NULL,
_adb_debug_service_connect, NULL, NULL);
if (serv != NULL) {
DD("Registered '%s' qemud service", DEBUG_SERVICE_NAME);
} else {
dwarning("%s: Could not register '%s' service",
__FUNCTION__, DEBUG_SERVICE_NAME);
}
}
}