Moved android_emulation_setup from android/main.c to android/qemu-setup.c

Change-Id: Ic66bb0d07e28088ae02055c88d37bfd00edea592
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
+        }
+    }
+}
+
+