1/* Copyright (C) 2011 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12#include "android/avd/util.h"
13#include "android/utils/bufprint.h"
14#include "android/utils/debug.h"
15#include "android/utils/eintr_wrapper.h"
16#include "android/utils/path.h"
17#include "android/utils/dirscanner.h"
18#include "android/main-common.h"
19#include "android/globals.h"
20#include "android/resource.h"
21#include "android/user-config.h"
22
23#include <errno.h>
24#include <fcntl.h>
25#include <limits.h>
26#ifdef _WIN32
27#include <process.h>
28#endif
29#include <signal.h>
30#include <stdlib.h>
31#include <string.h>
32#include <sys/time.h>
33#include <unistd.h>
34
35
36/***********************************************************************/
37/***********************************************************************/
38/*****                                                             *****/
39/*****            U T I L I T Y   R O U T I N E S                  *****/
40/*****                                                             *****/
41/***********************************************************************/
42/***********************************************************************/
43
44#define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
45
46void reassign_string(char** string, const char* new_value) {
47    free(*string);
48    *string = ASTRDUP(new_value);
49}
50
51unsigned convertBytesToMB( uint64_t  size )
52{
53    if (size == 0)
54        return 0;
55
56    size = (size + ONE_MB-1) >> 20;
57    if (size > UINT_MAX)
58        size = UINT_MAX;
59
60    return (unsigned) size;
61}
62
63uint64_t convertMBToBytes( unsigned  megaBytes )
64{
65    return ((uint64_t)megaBytes << 20);
66}
67
68/* this function is used to perform auto-detection of the
69 * system directory in the case of a SDK installation.
70 *
71 * we want to deal with several historical usages, hence
72 * the slightly complicated logic.
73 *
74 * NOTE: the function returns the path to the directory
75 *       containing 'fileName'. this is *not* the full
76 *       path to 'fileName'.
77 */
78static char*
79_getSdkImagePath( const char*  fileName )
80{
81    char   temp[MAX_PATH];
82    char*  p   = temp;
83    char*  end = p + sizeof(temp);
84    char*  q;
85    char*  app;
86
87    static const char* const  searchPaths[] = {
88        "",                                  /* program's directory */
89        "/lib/images",                       /* this is for SDK 1.0 */
90        "/../platforms/android-1.1/images",  /* this is for SDK 1.1 */
91        NULL
92    };
93
94    app = bufprint_app_dir(temp, end);
95    if (app >= end)
96        return NULL;
97
98    do {
99        int  nn;
100
101        /* first search a few well-known paths */
102        for (nn = 0; searchPaths[nn] != NULL; nn++) {
103            p = bufprint(app, end, "%s", searchPaths[nn]);
104            q = bufprint(p, end, "/%s", fileName);
105            if (q < end && path_exists(temp)) {
106                *p = 0;
107                goto FOUND_IT;
108            }
109        }
110
111        /* hmmm. let's assume that we are in a post-1.1 SDK
112         * scan ../platforms if it exists
113         */
114        p = bufprint(app, end, "/../platforms");
115        if (p < end) {
116            DirScanner*  scanner = dirScanner_new(temp);
117            if (scanner != NULL) {
118                int          found = 0;
119                const char*  subdir;
120
121                for (;;) {
122                    subdir = dirScanner_next(scanner);
123                    if (!subdir) break;
124
125                    q = bufprint(p, end, "/%s/images/%s", subdir, fileName);
126                    if (q >= end || !path_exists(temp))
127                        continue;
128
129                    found = 1;
130                    p = bufprint(p, end, "/%s/images", subdir);
131                    break;
132                }
133                dirScanner_free(scanner);
134                if (found)
135                    break;
136            }
137        }
138
139        /* I'm out of ideas */
140        return NULL;
141
142    } while (0);
143
144FOUND_IT:
145    //D("image auto-detection: %s/%s", temp, fileName);
146    return android_strdup(temp);
147}
148
149static char*
150_getSdkImage( const char*  path, const char*  file )
151{
152    char  temp[MAX_PATH];
153    char  *p = temp, *end = p + sizeof(temp);
154
155    p = bufprint(temp, end, "%s/%s", path, file);
156    if (p >= end || !path_exists(temp))
157        return NULL;
158
159    return android_strdup(temp);
160}
161
162static char*
163_getSdkSystemImage( const char*  path, const char*  optionName, const char*  file )
164{
165    char*  image = _getSdkImage(path, file);
166
167    if (image == NULL) {
168        derror("Your system directory is missing the '%s' image file.\n"
169               "Please specify one with the '%s <filepath>' option",
170               file, optionName);
171        exit(2);
172    }
173    return image;
174}
175
176void sanitizeOptions( AndroidOptions* opts )
177{
178    /* legacy support: we used to use -system <dir> and -image <file>
179     * instead of -sysdir <dir> and -system <file>, so handle this by checking
180     * whether the options point to directories or files.
181     */
182    if (opts->image != NULL) {
183        if (opts->system != NULL) {
184            if (opts->sysdir != NULL) {
185                derror( "You can't use -sysdir, -system and -image at the same time.\n"
186                        "You should probably use '-sysdir <path> -system <file>'.\n" );
187                exit(2);
188            }
189        }
190        dwarning( "Please note that -image is obsolete and that -system is now used to point\n"
191                  "to the system image. Next time, try using '-sysdir <path> -system <file>' instead.\n" );
192        opts->sysdir = opts->system;
193        opts->system = opts->image;
194        opts->image  = NULL;
195    }
196    else if (opts->system != NULL && path_is_dir(opts->system)) {
197        if (opts->sysdir != NULL) {
198            derror( "Option -system should now be followed by a file path, not a directory one.\n"
199                    "Please use '-sysdir <path>' to point to the system directory.\n" );
200            exit(1);
201        }
202        dwarning( "Please note that the -system option should now be used to point to the initial\n"
203                  "system image (like the obsolete -image option). To point to the system directory\n"
204                  "please now use '-sysdir <path>' instead.\n" );
205
206        opts->sysdir = opts->system;
207        opts->system = NULL;
208    }
209
210    if (opts->nojni) {
211        opts->no_jni = opts->nojni;
212        opts->nojni  = 0;
213    }
214
215    if (opts->nocache) {
216        opts->no_cache = opts->nocache;
217        opts->nocache  = 0;
218    }
219
220    if (opts->noaudio) {
221        opts->no_audio = opts->noaudio;
222        opts->noaudio  = 0;
223    }
224
225    if (opts->noskin) {
226        opts->no_skin = opts->noskin;
227        opts->noskin  = 0;
228    }
229
230    /* If -no-cache is used, ignore any -cache argument */
231    if (opts->no_cache) {
232        opts->cache = 0;
233    }
234
235    /* the purpose of -no-audio is to disable sound output from the emulator,
236     * not to disable Audio emulation. So simply force the 'none' backends */
237    if (opts->no_audio)
238        opts->audio = "none";
239
240    /* we don't accept -skindir without -skin now
241     * to simplify the autoconfig stuff with virtual devices
242     */
243    if (opts->no_skin) {
244        opts->skin    = "320x480";
245        opts->skindir = NULL;
246    }
247
248    if (opts->skindir) {
249        if (!opts->skin) {
250            derror( "the -skindir <path> option requires a -skin <name> option");
251            exit(1);
252        }
253    }
254
255    if (opts->bootchart) {
256        char*  end;
257        int    timeout = strtol(opts->bootchart, &end, 10);
258        if (timeout == 0)
259            opts->bootchart = NULL;
260        else if (timeout < 0 || timeout > 15*60) {
261            derror( "timeout specified for -bootchart option is invalid.\n"
262                    "please use integers between 1 and 900\n");
263            exit(1);
264        }
265    }
266}
267
268AvdInfo* createAVD(AndroidOptions* opts, int* inAndroidBuild)
269{
270    AvdInfo* ret = NULL;
271    char   tmp[MAX_PATH];
272    char*  tmpend = tmp + sizeof(tmp);
273    char*  android_build_root = NULL;
274    char*  android_build_out  = NULL;
275
276    /* If no AVD name was given, try to find the top of the
277     * Android build tree
278     */
279    if (opts->avd == NULL) {
280        do {
281            char*  out = getenv("ANDROID_PRODUCT_OUT");
282
283            if (out == NULL || out[0] == 0)
284                break;
285
286            if (!path_exists(out)) {
287                derror("Can't access ANDROID_PRODUCT_OUT as '%s'\n"
288                    "You need to build the Android system before launching the emulator",
289                    out);
290                exit(2);
291            }
292
293            android_build_root = getenv("ANDROID_BUILD_TOP");
294            if (android_build_root == NULL || android_build_root[0] == 0)
295                break;
296
297            if (!path_exists(android_build_root)) {
298                derror("Can't find the Android build root '%s'\n"
299                    "Please check the definition of the ANDROID_BUILD_TOP variable.\n"
300                    "It should point to the root of your source tree.\n",
301                    android_build_root );
302                exit(2);
303            }
304            android_build_out = out;
305            D( "found Android build root: %s", android_build_root );
306            D( "found Android build out:  %s", android_build_out );
307        } while (0);
308    }
309    /* if no virtual device name is given, and we're not in the
310     * Android build system, we'll need to perform some auto-detection
311     * magic :-)
312     */
313    if (opts->avd == NULL && !android_build_out)
314    {
315        if (!opts->sysdir) {
316            opts->sysdir = _getSdkImagePath("system.img");
317            if (!opts->sysdir) {
318                derror(
319                "You did not specify a virtual device name, and the system\n"
320                "directory could not be found.\n\n"
321                "If you are an Android SDK user, please use '@<name>' or '-avd <name>'\n"
322                "to start a given virtual device (see -help-avd for details).\n\n"
323
324                "Otherwise, follow the instructions in -help-disk-images to start the emulator\n"
325                );
326                exit(2);
327            }
328            D("autoconfig: -sysdir %s", opts->sysdir);
329        }
330
331        if (!opts->system) {
332            opts->system = _getSdkSystemImage(opts->sysdir, "-image", "system.img");
333            D("autoconfig: -system %s", opts->system);
334        }
335
336        if (!opts->kernel) {
337            opts->kernel = _getSdkSystemImage(opts->sysdir, "-kernel", "kernel-qemu");
338            D("autoconfig: -kernel %s", opts->kernel);
339        }
340
341        if (!opts->ramdisk) {
342            opts->ramdisk = _getSdkSystemImage(opts->sysdir, "-ramdisk", "ramdisk.img");
343            D("autoconfig: -ramdisk %s", opts->ramdisk);
344        }
345
346        /* if no data directory is specified, use the system directory */
347        if (!opts->datadir) {
348            opts->datadir   = android_strdup(opts->sysdir);
349            D("autoconfig: -datadir %s", opts->sysdir);
350        }
351
352        if (!opts->data) {
353            /* check for userdata-qemu.img in the data directory */
354            bufprint(tmp, tmpend, "%s/userdata-qemu.img", opts->datadir);
355            if (!path_exists(tmp)) {
356                derror(
357                "You did not provide the name of an Android Virtual Device\n"
358                "with the '-avd <name>' option. Read -help-avd for more information.\n\n"
359
360                "If you *really* want to *NOT* run an AVD, consider using '-data <file>'\n"
361                "to specify a data partition image file (I hope you know what you're doing).\n"
362                );
363                exit(2);
364            }
365
366            opts->data = android_strdup(tmp);
367            D("autoconfig: -data %s", opts->data);
368        }
369
370        if (!opts->snapstorage && opts->datadir) {
371            bufprint(tmp, tmpend, "%s/snapshots.img", opts->datadir);
372            if (path_exists(tmp)) {
373                opts->snapstorage = android_strdup(tmp);
374                D("autoconfig: -snapstorage %s", opts->snapstorage);
375            }
376        }
377    }
378
379    /* setup the virtual device differently depending on whether
380     * we are in the Android build system or not
381     */
382    if (opts->avd != NULL)
383    {
384        ret = avdInfo_new( opts->avd, android_avdParams );
385        if (ret == NULL) {
386            /* an error message has already been printed */
387            dprint("could not find virtual device named '%s'", opts->avd);
388            exit(1);
389        }
390    }
391    else
392    {
393        if (!android_build_out) {
394            android_build_out = android_build_root = opts->sysdir;
395        }
396        ret = avdInfo_newForAndroidBuild(
397                            android_build_root,
398                            android_build_out,
399                            android_avdParams );
400
401        if(ret == NULL) {
402            D("could not start virtual device\n");
403            exit(1);
404        }
405    }
406
407    if (android_build_out) {
408        *inAndroidBuild = 1;
409    } else {
410        *inAndroidBuild = 0;
411    }
412
413    return ret;
414}
415
416void handle_ui_options( AndroidOptions* opts )
417{
418    return;
419}
420
421int attach_ui_to_core( AndroidOptions* opts )
422{
423    return 0;
424}
425