info.c revision fc8ed80ba1362d2ce500003625e1c9c39f765661
1/* Copyright (C) 2008 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/info.h"
13#include "android/config/config.h"
14#include "android/utils/path.h"
15#include "android/utils/bufprint.h"
16#include "android/utils/filelock.h"
17#include "android/utils/tempfile.h"
18#include "android/utils/debug.h"
19#include "android/utils/dirscanner.h"
20#include <ctype.h>
21#include <stddef.h>
22#include <string.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <errno.h>
26
27/* global variables - see android/globals.h */
28AvdInfoParams   android_avdParams[1];
29AvdInfo*        android_avdInfo;
30
31/* for debugging */
32#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
33#define  DD(...)  VERBOSE_PRINT(avd_config,__VA_ARGS__)
34
35/* technical note on how all of this is supposed to work:
36 *
37 * Each AVD corresponds to a "content directory" that is used to
38 * store persistent disk images and configuration files. Most remarkable
39 * are:
40 *
41 * - a "config.ini" file used to hold configuration information for the
42 *   AVD
43 *
44 * - mandatory user data image ("userdata-qemu.img") and cache image
45 *   ("cache.img")
46 *
47 * - optional mutable system image ("system-qemu.img"), kernel image
48 *   ("kernel-qemu") and read-only ramdisk ("ramdisk.img")
49 *
50 * When starting up an AVD, the emulator looks for relevant disk images
51 * in the content directory. If it doesn't find a given image there, it
52 * will try to search in the list of system directories listed in the
53 * 'config.ini' file through one of the following (key,value) pairs:
54 *
55 *    images.sysdir.1 = <first search path>
56 *    images.sysdir.2 = <second search path>
57 *
58 * The search paths can be absolute, or relative to the root SDK installation
59 * path (which is determined from the emulator program's location, or from the
60 * ANDROID_SDK_ROOT environment variable).
61 *
62 * Individual image disk search patch can be over-riden on the command-line
63 * with one of the usual options.
64 */
65
66/* this is the subdirectory of $HOME/.android where all
67 * root configuration files (and default content directories)
68 * are located.
69 */
70#define  ANDROID_AVD_DIR    "avd"
71
72/* the prefix of config.ini keys that will be used for search directories
73 * of system images.
74 */
75#define  SEARCH_PREFIX   "image.sysdir."
76
77/* the maximum number of search path keys we're going to read from the
78 * config.ini file
79 */
80#define  MAX_SEARCH_PATHS  2
81
82/* the config.ini key that will be used to indicate the full relative
83 * path to the skin directory (including the skin name).
84 */
85#define  SKIN_PATH       "skin.path"
86
87/* default skin name */
88#define  SKIN_DEFAULT    "HVGA"
89
90/* the config.ini key that is used to indicate the absolute path
91 * to the SD Card image file, if you don't want to place it in
92 * the content directory.
93 */
94#define  SDCARD_PATH     "sdcard.path"
95
96/* certain disk image files are mounted read/write by the emulator
97 * to ensure that several emulators referencing the same files
98 * do not corrupt these files, we need to lock them and respond
99 * to collision depending on the image type.
100 *
101 * the enumeration below is used to record information about
102 * each image file path.
103 *
104 * READONLY means that the file will be mounted read-only
105 * and this doesn't need to be locked. must be first in list
106 *
107 * MUSTLOCK means that the file should be locked before
108 * being mounted by the emulator
109 *
110 * TEMPORARY means that the file has been copied to a
111 * temporary image, which can be mounted read/write
112 * but doesn't require locking.
113 */
114typedef enum {
115    IMAGE_STATE_READONLY,     /* unlocked */
116    IMAGE_STATE_MUSTLOCK,     /* must be locked */
117    IMAGE_STATE_LOCKED,       /* locked */
118    IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */
119    IMAGE_STATE_TEMPORARY,    /* copied to temp file (no lock needed) */
120} AvdImageState;
121
122struct AvdInfo {
123    /* for the Android build system case */
124    char      inAndroidBuild;
125    char*     androidOut;
126    char*     androidBuildRoot;
127
128    /* for the normal virtual device case */
129    char*     deviceName;
130    char*     sdkRootPath;
131    char      sdkRootPathFromEnv;
132    char*     searchPaths[ MAX_SEARCH_PATHS ];
133    int       numSearchPaths;
134    char*     contentPath;
135    IniFile*  rootIni;      /* root <foo>.ini file */
136    IniFile*  configIni;    /* virtual device's config.ini */
137    IniFile*  hardwareIni;  /* skin-specific hardware.ini */
138
139    /* for both */
140    char*     skinName;     /* skin name */
141    char*     skinDirPath;  /* skin directory */
142
143    /* image files */
144    char*     imagePath [ AVD_IMAGE_MAX ];
145    char      imageState[ AVD_IMAGE_MAX ];
146};
147
148
149void
150avdInfo_free( AvdInfo*  i )
151{
152    if (i) {
153        int  nn;
154
155        for (nn = 0; nn < AVD_IMAGE_MAX; nn++)
156            AFREE(i->imagePath[nn]);
157
158        AFREE(i->skinName);
159        AFREE(i->skinDirPath);
160
161        for (nn = 0; nn < i->numSearchPaths; nn++)
162            AFREE(i->searchPaths[nn]);
163
164        i->numSearchPaths = 0;
165
166        if (i->configIni) {
167            iniFile_free(i->configIni);
168            i->configIni = NULL;
169        }
170
171        if (i->hardwareIni) {
172            iniFile_free(i->hardwareIni);
173            i->hardwareIni = NULL;
174        }
175
176        if (i->rootIni) {
177            iniFile_free(i->rootIni);
178            i->rootIni = NULL;
179        }
180
181        AFREE(i->contentPath);
182        AFREE(i->sdkRootPath);
183
184        if (i->inAndroidBuild) {
185            AFREE(i->androidOut);
186            AFREE(i->androidBuildRoot);
187        }
188
189        AFREE(i->deviceName);
190        AFREE(i);
191    }
192}
193
194/* list of default file names for each supported image file type */
195static const char*  const  _imageFileNames[ AVD_IMAGE_MAX ] = {
196#define  _AVD_IMG(x,y,z)  y,
197    AVD_IMAGE_LIST
198#undef _AVD_IMG
199};
200
201/* list of short text description for each supported image file type */
202static const char*  const _imageFileText[ AVD_IMAGE_MAX ] = {
203#define  _AVD_IMG(x,y,z)  z,
204    AVD_IMAGE_LIST
205#undef _AVD_IMG
206};
207
208/***************************************************************
209 ***************************************************************
210 *****
211 *****    NORMAL VIRTUAL DEVICE SUPPORT
212 *****
213 *****/
214
215/* compute path to the root SDK directory
216 * assume we are in $SDKROOT/tools/emulator[.exe]
217 */
218static int
219_getSdkRoot( AvdInfo*  i )
220{
221    const char*  env;
222    char         temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
223
224#define  SDK_ROOT_ENV  "ANDROID_SDK_ROOT"
225
226    env = getenv(SDK_ROOT_ENV);
227    if (env != NULL && env[0] != 0) {
228        if (path_exists(env)) {
229            D("found " SDK_ROOT_ENV ": %s", env);
230            i->sdkRootPath = ASTRDUP(env);
231            i->sdkRootPathFromEnv = 1;
232            return 0;
233        }
234        D(SDK_ROOT_ENV " points to unknown directory: %s", env);
235    }
236
237    (void) bufprint_app_dir(temp, end);
238
239    i->sdkRootPath = path_parent(temp, 1);
240    if (i->sdkRootPath == NULL) {
241        derror("can't find root of SDK directory");
242        return -1;
243    }
244    D("found SDK root at %s", i->sdkRootPath);
245    return 0;
246}
247
248static void
249_getSearchPaths( AvdInfo*  i )
250{
251    char  temp[PATH_MAX], *p = temp, *end= p+sizeof temp;
252    int   nn, count = 0;
253
254
255
256    for (nn = 0; nn < MAX_SEARCH_PATHS; nn++) {
257        char*  path;
258
259        p = bufprint(temp, end, "%s%d", SEARCH_PREFIX, nn+1 );
260        if (p >= end)
261            continue;
262
263        path = iniFile_getString( i->configIni, temp );
264        if (path != NULL) {
265            DD("    found image search path: %s", path);
266            if (!path_is_absolute(path)) {
267                p = bufprint(temp, end, "%s/%s", i->sdkRootPath, path);
268                AFREE(path);
269                path = ASTRDUP(temp);
270            }
271            i->searchPaths[count++] = path;
272        }
273    }
274
275    i->numSearchPaths = count;
276    if (count == 0) {
277        derror("no search paths found in this AVD's configuration.\n"
278               "Weird, the AVD's config.ini file is malformed. Try re-creating it.\n");
279        exit(2);
280    }
281    else
282        DD("found a total of %d search paths for this AVD", count);
283}
284
285static int
286_checkAvdName( const char*  name )
287{
288    int  len  = strlen(name);
289    int  len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
290                             "abcdefghijklmnopqrstuvwxyz"
291                             "0123456789_.-");
292    return (len == len2);
293}
294
295/* parse the root config .ini file. it is located in
296 * ~/.android/avd/<name>.ini or Windows equivalent
297 */
298static int
299_getRootIni( AvdInfo*  i )
300{
301    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
302
303    p = bufprint_config_path(temp, end);
304    p = bufprint(p, end, "/" ANDROID_AVD_DIR "/%s.ini", i->deviceName);
305    if (p >= end) {
306        derror("device name too long");
307        return -1;
308    }
309
310    i->rootIni = iniFile_newFromFile(temp);
311    if (i->rootIni == NULL) {
312        derror("unknown virtual device name: '%s'", i->deviceName);
313        return -1;
314    }
315    D("root virtual device file at %s", temp);
316    return 0;
317}
318
319/* the .ini variable name that points to the content directory
320 * in a root AVD ini file. This is required */
321#   define  ROOT_PATH_KEY    "path"
322
323static int
324_getContentPath( AvdInfo*  i )
325{
326    i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY);
327
328    if (i->contentPath == NULL) {
329        derror("bad config: %s",
330               "virtual device file lacks a "ROOT_PATH_KEY" entry");
331        return -1;
332    }
333    D("virtual device content at %s", i->contentPath);
334    return 0;
335}
336
337/* find and parse the config.ini file from the content directory */
338static int
339_getConfigIni(AvdInfo*  i)
340{
341    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
342
343    p = bufprint(p, end, "%s/config.ini", i->contentPath);
344    if (p >= end) {
345        derror("can't access virtual device content directory");
346        return -1;
347    }
348
349#if 1   /* XXX: TODO: remove this in the future */
350    /* for now, allow a non-existing config.ini */
351    if (!path_exists(temp)) {
352        D("virtual device has no config file - no problem");
353        return 0;
354    }
355#endif
356
357    i->configIni = iniFile_newFromFile(temp);
358    if (i->configIni == NULL) {
359        derror("bad config: %s",
360               "virtual device directory lacks config.ini");
361        return -1;
362    }
363    D("virtual device config file: %s", temp);
364    return 0;
365}
366
367/***************************************************************
368 ***************************************************************
369 *****
370 *****    KERNEL/DISK IMAGE LOADER
371 *****
372 *****/
373
374/* a structure used to handle the loading of
375 * kernel/disk images.
376 */
377typedef struct {
378    AvdInfo*        info;
379    AvdInfoParams*  params;
380    AvdImageType    id;
381    const char*     imageFile;
382    const char*     imageText;
383    char**          pPath;
384    char*           pState;
385    char            temp[PATH_MAX];
386} ImageLoader;
387
388static void
389imageLoader_init( ImageLoader*  l, AvdInfo*  info, AvdInfoParams*  params )
390{
391    memset(l, 0, sizeof(*l));
392    l->info    = info;
393    l->params  = params;
394}
395
396/* set the type of the image to load */
397static void
398imageLoader_set( ImageLoader*  l, AvdImageType  id )
399{
400    l->id        = id;
401    l->imageFile = _imageFileNames[id];
402    l->imageText = _imageFileText[id];
403    l->pPath     = &l->info->imagePath[id];
404    l->pState    = &l->info->imageState[id];
405
406    l->pState[0] = IMAGE_STATE_READONLY;
407}
408
409/* change the image path */
410static char*
411imageLoader_setPath( ImageLoader*  l, const char*  path )
412{
413    path = path ? ASTRDUP(path) : NULL;
414
415    AFREE(l->pPath[0]);
416    l->pPath[0] = (char*) path;
417
418    return (char*) path;
419}
420
421static char*
422imageLoader_extractPath( ImageLoader*  l )
423{
424    char*  result = l->pPath[0];
425    l->pPath[0] = NULL;
426    return result;
427}
428
429/* flags used when loading images */
430enum {
431    IMAGE_REQUIRED          = (1<<0),  /* image is required */
432    IMAGE_SEARCH_SDK        = (1<<1),  /* search image in SDK */
433    IMAGE_EMPTY_IF_MISSING  = (1<<2),  /* create empty file if missing */
434    IMAGE_DONT_LOCK         = (1<<4),  /* don't try to lock image */
435    IMAGE_IGNORE_IF_LOCKED  = (1<<5),  /* ignore file if it's locked */
436};
437
438#define  IMAGE_OPTIONAL  0
439
440/* find an image from the SDK search directories.
441 * returns the full path or NULL if the file could not be found.
442 *
443 * note: this stores the result in the image's path as well
444 */
445static char*
446imageLoader_lookupSdk( ImageLoader*  l  )
447{
448    AvdInfo*     i     = l->info;
449    const char*  image = l->imageFile;
450    char*        temp  = l->temp, *p = temp, *end = p + sizeof(l->temp);
451
452    do {
453        /* try the search paths */
454        int  nn;
455
456        for (nn = 0; nn < i->numSearchPaths; nn++) {
457            const char* searchDir = i->searchPaths[nn];
458
459            p = bufprint(temp, end, "%s/%s", searchDir, image);
460            if (p < end && path_exists(temp)) {
461                DD("found %s in search dir: %s", image, searchDir);
462                goto FOUND;
463            }
464            DD("    no %s in search dir: %s", image, searchDir);
465        }
466
467        return NULL;
468
469    } while (0);
470
471FOUND:
472    l->pState[0] = IMAGE_STATE_READONLY;
473
474    return imageLoader_setPath(l, temp);
475}
476
477/* search for a file in the content directory.
478 * returns NULL if the file cannot be found.
479 *
480 * note that this formats l->temp with the file's path
481 * allowing you to retrieve it if the function returns NULL
482 */
483static char*
484imageLoader_lookupContent( ImageLoader*  l )
485{
486    AvdInfo*  i     = l->info;
487    char*     temp  = l->temp, *p = temp, *end = p + sizeof(l->temp);
488
489    p = bufprint(temp, end, "%s/%s", i->contentPath, l->imageFile);
490    if (p >= end) {
491        derror("content directory path too long");
492        exit(2);
493    }
494    if (!path_exists(temp)) {
495        DD("    no %s in content directory", l->imageFile);
496        return NULL;
497    }
498    DD("found %s in content directory", l->imageFile);
499
500    /* assume content image files must be locked */
501    l->pState[0] = IMAGE_STATE_MUSTLOCK;
502
503    return imageLoader_setPath(l, temp);
504}
505
506/* lock a file image depending on its state and user flags
507 * note that this clears l->pPath[0] if the lock could not
508 * be acquired and that IMAGE_IGNORE_IF_LOCKED is used.
509 */
510static void
511imageLoader_lock( ImageLoader*  l, unsigned  flags )
512{
513    const char*  path = l->pPath[0];
514
515    if (flags & IMAGE_DONT_LOCK)
516        return;
517
518    if (l->pState[0] != IMAGE_STATE_MUSTLOCK)
519        return;
520
521    D("    locking %s image at %s", l->imageText, path);
522
523    if (filelock_create(path) != NULL) {
524        /* succesful lock */
525        l->pState[0] = IMAGE_STATE_LOCKED;
526        return;
527    }
528
529    if (flags & IMAGE_IGNORE_IF_LOCKED) {
530        dwarning("ignoring locked %s image at %s", l->imageText, path);
531        imageLoader_setPath(l, NULL);
532        return;
533    }
534
535    derror("the %s image is used by another emulator. aborting",
536            l->imageText);
537    exit(2);
538}
539
540/* make a file image empty, this may require locking */
541static void
542imageLoader_empty( ImageLoader*  l, unsigned  flags )
543{
544    const char*  path;
545
546    imageLoader_lock(l, flags);
547
548    path = l->pPath[0];
549    if (path == NULL)  /* failed to lock, caller will handle it */
550        return;
551
552    if (path_empty_file(path) < 0) {
553        derror("could not create %s image at %s: %s",
554                l->imageText, path, strerror(errno));
555        exit(2);
556    }
557    l->pState[0] = IMAGE_STATE_LOCKED_EMPTY;
558}
559
560
561/* copy image file from a given source
562 * assumes locking is needed.
563 */
564static void
565imageLoader_copyFrom( ImageLoader*  l, const char*  srcPath )
566{
567    const char*  dstPath = NULL;
568
569    /* find destination file */
570    if (l->params) {
571        dstPath = l->params->forcePaths[l->id];
572    }
573    if (!dstPath) {
574        imageLoader_lookupContent(l);
575        dstPath = l->temp;
576    }
577
578    /* lock destination */
579    imageLoader_setPath(l, dstPath);
580    l->pState[0] = IMAGE_STATE_MUSTLOCK;
581    imageLoader_lock(l, 0);
582
583    /* make the copy */
584    if (path_copy_file(dstPath, srcPath) < 0) {
585        derror("can't initialize %s image from SDK: %s: %s",
586               l->imageText, dstPath, strerror(errno));
587        exit(2);
588    }
589}
590
591/* this will load and eventually lock and image file, depending
592 * on the flags being used. on exit, this function udpates
593 * l->pState[0] and l->pPath[0]
594 *
595 * returns the path to the file. Note that it returns NULL
596 * only if the file was optional and could not be found.
597 *
598 * if the file is required and missing, the function aborts
599 * the program.
600 */
601static char*
602imageLoader_load( ImageLoader*    l,
603                  unsigned        flags )
604{
605    const char*  path = NULL;
606
607    /* set image state */
608    l->pState[0] = (flags & IMAGE_DONT_LOCK) == 0
609                 ? IMAGE_STATE_MUSTLOCK
610                 : IMAGE_STATE_READONLY;
611
612    /* check user-provided path */
613    path = l->params->forcePaths[l->id];
614    if (path != NULL) {
615        imageLoader_setPath(l, path);
616        if (path_exists(path)) {
617            DD("found user-provided %s image: %s", l->imageText, l->imageFile);
618            goto EXIT;
619        }
620        D("user-provided %s image does not exist: %s",
621          l->imageText, path);
622
623        /* if the file is required, abort */
624        if (flags & IMAGE_REQUIRED) {
625            derror("user-provided %s image at %s doesn't exist",
626                    l->imageText, path);
627            exit(2);
628        }
629    }
630    else {
631        const char*  contentFile;
632
633        /* second, look in the content directory */
634        path = imageLoader_lookupContent(l);
635        if (path) goto EXIT;
636
637        contentFile = ASTRDUP(l->temp);
638
639        /* it's not there */
640        if (flags & IMAGE_SEARCH_SDK) {
641            /* third, look in the SDK directory */
642            path = imageLoader_lookupSdk(l);
643            if (path) {
644                AFREE((char*)contentFile);
645                goto EXIT;
646            }
647        }
648        DD("found no %s image (%s)", l->imageText, l->imageFile);
649
650        /* if the file is required, abort */
651        if (flags & IMAGE_REQUIRED) {
652            AvdInfo*  i = l->info;
653
654            derror("could not find required %s image (%s).",
655                   l->imageText, l->imageFile);
656
657            if (i->inAndroidBuild) {
658                dprint( "Did you build everything ?" );
659            } else if (!i->sdkRootPathFromEnv) {
660                dprint( "Maybe defining %s to point to a valid SDK "
661                        "installation path might help ?", SDK_ROOT_ENV );
662            } else {
663                dprint( "Your %s is probably wrong: %s", SDK_ROOT_ENV,
664                        i->sdkRootPath );
665            }
666            exit(2);
667        }
668
669        path = imageLoader_setPath(l, contentFile);
670        AFREE((char*)contentFile);
671    }
672
673    /* otherwise, do we need to create it ? */
674    if (flags & IMAGE_EMPTY_IF_MISSING) {
675        imageLoader_empty(l, flags);
676        return l->pPath[0];
677    }
678    return NULL;
679
680EXIT:
681    imageLoader_lock(l, flags);
682    return l->pPath[0];
683}
684
685/* Attempts to load an AVD image, but does not kill the process if loading
686 * fails.
687 */
688static void
689imageLoader_loadOptional( ImageLoader *l, AvdImageType img_type,
690                           const char *forcedPath )
691{
692    imageLoader_set (l, img_type);
693    imageLoader_load(l, IMAGE_OPTIONAL |
694                        IMAGE_IGNORE_IF_LOCKED);
695
696    /* if the file was not found, ignore it */
697    if (l->pPath[0] && !path_exists(l->pPath[0]))
698    {
699        D("ignoring non-existing %s at %s: %s",
700          l->imageText, l->pPath[0], strerror(errno));
701
702        /* if the user provided the path by hand, warn him. */
703        if (forcedPath != NULL)
704            dwarning("ignoring non-existing %s image", l->imageText);
705
706        imageLoader_setPath(l, NULL);
707    }
708}
709
710/* find the correct path of all image files we're going to need
711 * and lock the files that need it.
712 */
713static int
714_getImagePaths(AvdInfo*  i, AvdInfoParams*  params )
715{
716    int   wipeData    = (params->flags & AVDINFO_WIPE_DATA) != 0;
717    int   wipeCache   = (params->flags & AVDINFO_WIPE_CACHE) != 0;
718    int   noCache     = (params->flags & AVDINFO_NO_CACHE) != 0;
719    int   noSdCard    = (params->flags & AVDINFO_NO_SDCARD) != 0;
720#if CONFIG_ANDROID_SNAPSHOTS
721    int   noSnapshots = (params->flags & AVDINFO_NO_SNAPSHOTS) != 0;
722#endif
723
724    ImageLoader  l[1];
725
726    imageLoader_init(l, i, params);
727
728    /* pick up the kernel and ramdisk image files - these don't
729     * need a specific handling.
730     */
731    imageLoader_set ( l, AVD_IMAGE_KERNEL );
732    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
733
734    imageLoader_set ( l, AVD_IMAGE_RAMDISK );
735    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
736
737    /* the system image
738     *
739     * if there is one in the content directory just lock
740     * and use it.
741     */
742    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
743    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
744
745    /* the data partition - this one is special because if it
746     * is missing, we need to copy the initial image file into it.
747     *
748     * first, try to see if it is in the content directory
749     * (or the user-provided path)
750     */
751    imageLoader_set( l, AVD_IMAGE_USERDATA );
752    if ( !imageLoader_load( l, IMAGE_OPTIONAL |
753                               IMAGE_DONT_LOCK ) )
754    {
755        /* it's not, we're going to initialize it. simply
756         * forcing a data wipe should be enough */
757        D("initializing new data partition image: %s", l->pPath[0]);
758        wipeData = 1;
759    }
760
761    if (wipeData) {
762        /* find SDK source file */
763        const char*  srcPath;
764
765        imageLoader_set( l, AVD_IMAGE_INITDATA );
766        if (imageLoader_lookupSdk(l) == NULL) {
767            derror("can't locate initial %s image in SDK",
768                l->imageText);
769            exit(2);
770        }
771        srcPath = imageLoader_extractPath(l);
772
773        imageLoader_set( l, AVD_IMAGE_USERDATA );
774        imageLoader_copyFrom( l, srcPath );
775        AFREE((char*) srcPath);
776    }
777    else
778    {
779        /* lock the data partition image */
780        l->pState[0] = IMAGE_STATE_MUSTLOCK;
781        imageLoader_lock( l, 0 );
782    }
783
784    /* the cache partition: unless the user doesn't want one,
785     * we're going to create it in the content directory
786     */
787    if (!noCache) {
788        imageLoader_set (l, AVD_IMAGE_CACHE);
789        imageLoader_load(l, IMAGE_OPTIONAL |
790                            IMAGE_EMPTY_IF_MISSING );
791
792        if (wipeCache) {
793            if (path_empty_file(l->pPath[0]) < 0) {
794                derror("cannot wipe %s image at %s: %s",
795                       l->imageText, l->pPath[0],
796                       strerror(errno));
797                exit(2);
798            }
799        }
800    }
801
802    /* the SD Card image. unless the user doesn't want to, we're
803     * going to mount it if available. Note that if the image is
804     * already used, we must ignore it.
805     */
806    if (!noSdCard) {
807        imageLoader_loadOptional(l, AVD_IMAGE_SDCARD,
808                                 params->forcePaths[AVD_IMAGE_SDCARD]);
809    }
810
811#if CONFIG_ANDROID_SNAPSHOTS
812    /* the state snapshot image. Mounting behaviour identical to
813     * SD card.
814     */
815    if (!noSnapshots) {
816        imageLoader_loadOptional(l, AVD_IMAGE_SNAPSHOTS,
817                                 params->forcePaths[AVD_IMAGE_SNAPSHOTS]);
818    }
819#endif
820
821    return 0;
822}
823
824/* check that a given directory contains a valid skin.
825 * returns 1 on success, 0 on failure.
826 */
827static int
828_checkSkinPath( const char*  skinPath )
829{
830    char  temp[MAX_PATH], *p=temp, *end=p+sizeof(temp);
831
832    /* for now, if it has a 'layout' file, it is a valid skin path */
833    p = bufprint(temp, end, "%s/layout", skinPath);
834    if (p >= end || !path_exists(temp))
835        return 0;
836
837    return 1;
838}
839
840/* check that there is a skin named 'skinName' listed from 'skinDirRoot'
841 * this returns 1 on success, 0 on failure
842 * on success, the 'temp' buffer will get the path containing the real
843 * skin directory (after alias expansion), including the skin name.
844 */
845static int
846_checkSkinDir( char*        temp,
847               char*        end,
848               const char*  skinDirRoot,
849               const char*  skinName )
850{
851    DirScanner*  scanner;
852    char        *p;
853    int          result;
854
855    p = bufprint(temp, end, "%s/skins/%s",
856                 skinDirRoot, skinName);
857
858    if (p >= end || !path_exists(temp)) {
859        DD("    ignore bad skin directory %s", temp);
860        return 0;
861    }
862
863    /* first, is this a normal skin directory ? */
864    if (_checkSkinPath(temp)) {
865        /* yes */
866        DD("    found skin directory: %s", temp);
867        return 1;
868    }
869
870    /* second, is it an alias to another skin ? */
871    *p      = 0;
872    result  = 0;
873    scanner = dirScanner_new(temp);
874    if (scanner != NULL) {
875        for (;;) {
876            const char*  file = dirScanner_next(scanner);
877
878            if (file == NULL)
879                break;
880
881            if (strncmp(file, "alias-", 6) || file[6] == 0)
882                continue;
883
884            p = bufprint(temp, end, "%s/skins/%s",
885                            skinDirRoot, file+6);
886
887            if (p < end && _checkSkinPath(temp)) {
888                /* yes, it's an alias */
889                DD("    skin alias '%s' points to skin directory: %s",
890                   file+6, temp);
891                result = 1;
892                break;
893            }
894        }
895        dirScanner_free(scanner);
896    }
897    return result;
898}
899
900/* try to see if the skin name leads to a magic skin or skin path directly
901 * returns 1 on success, 0 on error.
902 * on success, this sets up 'skinDirPath' and 'skinName' in the AvdInfo.
903 */
904static int
905_getSkinPathFromName( AvdInfo*  i, const char*  skinName )
906{
907    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
908
909    /* if the skin name has the format 'NNNNxNNN' where
910    * NNN is a decimal value, then this is a 'magic' skin
911    * name that doesn't require a skin directory
912    */
913    if (isdigit(skinName[0])) {
914        int  width, height;
915        if (sscanf(skinName, "%dx%d", &width, &height) == 2) {
916            D("'magic' skin format detected: %s", skinName);
917            i->skinName    = ASTRDUP(skinName);
918            i->skinDirPath = NULL;
919            return 1;
920        }
921    }
922
923    /* is the skin name a direct path to the skin directory ? */
924    if (_checkSkinPath(skinName)) {
925        goto FOUND_IT;
926    }
927
928    /* is the skin name a relative path from the SDK root ? */
929    p = bufprint(temp, end, "%s/%s", i->sdkRootPath, skinName);
930    if (p < end && _checkSkinPath(temp)) {
931        skinName = temp;
932        goto FOUND_IT;
933    }
934
935    /* nope */
936    return 0;
937
938FOUND_IT:
939    if (path_split(skinName, &i->skinDirPath, &i->skinName) < 0) {
940        derror("malformed skin name: %s", skinName);
941        exit(2);
942    }
943    D("found skin '%s' in directory: %s", i->skinName, i->skinDirPath);
944    return 1;
945}
946
947/* return 0 on success, -1 on error */
948static int
949_getSkin( AvdInfo*  i, AvdInfoParams*  params )
950{
951    char*  skinName;
952    char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
953    char   explicitSkin = 1;
954
955    /* this function is used to compute the 'skinName' and 'skinDirPath'
956     * fields of the AvdInfo.
957     */
958
959    /* processing here is a bit tricky, so here's how it happens
960     *
961     * - command-line option '-skin <name>' can be used to specify the
962     *   name of a skin, to override the AVD settings.
963     *
964     * - skins are searched from <dir>/../skins for each <dir> in the
965     *   images search list, unless a '-skindir <path>' option has been
966     *   provided on the command-line
967     *
968     * - otherwise, the config.ini can also contain a SKIN_PATH key that
969     *   shall  give the full path to the skin directory, either relative
970     *   to the SDK root, or an absolute path.
971     *
972     * - skin names like '320x480' corresponds to "magic skins" that
973     *   simply display a framebuffer, without any ornaments of the
974     *   corresponding size. They do not correspond to any real skin
975     *   directory / files and are handled later. But they must be
976     *   recognized here and report a NULL skindir.
977     */
978    if (params->skinName) {
979        skinName = ASTRDUP(params->skinName);
980    } else {
981        skinName = iniFile_getString( i->configIni, SKIN_PATH );
982        explicitSkin = 0;
983    }
984
985    /* first, check that the skin name is not magic or a direct
986     * directory path
987     */
988    if (skinName != NULL && _getSkinPathFromName(i, skinName)) {
989        AFREE(skinName);
990        return 0;
991    }
992
993    /* if not, the default skinName is "HVGA" */
994    if (skinName == NULL) {
995        skinName = ASTRDUP(SKIN_DEFAULT);
996        explicitSkin = 0;
997    }
998
999    i->skinName = skinName;
1000
1001    /* now try to find the skin directory for that name -
1002     * first try the content directory */
1003    do {
1004        /* if there is a single 'skin' directory in
1005         * the content directory, assume that's what the
1006         * user wants,  unless an explicit name was given
1007         */
1008        if (!explicitSkin) {
1009            p = bufprint(temp, end, "%s/skin", i->contentPath);
1010            if (p < end && _checkSkinPath(temp)) {
1011                D("using skin content from %s", temp);
1012                AFREE(i->skinName);
1013                i->skinName    = ASTRDUP("skin");
1014                i->skinDirPath = ASTRDUP(i->contentPath);
1015                return 0;
1016            }
1017        }
1018
1019        /* look in content directory */
1020        if (_checkSkinDir(temp, end, i->contentPath, skinName))
1021            break;
1022
1023        /* look in the search paths. For each <dir> in the list,
1024         * look the skins in <dir>/.. */
1025        {
1026            int  nn;
1027            for (nn = 0; nn < i->numSearchPaths; nn++) {
1028                char*  parentDir = path_parent(i->searchPaths[nn], 1);
1029                int    ret;
1030                if (parentDir == NULL)
1031                    continue;
1032                ret=_checkSkinDir(temp, end, parentDir, skinName);
1033                AFREE(parentDir);
1034                if (ret)
1035                  break;
1036            }
1037            if (nn < i->numSearchPaths)
1038                break;
1039        }
1040
1041        /* didn't find it */
1042        if (explicitSkin) {
1043            derror("could not find directory for skin '%s',"
1044                   " please use a different name", skinName);
1045            exit(2);
1046        } else {
1047            dwarning("no skin directory matched '%s', so reverted to default",
1048                     skinName);
1049            AFREE(i->skinName);
1050            params->skinName = SKIN_DEFAULT;
1051            return _getSkin(i, params);
1052        }
1053
1054        return -1;
1055
1056    } while (0);
1057
1058    /* separate skin name from parent directory. the skin name
1059     * returned in 'temp' might be different from the original
1060     * one due to alias expansion so strip it.
1061     */
1062    AFREE(i->skinName);
1063
1064    if (path_split(temp, &i->skinDirPath, &i->skinName) < 0) {
1065        derror("weird skin path: %s", temp);
1066        return -1;
1067    }
1068    DD("found skin '%s' in directory: %s", i->skinName, i->skinDirPath);
1069    return 0;
1070}
1071
1072/* If the user didn't explicitely provide an SD Card path,
1073 * check the SDCARD_PATH key in config.ini and use that if
1074 * available.
1075 */
1076static void
1077_getSDCardPath( AvdInfo*  i, AvdInfoParams*  params )
1078{
1079    const char*  path;
1080
1081    if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL)
1082        return;
1083
1084    path = iniFile_getString(i->configIni, SDCARD_PATH);
1085    if (path == NULL)
1086        return;
1087
1088    params->forcePaths[AVD_IMAGE_SDCARD] = path;
1089}
1090
1091AvdInfo*
1092avdInfo_new( const char*  name, AvdInfoParams*  params )
1093{
1094    AvdInfo*  i;
1095
1096    if (name == NULL)
1097        return NULL;
1098
1099    if (!_checkAvdName(name)) {
1100        derror("virtual device name contains invalid characters");
1101        exit(1);
1102    }
1103
1104    ANEW0(i);
1105    i->deviceName = ASTRDUP(name);
1106
1107    if ( _getSdkRoot(i)     < 0 ||
1108         _getRootIni(i)     < 0 ||
1109         _getContentPath(i) < 0 ||
1110         _getConfigIni(i)   < 0 )
1111        goto FAIL;
1112
1113    /* look for image search paths. handle post 1.1/pre cupcake
1114     * obsolete SDKs.
1115     */
1116    _getSearchPaths(i);
1117    _getSDCardPath(i, params);
1118
1119    /* don't need this anymore */
1120    iniFile_free(i->rootIni);
1121    i->rootIni = NULL;
1122
1123    if ( _getImagePaths(i, params) < 0 ||
1124         _getSkin      (i, params) < 0 )
1125        goto FAIL;
1126
1127    return i;
1128
1129FAIL:
1130    avdInfo_free(i);
1131    return NULL;
1132}
1133
1134/***************************************************************
1135 ***************************************************************
1136 *****
1137 *****    ANDROID BUILD SUPPORT
1138 *****
1139 *****    The code below corresponds to the case where we're
1140 *****    starting the emulator inside the Android build
1141 *****    system. The main differences are that:
1142 *****
1143 *****    - the $ANDROID_PRODUCT_OUT directory is used as the
1144 *****      content file.
1145 *****
1146 *****    - built images must not be modified by the emulator,
1147 *****      so system.img must be copied to a temporary file
1148 *****      and userdata.img must be copied to userdata-qemu.img
1149 *****      if the latter doesn't exist.
1150 *****
1151 *****    - the kernel and default skin directory are taken from
1152 *****      prebuilt
1153 *****
1154 *****    - there is no root .ini file, or any config.ini in
1155 *****      the content directory, no SDK images search path.
1156 *****/
1157
1158/* used to fake a config.ini located in the content directory */
1159static int
1160_getBuildConfigIni( AvdInfo*  i )
1161{
1162    /* a blank file is ok at the moment */
1163    i->configIni = iniFile_newFromMemory( "", 0 );
1164    return 0;
1165}
1166
1167static int
1168_getBuildImagePaths( AvdInfo*  i, AvdInfoParams*  params )
1169{
1170    int   wipeData    = (params->flags & AVDINFO_WIPE_DATA) != 0;
1171    int   noCache     = (params->flags & AVDINFO_NO_CACHE) != 0;
1172    int   noSdCard    = (params->flags & AVDINFO_NO_SDCARD) != 0;
1173#if CONFIG_ANDROID_SNAPSHOTS
1174    int   noSnapshots = (params->flags & AVDINFO_NO_SNAPSHOTS) != 0;
1175#endif
1176
1177    char         temp[PATH_MAX], *p=temp, *end=p+sizeof temp;
1178    char*        srcData;
1179    ImageLoader  l[1];
1180
1181    imageLoader_init(l, i, params);
1182
1183    /** load the kernel image
1184     **/
1185
1186    /* if it is not in the out directory, get it from prebuilt
1187     */
1188    imageLoader_set ( l, AVD_IMAGE_KERNEL );
1189
1190    if ( !imageLoader_load( l, IMAGE_OPTIONAL |
1191                               IMAGE_DONT_LOCK ) )
1192    {
1193#ifdef TARGET_ARM
1194#define  PREBUILT_KERNEL_PATH   "prebuilt/android-arm/kernel/kernel-qemu"
1195#endif
1196#ifdef TARGET_I386
1197#define  PREBUILT_KERNEL_PATH   "prebuilt/android-x86/kernel/kernel-qemu"
1198#endif
1199        p = bufprint(temp, end, "%s/%s", i->androidBuildRoot,
1200                        PREBUILT_KERNEL_PATH);
1201        if (p >= end || !path_exists(temp)) {
1202            derror("bad workspace: cannot find prebuilt kernel in: %s", temp);
1203            exit(1);
1204        }
1205        imageLoader_setPath(l, temp);
1206    }
1207
1208    /** load the data partition. note that we use userdata-qemu.img
1209     ** since we don't want to modify userdata.img at all
1210     **/
1211    imageLoader_set ( l, AVD_IMAGE_USERDATA );
1212    imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK );
1213
1214    /* get the path of the source file, and check that it actually exists
1215     * if the user didn't provide an explicit data file
1216     */
1217    srcData = imageLoader_extractPath(l);
1218    if (srcData == NULL && params->forcePaths[AVD_IMAGE_USERDATA] == NULL) {
1219        derror("There is no %s image in your build directory. Please make a full build",
1220                l->imageText, l->imageFile);
1221        exit(2);
1222    }
1223
1224    /* get the path of the target file */
1225    l->imageFile = "userdata-qemu.img";
1226    imageLoader_load( l, IMAGE_OPTIONAL |
1227                         IMAGE_EMPTY_IF_MISSING |
1228                         IMAGE_IGNORE_IF_LOCKED );
1229
1230    /* force a data wipe if we just created the image */
1231    if (l->pState[0] == IMAGE_STATE_LOCKED_EMPTY)
1232        wipeData = 1;
1233
1234    /* if the image was already locked, create a temp file
1235     * then force a data wipe.
1236     */
1237    if (l->pPath[0] == NULL) {
1238        TempFile*  temp = tempfile_create();
1239        imageLoader_setPath(l, tempfile_path(temp));
1240        dwarning( "Another emulator is running. user data changes will *NOT* be saved");
1241        wipeData = 1;
1242    }
1243
1244    /* in the case of a data wipe, copy userdata.img into
1245     * the destination */
1246    if (wipeData) {
1247        if (srcData == NULL || !path_exists(srcData)) {
1248            derror("There is no %s image in your build directory. Please make a full build",
1249                   l->imageText, _imageFileNames[l->id]);
1250            exit(2);
1251        }
1252        if (path_copy_file( l->pPath[0], srcData ) < 0) {
1253            derror("could not initialize %s image from %s: %s",
1254                   l->imageText, temp, strerror(errno));
1255            exit(2);
1256        }
1257    }
1258
1259    AFREE(srcData);
1260
1261    /** load the ramdisk image
1262     **/
1263    imageLoader_set ( l, AVD_IMAGE_RAMDISK );
1264    imageLoader_load( l, IMAGE_REQUIRED |
1265                         IMAGE_DONT_LOCK );
1266
1267    /** load the system image. read-only. the caller must
1268     ** take care of checking the state
1269     **/
1270    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
1271    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK );
1272
1273    /* force the system image to read-only status */
1274    l->pState[0] = IMAGE_STATE_READONLY;
1275
1276    /** cache partition handling
1277     **/
1278    if (!noCache) {
1279        imageLoader_set (l, AVD_IMAGE_CACHE);
1280
1281        /* if the user provided one cache image, lock & use it */
1282        if ( params->forcePaths[l->id] != NULL ) {
1283            imageLoader_load(l, IMAGE_REQUIRED |
1284                                IMAGE_IGNORE_IF_LOCKED);
1285        }
1286    }
1287
1288    /** SD Card image
1289     **/
1290    if (!noSdCard) {
1291        imageLoader_set (l, AVD_IMAGE_SDCARD);
1292        imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED);
1293    }
1294
1295#if CONFIG_ANDROID_SNAPSHOTS
1296    /** State snapshots image
1297     **/
1298    if (!noSnapshots) {
1299        imageLoader_set (l, AVD_IMAGE_SNAPSHOTS);
1300        imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED);
1301    }
1302#endif
1303
1304    return 0;
1305}
1306
1307static int
1308_getBuildSkin( AvdInfo*  i, AvdInfoParams*  params )
1309{
1310    /* the (current) default skin name for our build system */
1311    const char*  skinName = params->skinName;
1312    const char*  skinDir  = params->skinRootPath;
1313    char         temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
1314    char*        q;
1315
1316    if (!skinName) {
1317        /* the (current) default skin name for the build system */
1318        skinName = SKIN_DEFAULT;
1319        D("selecting default skin name '%s'", skinName);
1320    }
1321
1322    if (!skinDir) {
1323
1324#define  PREBUILT_SKINS_DIR  "development/tools/emulator/skins"
1325#define  PRODUCT_SKIN_DIR "skin"
1326
1327        do {
1328            /* look for the product skin in $ANDROID_PRODUCT_OUT/skin if no skin name is defined */
1329            if (!params->skinName) {
1330                /* look for <product_out>/skin first */
1331                p = bufprint( temp, end, "%s/skin",
1332                              i->androidOut );
1333                if (path_exists(temp)) {
1334                    p = bufprint( temp, end, "%s",
1335                                  i->androidOut );
1336                    skinName = PRODUCT_SKIN_DIR;
1337                    D("selecting default product skin at '%s/%s'", temp, skinName);
1338                    break;
1339                }
1340            }
1341
1342            /* next try in <sysdir>/../skins */
1343            p = bufprint( temp, end, "%s/../skins",
1344                          i->androidBuildRoot );
1345            if (path_exists(temp))
1346                break;
1347
1348            /* the (current) default skin directory */
1349            p = bufprint( temp, end, "%s/%s",
1350                        i->androidBuildRoot, PREBUILT_SKINS_DIR );
1351        } while (0);
1352
1353    } else {
1354        p = bufprint( temp, end, "%s", skinDir );
1355    }
1356
1357    i->skinName = ASTRDUP(skinName);
1358
1359    q  = bufprint(p, end, "/%s/layout", skinName);
1360    if (q >= end || !path_exists(temp)) {
1361        DD("skin content directory does not exist: %s", temp);
1362        if (skinDir)
1363            dwarning("could not find valid skin '%s' in %s:\n",
1364                     skinName, temp);
1365        return -1;
1366    }
1367    *p = 0;
1368    DD("found skin path: %s", temp);
1369    i->skinDirPath = ASTRDUP(temp);
1370
1371    return 0;
1372}
1373
1374/* Read a hardware.ini if it is located in the skin directory */
1375static int
1376_getBuildHardwareIni( AvdInfo*  i )
1377{
1378    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
1379
1380    if (i->skinDirPath == NULL || i->skinName == NULL)
1381        return 0;
1382
1383    p = bufprint(temp, end, "%s/%s/hardware.ini", i->skinDirPath, i->skinName);
1384    if (p >= end || !path_exists(temp)) {
1385        DD("no skin-specific hardware.ini in %s", i->skinDirPath);
1386        return 0;
1387    }
1388
1389    D("found skin-specific hardware.ini: %s", temp);
1390    i->hardwareIni = iniFile_newFromFile(temp);
1391    if (i->hardwareIni == NULL)
1392        return -1;
1393
1394    return 0;
1395}
1396
1397
1398AvdInfo*
1399avdInfo_newForAndroidBuild( const char*     androidBuildRoot,
1400                            const char*     androidOut,
1401                            AvdInfoParams*  params )
1402{
1403    AvdInfo*  i;
1404
1405    ANEW0(i);
1406
1407    i->inAndroidBuild   = 1;
1408    i->androidBuildRoot = ASTRDUP(androidBuildRoot);
1409    i->androidOut       = ASTRDUP(androidOut);
1410    i->contentPath      = ASTRDUP(androidOut);
1411
1412    /* TODO: find a way to provide better information from the build files */
1413    i->deviceName = ASTRDUP("<build>");
1414
1415    if (_getBuildConfigIni(i)          < 0 ||
1416        _getBuildImagePaths(i, params) < 0 )
1417        goto FAIL;
1418
1419    /* we don't need to fail if there is no valid skin */
1420    _getBuildSkin(i, params);
1421    _getBuildHardwareIni(i);
1422
1423    return i;
1424
1425FAIL:
1426    avdInfo_free(i);
1427    return NULL;
1428}
1429
1430const char*
1431avdInfo_getName( AvdInfo*  i )
1432{
1433    return i ? i->deviceName : NULL;
1434}
1435
1436const char*
1437avdInfo_getImageFile( AvdInfo*  i, AvdImageType  imageType )
1438{
1439    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
1440        return NULL;
1441
1442    return i->imagePath[imageType];
1443}
1444
1445uint64_t
1446avdInfo_getImageFileSize( AvdInfo*  i, AvdImageType  imageType )
1447{
1448    const char* file = avdInfo_getImageFile(i, imageType);
1449    uint64_t    size;
1450
1451    if (file == NULL)
1452        return 0ULL;
1453
1454    if (path_get_size(file, &size) < 0)
1455        return 0ULL;
1456
1457    return size;
1458}
1459
1460int
1461avdInfo_isImageReadOnly( AvdInfo*  i, AvdImageType  imageType )
1462{
1463    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
1464        return 1;
1465
1466    return (i->imageState[imageType] == IMAGE_STATE_READONLY);
1467}
1468
1469const char*
1470avdInfo_getSkinName( AvdInfo*  i )
1471{
1472    return i->skinName;
1473}
1474
1475const char*
1476avdInfo_getSkinDir ( AvdInfo*  i )
1477{
1478    return i->skinDirPath;
1479}
1480
1481int
1482avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw )
1483{
1484    IniFile*   ini = i->configIni;
1485    int        ret;
1486
1487    if (ini == NULL)
1488        ini = iniFile_newFromMemory("", 0);
1489
1490    ret = androidHwConfig_read(hw, ini);
1491
1492    if (ini != i->configIni)
1493        iniFile_free(ini);
1494
1495    if (ret == 0 && i->hardwareIni != NULL) {
1496        ret = androidHwConfig_read(hw, i->hardwareIni);
1497    }
1498
1499    /* special product-specific hardware configuration */
1500    if (i->androidOut != NULL)
1501    {
1502        char*  p = strrchr(i->androidOut, '/');
1503        if (p != NULL && p[0] != 0) {
1504            if (p[1] == 's') {
1505                hw->hw_keyboard = 0;
1506            }
1507        }
1508    }
1509
1510    return ret;
1511}
1512
1513const char*
1514avdInfo_getContentPath( AvdInfo*  i )
1515{
1516    return i->contentPath;
1517}
1518
1519int
1520avdInfo_inAndroidBuild( AvdInfo*  i )
1521{
1522    return i->inAndroidBuild;
1523}
1524
1525char*
1526avdInfo_getTracePath( AvdInfo*  i, const char*  traceName )
1527{
1528    char   tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp);
1529
1530    if (i == NULL || traceName == NULL || traceName[0] == 0)
1531        return NULL;
1532
1533    if (i->inAndroidBuild) {
1534        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
1535                      i->androidOut, traceName );
1536    } else {
1537        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
1538                      i->contentPath, traceName );
1539    }
1540    return ASTRDUP(tmp);
1541}
1542