blob: 8ed5da507259fcdcd90d22e364e16c4c5f58b6b4 [file] [log] [blame]
/* Copyright (C) 2006-2015 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/console.h"
#include "android/constants.h"
#include "android/adb-server.h"
#include "android/android.h"
#include "android/cmdline-option.h"
#include "android/clipboard-pipe.h"
#include "android/console.h"
#include "android/emulation/android_pipe_pingpong.h"
#include "android/emulation/android_pipe_throttle.h"
#include "android/emulation/android_pipe_unix.h"
#include "android/emulation/android_pipe_zero.h"
#include "android/globals.h"
#include "android/hw-fingerprint.h"
#include "android/hw-sensors.h"
#include "android/opengles-pipe.h"
#include "android/proxy/proxy_setup.h"
#include "android/utils/debug.h"
#include "android/utils/ipaddr.h"
#include "android/utils/path.h"
#include "android/utils/sockets.h"
#include "android/utils/system.h"
#include "android/utils/bufprint.h"
#include "android/version.h"
#include <stdbool.h>
#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
/* Contains arguments for -android-ports option. */
char* android_op_ports = NULL;
/* Contains the parsed numbers from android_op_ports */
int android_op_ports_numbers[2] = {-1, -1};
/* Contains arguments for -android-port option. */
char* android_op_port = NULL;
/* Contains the parsed number from android_op_port */
int android_op_port_number = -1;
/* Contains arguments for -android-report-console option. */
char* android_op_report_console = NULL;
/* Contains arguments for -http-proxy option. */
char* op_http_proxy = NULL;
/* Base port for the emulated system. */
int android_base_port;
/* ADB port */
int android_adb_port = 5037; // Default
/* The device "serial number" is "emulator-<this number>" */
int android_serial_number_port;
// The following code is used to support the -report-console option,
// which takes a parameter in one of the following formats:
//
// tcp:<port>[,<options>]
// unix:<path>[,<options>]
//
// Where <options> is a comma-separated list of options which can be
// server - Enable server mode (waits for client connection).
// max=<count> - Set maximum connection attempts (client mode only).
// ipv6 - Use IPv6 localhost (::1) instead of (127.0.0.1)
//
// bit flags returned by get_report_console_options() below.
enum {
REPORT_CONSOLE_SERVER = (1 << 0),
REPORT_CONSOLE_MAX = (1 << 1),
REPORT_CONSOLE_IPV6 = (1 << 2),
};
// Look at |end| for a comma-separated list of -report-console options
// and return a set of corresponding bit flags. Return -1 on failure.
// On success, if REPORT_CONSOLE_MAX is set in the result, |*maxtries|
// will be updated with the <count> parameter of the max= option.
static int get_report_console_options(char* end, int* maxtries) {
int flags = 0;
if (end == NULL || *end == 0) {
return 0;
}
if (end[0] != ',') {
derror("socket port/path can be followed by [,<option>]+ only");
return -1;
}
end += 1;
while (*end) {
char* p = strchr(end, ',');
if (p == NULL)
p = end + strlen(end);
if ((p - end) == strlen("server") && !memcmp(end, "server", p - end)) {
flags |= REPORT_CONSOLE_SERVER;
} else if (memcmp( end, "max=", 4) == 0) {
end += 4;
*maxtries = strtol( end, NULL, 10 );
flags |= REPORT_CONSOLE_MAX;
} else if ((p - end) == strlen("ipv6") &&
!memcmp(end, "ipv6", p - end)) {
flags |= REPORT_CONSOLE_IPV6;
} else {
derror("socket port/path can be followed by "
"[,server][,max=<count>][,ipv6] only");
return -1;
}
end = p;
if (*end)
end += 1;
}
return flags;
}
// Implement -report-console option. |proto_port| is the option's parameter
// as described above (e.g. 'tcp:<port>,server'). And |console_port| is
// the emulator's console port to report. Return 0 on success, -1 on failure.
static int report_console(const char* proto_port, int console_port) {
int s = -1, s2;
int maxtries = 10;
int flags = 0;
signal_state_t sigstate;
disable_sigalrm( &sigstate );
if ( !strncmp( proto_port, "tcp:", 4) ) {
char* end;
long port = strtol(proto_port + 4, &end, 10);
flags = get_report_console_options( end, &maxtries );
if (flags < 0) {
return -1;
}
if (flags & REPORT_CONSOLE_SERVER) {
// TODO: Listen on both IPv6 and IPv4 interfaces at the same time?
if (flags & REPORT_CONSOLE_IPV6) {
s = socket_loopback6_server( port, SOCKET_STREAM );
} else {
s = socket_loopback4_server( port, SOCKET_STREAM );
}
if (s < 0) {
derror("could not create server socket on TCP:%ld: %s", port,
errno_str);
return -1;
}
} else {
for ( ; maxtries > 0; maxtries-- ) {
D("trying to find console-report client on tcp:%d", port);
if (flags & REPORT_CONSOLE_IPV6) {
s = socket_loopback6_client( port, SOCKET_STREAM );
} else {
s = socket_loopback4_client( port, SOCKET_STREAM );
}
if (s >= 0)
break;
sleep_ms(1000);
}
if (s < 0) {
derror("could not connect to server on TCP:%ld: %s", port,
errno_str);
return -1;
}
}
} else if ( !strncmp( proto_port, "unix:", 5) ) {
#ifdef _WIN32
derror("sorry, the unix: protocol is not supported on Win32");
return -1;
#else
char* path = strdup(proto_port+5);
char* end = strchr(path, ',');
if (end != NULL) {
flags = get_report_console_options( end, &maxtries );
if (flags < 0) {
free(path);
return -1;
}
*end = 0;
}
if (flags & REPORT_CONSOLE_SERVER) {
s = socket_unix_server( path, SOCKET_STREAM );
if (s < 0) {
derror("could not bind unix socket on '%s': %s", proto_port + 5,
errno_str);
return -1;
}
} else {
for ( ; maxtries > 0; maxtries-- ) {
s = socket_unix_client( path, SOCKET_STREAM );
if (s >= 0)
break;
sleep_ms(1000);
}
if (s < 0) {
derror("could not connect to unix socket on '%s': %s", path,
errno_str);
return -1;
}
}
free(path);
#endif
} else {
derror("-report-console must be followed by a 'tcp:<port>' or "
"'unix:<path>'");
return -1;
}
if (flags & REPORT_CONSOLE_SERVER) {
int tries = 3;
D( "waiting for console-reporting client" );
do {
s2 = socket_accept(s, NULL);
} while (s2 < 0 && --tries > 0);
if (s2 < 0) {
derror("could not accept console-reporting client connection: %s",
errno_str);
return -1;
}
socket_close(s);
s = s2;
}
/* simply send the console port in text */
{
char temp[12];
snprintf( temp, sizeof(temp), "%d", console_port );
if (socket_send(s, temp, strlen(temp)) < 0) {
derror("could not send console number report: %d: %s", errno,
errno_str);
return -1;
}
socket_close(s);
}
D( "console port number sent to remote. resuming boot" );
restore_sigalrm (&sigstate);
return 0;
}
static int qemu_android_console_start(int port,
const AndroidConsoleAgents* agents) {
return android_console_start(port, agents);
}
// Try to bind to specific |console_port| and |adb_port| on the localhost
// interface. |legacy_adb| is true iff the legacy ADB network redirection
// through guest:tcp:5555 must also be setup.
//
// Returns true on success, false otherwise. Note that failure is clean, i.e.
// it won't leave ports bound by mistake.
static bool setup_console_and_adb_ports(int console_port,
int adb_port,
bool legacy_adb,
const AndroidConsoleAgents* agents) {
bool register_adb_service = false;
// The guest IP that ADB listens to in legacy mode.
uint32_t guest_ip;
inet_strtoip("10.0.2.15", &guest_ip);
if (legacy_adb) {
agents->net->slirpRedir(false, adb_port, guest_ip, 5555);
} else {
if (android_adb_server_init(adb_port) < 0) {
return false;
}
register_adb_service = true;
}
if (qemu_android_console_start(console_port, agents) < 0) {
if (legacy_adb) {
agents->net->slirpUnredir(false, adb_port);
} else {
register_adb_service = false;
android_adb_server_undo_init();
}
return false;
}
if (register_adb_service) {
android_adb_service_init();
}
return true;
}
/* this function is called from qemu_main() once all arguments have been parsed
* it should be used to setup any Android-specific items in the emulation before the
* main loop runs
*/
bool android_emulation_setup(const AndroidConsoleAgents* agents) {
// Register Android pipe services.
android_pipe_add_type_zero();
android_pipe_add_type_pingpong();
android_pipe_add_type_throttle();
android_unix_pipes_init();
android_init_opengles_pipe();
android_init_clipboard_pipe();
if (android_op_port && android_op_ports) {
derror("options -port and -ports cannot be used together.");
return false;
}
int tries = MAX_ANDROID_EMULATORS;
int success = 0;
int adb_port = -1;
int base_port = ANDROID_CONSOLE_BASEPORT;
int legacy_adb = avdInfo_getAdbdCommunicationMode(android_avdInfo) ? 0 : 1;
if (android_op_ports) {
int console_port = -1;
if (!android_parse_ports_option(android_op_ports,
&console_port,
&adb_port)) {
return false;
}
setup_console_and_adb_ports(console_port, adb_port, legacy_adb,
agents);
base_port = console_port;
} else {
if (android_op_port) {
if (!android_parse_port_option(android_op_port, &base_port,
&adb_port)) {
return false;
}
tries = 1;
}
// TODO(pprabhu): Is this loop lying?
for ( ; tries > 0; tries--, base_port += 2 ) {
/* setup first redirection for ADB, the Android Debug Bridge */
adb_port = base_port + 1;
if (!setup_console_and_adb_ports(base_port, adb_port,
legacy_adb, agents)) {
continue;
}
D( "control console listening on port %d, ADB on port %d", base_port, adb_port );
success = 1;
break;
}
if (!success) {
derror("It seems too many emulator instances are running on "
"this machine. Aborting.");
return false;
}
}
/* Save base port and ADB port. */
android_base_port = base_port;
android_serial_number_port = adb_port - 1;
/* send a simple message to the ADB host server to tell it we just started.
* it should be listening on port 5037. if we can't reach it, don't bother
*/
android_adb_server_notify(adb_port);
android_validate_ports(base_port, adb_port);
if (android_op_report_console) {
if (report_console(android_op_report_console, android_base_port) < 0) {
return false;
}
}
agents->telephony->initModem(android_base_port);
/* setup the http proxy, if any */
if (!op_http_proxy) {
op_http_proxy = getenv("http_proxy");
}
android_http_proxy_setup(op_http_proxy, VERBOSE_CHECK(proxy));
/* initialize sensors, this must be done here due to timer issues */
android_hw_sensors_init();
/* initilize fingperprint here */
android_hw_fingerprint_init();
return true;
}