aboutsummaryrefslogtreecommitdiffstats
path: root/android/qemu-setup.c
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2010-05-13 11:04:42 -0700
committerVladimir Chtchetkine <vchtchetkine@google.com>2010-05-14 12:14:15 -0700
commitd27aca1c8172462c6e834c3c42582106b36aa422 (patch)
treee3cbd46d95877ed0680769b77ff809641bba6877 /android/qemu-setup.c
parenta2cb54380beeb18f20a21922a2c01687117931ba (diff)
downloadexternal_qemu-d27aca1c8172462c6e834c3c42582106b36aa422.zip
external_qemu-d27aca1c8172462c6e834c3c42582106b36aa422.tar.gz
external_qemu-d27aca1c8172462c6e834c3c42582106b36aa422.tar.bz2
Moved android_emulation_setup from android/main.c to android/qemu-setup.c
Change-Id: Ic66bb0d07e28088ae02055c88d37bfd00edea592
Diffstat (limited to 'android/qemu-setup.c')
-rw-r--r--android/qemu-setup.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/android/qemu-setup.c b/android/qemu-setup.c
new file mode 100644
index 0000000..1ab1737
--- /dev/null
+++ b/android/qemu-setup.c
@@ -0,0 +1,514 @@
+/* Copyright (C) 2006-2010 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 "libslirp.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "modem_driver.h"
+#include "proxy_http.h"
+
+#include "android/qemulator.h"
+#include "android/android.h"
+#include "android/globals.h"
+#include "android/hw-sensors.h"
+#include "android/utils/debug.h"
+#include "android/utils/path.h"
+#include "android/utils/system.h"
+#include "android/utils/bufprint.h"
+
+#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
+
+#ifdef ANDROID_SDK_TOOLS_REVISION
+# define VERSION_STRING STRINGIFY(ANDROID_SDK_TOOLS_REVISION)".0"
+#else
+# define VERSION_STRING "standalone"
+#endif
+
+extern int control_console_start( int port ); /* in control.c */
+
+/*** APPLICATION DIRECTORY
+ *** Where are we ?
+ ***/
+
+const char* get_app_dir(void)
+{
+ char buffer[1024];
+ char* p = buffer;
+ char* end = p + sizeof(buffer);
+ p = bufprint_app_dir(p, end);
+ if (p >= end)
+ return NULL;
+
+ return strdup(buffer);
+}
+
+enum {
+ REPORT_CONSOLE_SERVER = (1 << 0),
+ REPORT_CONSOLE_MAX = (1 << 1)
+};
+
+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\n");
+ exit(3);
+ }
+ end += 1;
+ while (*end) {
+ char* p = strchr(end, ',');
+ if (p == NULL)
+ p = end + strlen(end);
+
+ if (memcmp( end, "server", p-end ) == 0)
+ flags |= REPORT_CONSOLE_SERVER;
+ else if (memcmp( end, "max=", 4) == 0) {
+ end += 4;
+ *maxtries = strtol( end, NULL, 10 );
+ flags |= REPORT_CONSOLE_MAX;
+ } else {
+ derror( "socket port/path can be followed by [,server][,max=<count>] only\n");
+ exit(3);
+ }
+
+ end = p;
+ if (*end)
+ end += 1;
+ }
+ return flags;
+}
+
+static void
+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 & REPORT_CONSOLE_SERVER) {
+ s = socket_loopback_server( port, SOCKET_STREAM );
+ if (s < 0) {
+ fprintf(stderr, "could not create server socket on TCP:%ld: %s\n",
+ port, errno_str);
+ exit(3);
+ }
+ } else {
+ for ( ; maxtries > 0; maxtries-- ) {
+ D("trying to find console-report client on tcp:%d", port);
+ s = socket_loopback_client( port, SOCKET_STREAM );
+ if (s >= 0)
+ break;
+
+ sleep_ms(1000);
+ }
+ if (s < 0) {
+ fprintf(stderr, "could not connect to server on TCP:%ld: %s\n",
+ port, errno_str);
+ exit(3);
+ }
+ }
+ } else if ( !strncmp( proto_port, "unix:", 5) ) {
+#ifdef _WIN32
+ fprintf(stderr, "sorry, the unix: protocol is not supported on Win32\n");
+ exit(3);
+#else
+ char* path = strdup(proto_port+5);
+ char* end = strchr(path, ',');
+ if (end != NULL) {
+ flags = get_report_console_options( end, &maxtries );
+ *end = 0;
+ }
+ if (flags & REPORT_CONSOLE_SERVER) {
+ s = socket_unix_server( path, SOCKET_STREAM );
+ if (s < 0) {
+ fprintf(stderr, "could not bind unix socket on '%s': %s\n",
+ proto_port+5, errno_str);
+ exit(3);
+ }
+ } else {
+ for ( ; maxtries > 0; maxtries-- ) {
+ s = socket_unix_client( path, SOCKET_STREAM );
+ if (s >= 0)
+ break;
+
+ sleep_ms(1000);
+ }
+ if (s < 0) {
+ fprintf(stderr, "could not connect to unix socket on '%s': %s\n",
+ path, errno_str);
+ exit(3);
+ }
+ }
+ free(path);
+#endif
+ } else {
+ fprintf(stderr, "-report-console must be followed by a 'tcp:<port>' or 'unix:<path>'\n");
+ exit(3);
+ }
+
+ 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) {
+ fprintf(stderr, "could not accept console-reporting client connection: %s\n",
+ errno_str);
+ exit(3);
+ }
+
+ 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) {
+ fprintf(stderr, "could not send console number report: %d: %s\n",
+ errno, errno_str );
+ exit(3);
+ }
+ socket_close(s);
+ }
+ D( "console port number sent to remote. resuming boot" );
+
+ restore_sigalrm (&sigstate);
+}
+
+/* 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
+ */
+void android_emulation_setup( void )
+{
+ int tries = 16;
+ int base_port = 5554;
+ int adb_host_port = 5037; // adb's default
+ int success = 0;
+ int s;
+ uint32_t guest_ip;
+
+ /* Set the port where the emulator expects adb to run on the host
+ * machine */
+ char* adb_host_port_str = getenv( "ANDROID_ADB_SERVER_PORT" );
+ if ( adb_host_port_str && strlen( adb_host_port_str ) > 0 ) {
+ adb_host_port = (int) strtol( adb_host_port_str, NULL, 0 );
+ if ( adb_host_port <= 0 ) {
+ derror( "env var ANDROID_ADB_SERVER_PORT must be a number > 0. Got \"%s\"\n",
+ adb_host_port_str );
+ exit(1);
+ }
+ }
+
+ AndroidOptions* opts = qemulator_get()->opts;
+
+ inet_strtoip("10.0.2.15", &guest_ip);
+
+#if 0
+ if (opts->adb_port) {
+ fprintf( stderr, "option -adb-port is obsolete, use -port instead\n" );
+ exit(1);
+ }
+#endif
+
+ if (opts->port && opts->ports) {
+ fprintf( stderr, "options -port and -ports cannot be used together.\n");
+ exit(1);
+ }
+
+ if (opts->ports) {
+ char* comma_location;
+ char* end;
+ int console_port = strtol( opts->ports, &comma_location, 0 );
+
+ if ( comma_location == NULL || *comma_location != ',' ) {
+ derror( "option -ports must be followed by two comma separated positive integer numbers" );
+ exit(1);
+ }
+
+ int adb_port = strtol( comma_location+1, &end, 0 );
+
+ if ( end == NULL || *end ) {
+ derror( "option -ports must be followed by two comma separated positive integer numbers" );
+ exit(1);
+ }
+
+ if ( console_port == adb_port ) {
+ derror( "option -ports must be followed by two different integer numbers" );
+ exit(1);
+ }
+
+ // Set up redirect from host to guest system. adbd on the guest listens
+ // on 5555.
+ slirp_redir( 0, adb_port, guest_ip, 5555 );
+ if ( control_console_start( console_port ) < 0 ) {
+ slirp_unredir( 0, adb_port );
+ }
+
+ base_port = console_port;
+ } else {
+ if (opts->port) {
+ char* end;
+ int port = strtol( opts->port, &end, 0 );
+ if ( end == NULL || *end ||
+ (unsigned)((port - base_port) >> 1) >= (unsigned)tries ) {
+ derror( "option -port must be followed by an even integer number between %d and %d\n",
+ base_port, base_port + (tries-1)*2 );
+ exit(1);
+ }
+ if ( (port & 1) != 0 ) {
+ port &= ~1;
+ dwarning( "option -port must be followed by an even integer, using port number %d\n",
+ port );
+ }
+ base_port = port;
+ tries = 1;
+ }
+
+ for ( ; tries > 0; tries--, base_port += 2 ) {
+
+ /* setup first redirection for ADB, the Android Debug Bridge */
+ if ( slirp_redir( 0, base_port+1, guest_ip, 5555 ) < 0 )
+ continue;
+
+ /* setup second redirection for the emulator console */
+ if ( control_console_start( base_port ) < 0 ) {
+ slirp_unredir( 0, base_port+1 );
+ continue;
+ }
+
+ D( "control console listening on port %d, ADB on port %d", base_port, base_port+1 );
+ success = 1;
+ break;
+ }
+
+ if (!success) {
+ fprintf(stderr, "it seems too many emulator instances are running on this machine. Aborting\n" );
+ exit(1);
+ }
+ }
+
+ if (opts->report_console) {
+ report_console(opts->report_console, base_port);
+ }
+
+ android_modem_init( base_port );
+
+ android_base_port = base_port;
+ /* 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
+ */
+ do
+ {
+ SockAddress addr;
+ char tmp[32];
+
+ s = socket_create_inet( SOCKET_STREAM );
+ if (s < 0) {
+ D("can't create socket to talk to the ADB server");
+ break;
+ }
+
+ sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, adb_host_port );
+ if (socket_connect( s, &addr ) < 0) {
+ D("can't connect to ADB server: %s", errno_str );
+ break;
+ }
+
+ sprintf(tmp,"0012host:emulator:%d",base_port+1);
+ socket_send(s, tmp, 18+4);
+ D("sent '%s' to ADB server", tmp);
+ }
+ while (0);
+
+ if (s >= 0)
+ socket_close(s);
+
+ /* setup the http proxy, if any */
+ if (VERBOSE_CHECK(proxy))
+ proxy_set_verbose(1);
+
+ if (!opts->http_proxy) {
+ opts->http_proxy = getenv("http_proxy");
+ }
+
+ do
+ {
+ const char* env = opts->http_proxy;
+ int envlen;
+ ProxyOption option_tab[4];
+ ProxyOption* option = option_tab;
+ char* p;
+ char* q;
+ const char* proxy_name;
+ int proxy_name_len;
+ int proxy_port;
+
+ if (!env)
+ break;
+
+ envlen = strlen(env);
+
+ /* skip the 'http://' header, if present */
+ if (envlen >= 7 && !memcmp(env, "http://", 7)) {
+ env += 7;
+ envlen -= 7;
+ }
+
+ /* do we have a username:password pair ? */
+ p = strchr(env, '@');
+ if (p != 0) {
+ q = strchr(env, ':');
+ if (q == NULL) {
+ BadHttpProxyFormat:
+ dprint("http_proxy format unsupported, try 'proxy:port' or 'username:password@proxy:port'");
+ break;
+ }
+
+ option->type = PROXY_OPTION_AUTH_USERNAME;
+ option->string = env;
+ option->string_len = q - env;
+ option++;
+
+ option->type = PROXY_OPTION_AUTH_PASSWORD;
+ option->string = q+1;
+ option->string_len = p - (q+1);
+ option++;
+
+ env = p+1;
+ }
+
+ p = strchr(env,':');
+ if (p == NULL)
+ goto BadHttpProxyFormat;
+
+ proxy_name = env;
+ proxy_name_len = p - env;
+ proxy_port = atoi(p+1);
+
+ D( "setting up http proxy: server=%.*s port=%d",
+ proxy_name_len, proxy_name, proxy_port );
+
+ if ( proxy_http_setup( proxy_name, proxy_name_len, proxy_port,
+ option - option_tab, option_tab ) < 0 )
+ {
+ dprint( "http proxy setup failed, check your $http_proxy variable");
+ }
+ }
+ while (0);
+
+ /* initialize sensors, this must be done here due to timer issues */
+ android_hw_sensors_init();
+
+ /* cool, now try to run the "ddms ping" command, which will take care of pinging usage
+ * if the user agreed for it. the emulator itself never sends anything to any outside
+ * machine
+ */
+ {
+#ifdef _WIN32
+# define _ANDROID_PING_PROGRAM "ddms.bat"
+#else
+# define _ANDROID_PING_PROGRAM "ddms"
+#endif
+
+ char tmp[PATH_MAX];
+ const char* appdir = get_app_dir();
+
+ if (snprintf( tmp, PATH_MAX, "%s%s%s", appdir, PATH_SEP,
+ _ANDROID_PING_PROGRAM ) >= PATH_MAX) {
+ dprint( "Application directory too long: %s", appdir);
+ return;
+ }
+
+ /* if the program isn't there, don't bother */
+ D( "ping program: %s", tmp);
+ if (path_exists(tmp)) {
+#ifdef _WIN32
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+
+ ZeroMemory( &startup, sizeof(startup) );
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWMINIMIZED;
+
+ ZeroMemory( &pinfo, sizeof(pinfo) );
+
+ char* comspec = getenv("COMSPEC");
+ if (!comspec) comspec = "cmd.exe";
+
+ // Run
+ char args[PATH_MAX + 30];
+ if (snprintf( args, PATH_MAX, "/C \"%s\" ping emulator " VERSION_STRING,
+ tmp) >= PATH_MAX ) {
+ D( "DDMS path too long: %s", tmp);
+ return;
+ }
+
+ CreateProcess(
+ comspec, /* program path */
+ args, /* command line args */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ FALSE, /* no, don't inherit any handles */
+ DETACHED_PROCESS, /* the new process doesn't have a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo );
+
+ D( "ping command: %s %s", comspec, args );
+#else
+ int pid;
+
+ /* disable SIGALRM for the fork(), the periodic signal seems to
+ * interefere badly with the fork() implementation on Linux running
+ * under VMWare.
+ */
+ BEGIN_NOSIGALRM
+ pid = fork();
+ if (pid == 0) {
+ int fd = open("/dev/null", O_WRONLY);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL );
+ }
+ END_NOSIGALRM
+
+ /* don't do anything in the parent or in case of error */
+ strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) );
+ D( "ping command: %s", tmp );
+#endif
+ }
+ }
+}
+
+