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/avd/util.h"
14#include "android/avd/keys.h"
15#include "android/config/config.h"
16#include "android/utils/path.h"
17#include "android/utils/bufprint.h"
18#include "android/utils/filelock.h"
19#include "android/utils/tempfile.h"
20#include "android/utils/debug.h"
21#include "android/utils/dirscanner.h"
22#include <ctype.h>
23#include <stddef.h>
24#include <string.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <errno.h>
28
29/* global variables - see android/globals.h */
30AvdInfoParams   android_avdParams[1];
31AvdInfo*        android_avdInfo;
32
33/* for debugging */
34#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
35#define  DD(...)  VERBOSE_PRINT(avd_config,__VA_ARGS__)
36
37/* technical note on how all of this is supposed to work:
38 *
39 * Each AVD corresponds to a "content directory" that is used to
40 * store persistent disk images and configuration files. Most remarkable
41 * are:
42 *
43 * - a "config.ini" file used to hold configuration information for the
44 *   AVD
45 *
46 * - mandatory user data image ("userdata-qemu.img") and cache image
47 *   ("cache.img")
48 *
49 * - optional mutable system image ("system-qemu.img"), kernel image
50 *   ("kernel-qemu") and read-only ramdisk ("ramdisk.img")
51 *
52 * When starting up an AVD, the emulator looks for relevant disk images
53 * in the content directory. If it doesn't find a given image there, it
54 * will try to search in the list of system directories listed in the
55 * 'config.ini' file through one of the following (key,value) pairs:
56 *
57 *    images.sysdir.1 = <first search path>
58 *    images.sysdir.2 = <second search path>
59 *
60 * The search paths can be absolute, or relative to the root SDK installation
61 * path (which is determined from the emulator program's location, or from the
62 * ANDROID_SDK_ROOT environment variable).
63 *
64 * Individual image disk search patch can be over-riden on the command-line
65 * with one of the usual options.
66 */
67
68/* the name of the .ini file that will contain the complete hardware
69 * properties for the AVD. This will be used to launch the corresponding
70 * core from the UI.
71 */
72#define  CORE_HARDWARE_INI   "hardware-qemu.ini"
73
74/* certain disk image files are mounted read/write by the emulator
75 * to ensure that several emulators referencing the same files
76 * do not corrupt these files, we need to lock them and respond
77 * to collision depending on the image type.
78 *
79 * the enumeration below is used to record information about
80 * each image file path.
81 *
82 * READONLY means that the file will be mounted read-only
83 * and this doesn't need to be locked. must be first in list
84 *
85 * MUSTLOCK means that the file should be locked before
86 * being mounted by the emulator
87 *
88 * TEMPORARY means that the file has been copied to a
89 * temporary image, which can be mounted read/write
90 * but doesn't require locking.
91 */
92typedef enum {
93    IMAGE_STATE_READONLY,     /* unlocked */
94    IMAGE_STATE_MUSTLOCK,     /* must be locked */
95    IMAGE_STATE_LOCKED,       /* locked */
96    IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */
97    IMAGE_STATE_TEMPORARY,    /* copied to temp file (no lock needed) */
98} AvdImageState;
99
100struct AvdInfo {
101    /* for the Android build system case */
102    char      inAndroidBuild;
103    char*     androidOut;
104    char*     androidBuildRoot;
105    char*     targetArch;
106
107    /* for the normal virtual device case */
108    char*     deviceName;
109    char*     sdkRootPath;
110    char      sdkRootPathFromEnv;
111    char*     searchPaths[ MAX_SEARCH_PATHS ];
112    int       numSearchPaths;
113    char*     contentPath;
114    IniFile*  rootIni;      /* root <foo>.ini file, empty if missing */
115    IniFile*  configIni;    /* virtual device's config.ini, NULL if missing */
116    IniFile*  skinHardwareIni;  /* skin-specific hardware.ini */
117
118    /* for both */
119    int       apiLevel;
120    char*     skinName;     /* skin name */
121    char*     skinDirPath;  /* skin directory */
122    char*     coreHardwareIniPath;  /* core hardware.ini path */
123
124    /* image files */
125    char*     imagePath [ AVD_IMAGE_MAX ];
126    char      imageState[ AVD_IMAGE_MAX ];
127};
128
129
130void
131avdInfo_free( AvdInfo*  i )
132{
133    if (i) {
134        int  nn;
135
136        for (nn = 0; nn < AVD_IMAGE_MAX; nn++)
137            AFREE(i->imagePath[nn]);
138
139        AFREE(i->skinName);
140        AFREE(i->skinDirPath);
141        AFREE(i->coreHardwareIniPath);
142
143        for (nn = 0; nn < i->numSearchPaths; nn++)
144            AFREE(i->searchPaths[nn]);
145
146        i->numSearchPaths = 0;
147
148        if (i->configIni) {
149            iniFile_free(i->configIni);
150            i->configIni = NULL;
151        }
152
153        if (i->skinHardwareIni) {
154            iniFile_free(i->skinHardwareIni);
155            i->skinHardwareIni = NULL;
156        }
157
158        if (i->rootIni) {
159            iniFile_free(i->rootIni);
160            i->rootIni = NULL;
161        }
162
163        AFREE(i->contentPath);
164        AFREE(i->sdkRootPath);
165
166        if (i->inAndroidBuild) {
167            AFREE(i->androidOut);
168            AFREE(i->androidBuildRoot);
169        }
170
171        AFREE(i->deviceName);
172        AFREE(i);
173    }
174}
175
176/* list of default file names for each supported image file type */
177static const char*  const  _imageFileNames[ AVD_IMAGE_MAX ] = {
178#define  _AVD_IMG(x,y,z)  y,
179    AVD_IMAGE_LIST
180#undef _AVD_IMG
181};
182
183/* list of short text description for each supported image file type */
184static const char*  const _imageFileText[ AVD_IMAGE_MAX ] = {
185#define  _AVD_IMG(x,y,z)  z,
186    AVD_IMAGE_LIST
187#undef _AVD_IMG
188};
189
190/***************************************************************
191 ***************************************************************
192 *****
193 *****    UTILITY FUNCTIONS
194 *****
195 *****  The following functions do not depend on the AvdInfo
196 *****  structure and could easily be moved elsewhere.
197 *****
198 *****/
199
200/* Parse a given config.ini file and extract the list of SDK search paths
201 * from it. Returns the number of valid paths stored in 'searchPaths', or -1
202 * in case of problem.
203 *
204 * Relative search paths in the config.ini will be stored as full pathnames
205 * relative to 'sdkRootPath'.
206 *
207 * 'searchPaths' must be an array of char* pointers of at most 'maxSearchPaths'
208 * entries.
209 */
210static int
211_getSearchPaths( IniFile*    configIni,
212                 const char* sdkRootPath,
213                 int         maxSearchPaths,
214                 char**      searchPaths )
215{
216    char  temp[PATH_MAX], *p = temp, *end= p+sizeof temp;
217    int   nn, count = 0;
218
219    for (nn = 0; nn < maxSearchPaths; nn++) {
220        char*  path;
221
222        p = bufprint(temp, end, "%s%d", SEARCH_PREFIX, nn+1 );
223        if (p >= end)
224            continue;
225
226        path = iniFile_getString(configIni, temp, NULL);
227        if (path != NULL) {
228            DD("    found image search path: %s", path);
229            if (!path_is_absolute(path)) {
230                p = bufprint(temp, end, "%s/%s", sdkRootPath, path);
231                AFREE(path);
232                path = ASTRDUP(temp);
233            }
234            searchPaths[count++] = path;
235        }
236    }
237    return count;
238}
239
240/* Check that an AVD name is valid. Returns 1 on success, 0 otherwise.
241 */
242static int
243_checkAvdName( const char*  name )
244{
245    int  len  = strlen(name);
246    int  len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
247                             "abcdefghijklmnopqrstuvwxyz"
248                             "0123456789_.-");
249    return (len == len2);
250}
251
252/* Returns the full path of a given file.
253 *
254 * If 'fileName' is an absolute path, this returns a simple copy.
255 * Otherwise, this returns a new string corresponding to <rootPath>/<fileName>
256 *
257 * This returns NULL if the paths are too long.
258 */
259static char*
260_getFullFilePath( const char* rootPath, const char* fileName )
261{
262    if (path_is_absolute(fileName)) {
263        return ASTRDUP(fileName);
264    } else {
265        char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
266
267        p = bufprint(temp, end, "%s/%s", rootPath, fileName);
268        if (p >= end) {
269            return NULL;
270        }
271        return ASTRDUP(temp);
272    }
273}
274
275/* check that a given directory contains a valid skin.
276 * returns 1 on success, 0 on failure.
277 */
278static int
279_checkSkinPath( const char*  skinPath )
280{
281    char  temp[MAX_PATH], *p=temp, *end=p+sizeof(temp);
282
283    /* for now, if it has a 'layout' file, it is a valid skin path */
284    p = bufprint(temp, end, "%s/layout", skinPath);
285    if (p >= end || !path_exists(temp))
286        return 0;
287
288    return 1;
289}
290
291/* Check that there is a skin named 'skinName' listed from 'skinDirRoot'
292 * this returns the full path of the skin directory (after alias expansions),
293 * including the skin name, or NULL on failure.
294 */
295static char*
296_checkSkinSkinsDir( const char*  skinDirRoot,
297                    const char*  skinName )
298{
299    DirScanner*  scanner;
300    char*        result;
301    char         temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
302
303    p = bufprint(temp, end, "%s/skins/%s", skinDirRoot, skinName);
304    DD("Probing skin directory: %s", temp);
305    if (p >= end || !path_exists(temp)) {
306        DD("    ignore bad skin directory %s", temp);
307        return NULL;
308    }
309
310    /* first, is this a normal skin directory ? */
311    if (_checkSkinPath(temp)) {
312        /* yes */
313        DD("    found skin directory: %s", temp);
314        return ASTRDUP(temp);
315    }
316
317    /* second, is it an alias to another skin ? */
318    *p      = 0;
319    result  = NULL;
320    scanner = dirScanner_new(temp);
321    if (scanner != NULL) {
322        for (;;) {
323            const char*  file = dirScanner_next(scanner);
324
325            if (file == NULL)
326                break;
327
328            if (strncmp(file, "alias-", 6) || file[6] == 0)
329                continue;
330
331            p = bufprint(temp, end, "%s/skins/%s", skinDirRoot, file+6);
332            if (p < end && _checkSkinPath(temp)) {
333                /* yes, it's an alias */
334                DD("    skin alias '%s' points to skin directory: %s",
335                   file+6, temp);
336                result = ASTRDUP(temp);
337                break;
338            }
339        }
340        dirScanner_free(scanner);
341    }
342    return result;
343}
344
345/* try to see if the skin name leads to a magic skin or skin path directly
346 * returns 1 on success, 0 on error.
347 *
348 * on success, this sets up '*pSkinName' and '*pSkinDir'
349 */
350static int
351_getSkinPathFromName( const char*  skinName,
352                      const char*  sdkRootPath,
353                      char**       pSkinName,
354                      char**       pSkinDir )
355{
356    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
357
358    /* if the skin name has the format 'NNNNxNNN' where
359    * NNN is a decimal value, then this is a 'magic' skin
360    * name that doesn't require a skin directory
361    */
362    if (isdigit(skinName[0])) {
363        int  width, height;
364        if (sscanf(skinName, "%dx%d", &width, &height) == 2) {
365            D("'magic' skin format detected: %s", skinName);
366            *pSkinName = ASTRDUP(skinName);
367            *pSkinDir  = NULL;
368            return 1;
369        }
370    }
371
372    /* is the skin name a direct path to the skin directory ? */
373    if (path_is_absolute(skinName) && _checkSkinPath(skinName)) {
374        goto FOUND_IT;
375    }
376
377    /* is the skin name a relative path from the SDK root ? */
378    p = bufprint(temp, end, "%s/%s", sdkRootPath, skinName);
379    if (p < end && _checkSkinPath(temp)) {
380        skinName = temp;
381        goto FOUND_IT;
382    }
383
384    /* nope */
385    return 0;
386
387FOUND_IT:
388    if (path_split(skinName, pSkinDir, pSkinName) < 0) {
389        derror("malformed skin name: %s", skinName);
390        exit(2);
391    }
392    D("found skin '%s' in directory: %s", *pSkinName, *pSkinDir);
393    return 1;
394}
395
396/***************************************************************
397 ***************************************************************
398 *****
399 *****    NORMAL VIRTUAL DEVICE SUPPORT
400 *****
401 *****/
402
403/* compute path to the root SDK directory
404 * assume we are in $SDKROOT/tools/emulator[.exe]
405 */
406static int
407_avdInfo_getSdkRoot( AvdInfo*  i )
408{
409
410    i->sdkRootPath = path_getSdkRoot(&i->sdkRootPathFromEnv);
411    if (i->sdkRootPath == NULL)
412        return -1;
413
414    return 0;
415}
416
417/* parse the root config .ini file. it is located in
418 * ~/.android/avd/<name>.ini or Windows equivalent
419 */
420static int
421_avdInfo_getRootIni( AvdInfo*  i )
422{
423    char*  iniPath = path_getRootIniPath( i->deviceName );
424
425    if (iniPath == NULL) {
426        derror("unknown virtual device name: '%s'", i->deviceName);
427        return -1;
428    }
429
430    D("Android virtual device file at: %s", iniPath);
431
432    i->rootIni = iniFile_newFromFile(iniPath);
433    AFREE(iniPath);
434
435    if (i->rootIni == NULL) {
436        derror("Corrupt virtual device config file!");
437        return -1;
438    }
439    return 0;
440}
441
442/* Returns the AVD's content path, i.e. the directory that contains
443 * the AVD's content files (e.g. data partition, cache, sd card, etc...).
444 *
445 * We extract this by parsing the root config .ini file, looking for
446 * a "path" elements.
447 */
448static int
449_avdInfo_getContentPath( AvdInfo*  i )
450{
451    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
452
453    i->contentPath = iniFile_getString(i->rootIni, ROOT_ABS_PATH_KEY, NULL);
454
455    if (i->contentPath == NULL) {
456        derror("bad config: %s",
457               "virtual device file lacks a "ROOT_ABS_PATH_KEY" entry");
458        return -1;
459    }
460
461    if (!path_is_dir(i->contentPath)) {
462        // If the absolute path doesn't match an actual directory, try
463        // the relative path if present.
464        const char* relPath = iniFile_getString(i->rootIni, ROOT_REL_PATH_KEY, NULL);
465        if (relPath != NULL) {
466            p = bufprint_config_path(temp, end);
467            p = bufprint(p, end, PATH_SEP "%s", relPath);
468            if (p < end && path_is_dir(temp)) {
469                AFREE(i->contentPath);
470                i->contentPath = ASTRDUP(temp);
471            }
472        }
473    }
474
475    D("virtual device content at %s", i->contentPath);
476    return 0;
477}
478
479static int
480_avdInfo_getApiLevel( AvdInfo*  i )
481{
482    char*       target;
483    const char* p;
484    const int   defaultLevel = 1000;
485    int         level        = defaultLevel;
486
487#    define ROOT_TARGET_KEY   "target"
488
489    target = iniFile_getString(i->rootIni, ROOT_TARGET_KEY, NULL);
490    if (target == NULL) {
491        D("No target field in root AVD .ini file?");
492        D("Defaulting to API level %d", level);
493        return level;
494    }
495
496    DD("Found target field in root AVD .ini file: '%s'", target);
497
498    /* There are two acceptable formats for the target key.
499     *
500     * 1/  android-<level>
501     * 2/  <vendor-name>:<add-on-name>:<level>
502     *
503     * Where <level> can be either a _name_ (for experimental/preview SDK builds)
504     * or a decimal number. Note that if a _name_, it can start with a digit.
505     */
506
507    /* First, extract the level */
508    if (!memcmp(target, "android-", 8))
509        p = target + 8;
510    else {
511        /* skip two columns */
512        p = strchr(target, ':');
513        if (p != NULL) {
514            p = strchr(p+1, ':');
515            if (p != NULL)
516                p += 1;
517        }
518    }
519    if (p == NULL || !isdigit(*p)) {
520        goto NOT_A_NUMBER;
521    } else {
522        char* end;
523        long  val = strtol(p, &end, 10);
524        if (end == NULL || *end != '\0' || val != (int)val) {
525            goto NOT_A_NUMBER;
526        }
527        level = (int)val;
528
529        /* Sanity check, we don't support anything prior to Android 1.5 */
530        if (level < 3)
531            level = 3;
532
533        D("Found AVD target API level: %d", level);
534    }
535EXIT:
536    AFREE(target);
537    return level;
538
539NOT_A_NUMBER:
540    if (p == NULL) {
541        D("Invalid target field in root AVD .ini file");
542    } else {
543        D("Target AVD api level is not a number");
544    }
545    D("Defaulting to API level %d", level);
546    goto EXIT;
547}
548
549/* Look for a named file inside the AVD's content directory.
550 * Returns NULL if it doesn't exist, or a strdup() copy otherwise.
551 */
552static char*
553_avdInfo_getContentFilePath(AvdInfo*  i, const char* fileName)
554{
555    char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
556
557    p = bufprint(p, end, "%s/%s", i->contentPath, fileName);
558    if (p >= end) {
559        derror("can't access virtual device content directory");
560        return NULL;
561    }
562    if (!path_exists(temp)) {
563        return NULL;
564    }
565    return ASTRDUP(temp);
566}
567
568/* find and parse the config.ini file from the content directory */
569static int
570_avdInfo_getConfigIni(AvdInfo*  i)
571{
572    char*  iniPath = _avdInfo_getContentFilePath(i, "config.ini");
573
574    /* Allow non-existing config.ini */
575    if (iniPath == NULL) {
576        D("virtual device has no config file - no problem");
577        return 0;
578    }
579
580    D("virtual device config file: %s", iniPath);
581    i->configIni = iniFile_newFromFile(iniPath);
582    AFREE(iniPath);
583
584    if (i->configIni == NULL) {
585        derror("bad config: %s",
586               "virtual device has corrupted config.ini");
587        return -1;
588    }
589    return 0;
590}
591
592/* The AVD's config.ini contains a list of search paths (all beginning
593 * with SEARCH_PREFIX) which are directory locations searched for
594 * AVD platform files.
595 */
596static void
597_avdInfo_getSearchPaths( AvdInfo*  i )
598{
599    if (i->configIni == NULL)
600        return;
601
602    i->numSearchPaths = _getSearchPaths( i->configIni,
603                                         i->sdkRootPath,
604                                         MAX_SEARCH_PATHS,
605                                         i->searchPaths );
606    if (i->numSearchPaths == 0) {
607        derror("no search paths found in this AVD's configuration.\n"
608               "Weird, the AVD's config.ini file is malformed. Try re-creating it.\n");
609        exit(2);
610    }
611    else
612        DD("found a total of %d search paths for this AVD", i->numSearchPaths);
613}
614
615/* Search a file in the SDK search directories. Return NULL if not found,
616 * or a strdup() otherwise.
617 */
618static char*
619_avdInfo_getSdkFilePath(AvdInfo*  i, const char*  fileName)
620{
621    char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
622
623    do {
624        /* try the search paths */
625        int  nn;
626
627        for (nn = 0; nn < i->numSearchPaths; nn++) {
628            const char* searchDir = i->searchPaths[nn];
629
630            p = bufprint(temp, end, "%s/%s", searchDir, fileName);
631            if (p < end && path_exists(temp)) {
632                DD("found %s in search dir: %s", fileName, searchDir);
633                goto FOUND;
634            }
635            DD("    no %s in search dir: %s", fileName, searchDir);
636        }
637
638        return NULL;
639
640    } while (0);
641
642FOUND:
643    return ASTRDUP(temp);
644}
645
646/* Search for a file in the content directory, and if not found, in the
647 * SDK search directory. Returns NULL if not found.
648 */
649static char*
650_avdInfo_getContentOrSdkFilePath(AvdInfo*  i, const char*  fileName)
651{
652    char*  path;
653
654    path = _avdInfo_getContentFilePath(i, fileName);
655    if (path)
656        return path;
657
658    path = _avdInfo_getSdkFilePath(i, fileName);
659    if (path)
660        return path;
661
662    return NULL;
663}
664
665#if 0
666static int
667_avdInfo_findContentOrSdkImage(AvdInfo* i, AvdImageType id)
668{
669    const char* fileName = _imageFileNames[id];
670    char*       path     = _avdInfo_getContentOrSdkFilePath(i, fileName);
671
672    i->imagePath[id]  = path;
673    i->imageState[id] = IMAGE_STATE_READONLY;
674
675    if (path == NULL)
676        return -1;
677    else
678        return 0;
679}
680#endif
681
682/* Returns path to the core hardware .ini file. This contains the
683 * hardware configuration that is read by the core. The content of this
684 * file is auto-generated before launching a core, but we need to know
685 * its path before that.
686 */
687static int
688_avdInfo_getCoreHwIniPath( AvdInfo* i, const char* basePath )
689{
690    i->coreHardwareIniPath = _getFullFilePath(basePath, CORE_HARDWARE_INI);
691    if (i->coreHardwareIniPath == NULL) {
692        DD("Path too long for %s: %s", CORE_HARDWARE_INI, basePath);
693        return -1;
694    }
695    D("using core hw config path: %s", i->coreHardwareIniPath);
696    return 0;
697}
698
699AvdInfo*
700avdInfo_new( const char*  name, AvdInfoParams*  params )
701{
702    AvdInfo*  i;
703
704    if (name == NULL)
705        return NULL;
706
707    if (!_checkAvdName(name)) {
708        derror("virtual device name contains invalid characters");
709        exit(1);
710    }
711
712    ANEW0(i);
713    i->deviceName = ASTRDUP(name);
714
715    if ( _avdInfo_getSdkRoot(i) < 0     ||
716         _avdInfo_getRootIni(i) < 0     ||
717         _avdInfo_getContentPath(i) < 0 ||
718         _avdInfo_getConfigIni(i)   < 0 ||
719         _avdInfo_getCoreHwIniPath(i, i->contentPath) < 0 )
720        goto FAIL;
721
722    i->apiLevel = _avdInfo_getApiLevel(i);
723
724    /* look for image search paths. handle post 1.1/pre cupcake
725     * obsolete SDKs.
726     */
727    _avdInfo_getSearchPaths(i);
728
729    /* don't need this anymore */
730    iniFile_free(i->rootIni);
731    i->rootIni = NULL;
732
733    return i;
734
735FAIL:
736    avdInfo_free(i);
737    return NULL;
738}
739
740/***************************************************************
741 ***************************************************************
742 *****
743 *****    ANDROID BUILD SUPPORT
744 *****
745 *****    The code below corresponds to the case where we're
746 *****    starting the emulator inside the Android build
747 *****    system. The main differences are that:
748 *****
749 *****    - the $ANDROID_PRODUCT_OUT directory is used as the
750 *****      content file.
751 *****
752 *****    - built images must not be modified by the emulator,
753 *****      so system.img must be copied to a temporary file
754 *****      and userdata.img must be copied to userdata-qemu.img
755 *****      if the latter doesn't exist.
756 *****
757 *****    - the kernel and default skin directory are taken from
758 *****      prebuilt
759 *****
760 *****    - there is no root .ini file, or any config.ini in
761 *****      the content directory, no SDK images search path.
762 *****/
763
764/* Read a hardware.ini if it is located in the skin directory */
765static int
766_avdInfo_getBuildSkinHardwareIni( AvdInfo*  i )
767{
768    char* skinName;
769    char* skinDirPath;
770
771    avdInfo_getSkinInfo(i, &skinName, &skinDirPath);
772    if (skinDirPath == NULL)
773        return 0;
774
775    int result = avdInfo_getSkinHardwareIni(i, skinName, skinDirPath);
776
777    AFREE(skinName);
778    AFREE(skinDirPath);
779
780    return result;
781}
782
783int avdInfo_getSkinHardwareIni( AvdInfo* i, char* skinName, char* skinDirPath)
784{
785    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
786
787    p = bufprint(temp, end, "%s/%s/hardware.ini", skinDirPath, skinName);
788    if (p >= end || !path_exists(temp)) {
789        DD("no skin-specific hardware.ini in %s", skinDirPath);
790        return 0;
791    }
792
793    D("found skin-specific hardware.ini: %s", temp);
794    if (i->skinHardwareIni != NULL)
795        iniFile_free(i->skinHardwareIni);
796    i->skinHardwareIni = iniFile_newFromFile(temp);
797    if (i->skinHardwareIni == NULL)
798        return -1;
799
800    return 0;
801}
802
803AvdInfo*
804avdInfo_newForAndroidBuild( const char*     androidBuildRoot,
805                            const char*     androidOut,
806                            AvdInfoParams*  params )
807{
808    AvdInfo*  i;
809
810    ANEW0(i);
811
812    i->inAndroidBuild   = 1;
813    i->androidBuildRoot = ASTRDUP(androidBuildRoot);
814    i->androidOut       = ASTRDUP(androidOut);
815    i->contentPath      = ASTRDUP(androidOut);
816    i->targetArch       = path_getBuildTargetArch(i->androidOut);
817    i->apiLevel         = path_getBuildTargetApiLevel(i->androidOut);
818
819    /* TODO: find a way to provide better information from the build files */
820    i->deviceName = ASTRDUP("<build>");
821
822    /* There is no config.ini in the build */
823    i->configIni = NULL;
824
825    if (_avdInfo_getCoreHwIniPath(i, i->androidOut) < 0 )
826        goto FAIL;
827
828    /* Read the build skin's hardware.ini, if any */
829    _avdInfo_getBuildSkinHardwareIni(i);
830
831    return i;
832
833FAIL:
834    avdInfo_free(i);
835    return NULL;
836}
837
838const char*
839avdInfo_getName( AvdInfo*  i )
840{
841    return i ? i->deviceName : NULL;
842}
843
844const char*
845avdInfo_getImageFile( AvdInfo*  i, AvdImageType  imageType )
846{
847    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
848        return NULL;
849
850    return i->imagePath[imageType];
851}
852
853uint64_t
854avdInfo_getImageFileSize( AvdInfo*  i, AvdImageType  imageType )
855{
856    const char* file = avdInfo_getImageFile(i, imageType);
857    uint64_t    size;
858
859    if (file == NULL)
860        return 0ULL;
861
862    if (path_get_size(file, &size) < 0)
863        return 0ULL;
864
865    return size;
866}
867
868int
869avdInfo_isImageReadOnly( AvdInfo*  i, AvdImageType  imageType )
870{
871    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
872        return 1;
873
874    return (i->imageState[imageType] == IMAGE_STATE_READONLY);
875}
876
877char*
878avdInfo_getKernelPath( AvdInfo*  i )
879{
880    const char* imageName = _imageFileNames[ AVD_IMAGE_KERNEL ];
881
882    char*  kernelPath = _avdInfo_getContentOrSdkFilePath(i, imageName);
883
884    if (kernelPath == NULL && i->inAndroidBuild) {
885        /* When in the Android build, look into the prebuilt directory
886         * for our target architecture.
887         */
888        char temp[PATH_MAX], *p = temp, *end = p + sizeof(temp);
889        const char* suffix = "";
890        char* abi;
891
892        /* If the target ABI is armeabi-v7a, then look for
893         * kernel-qemu-armv7 instead of kernel-qemu in the prebuilt
894         * directory. */
895        abi = path_getBuildTargetAbi(i->androidOut);
896        if (!strcmp(abi,"armeabi-v7a")) {
897            suffix = "-armv7";
898        }
899        AFREE(abi);
900
901        p = bufprint(temp, end, "%s/prebuilts/qemu-kernel/%s/kernel-qemu%s",
902                     i->androidBuildRoot, i->targetArch, suffix);
903        if (p >= end || !path_exists(temp)) {
904            derror("bad workspace: cannot find prebuilt kernel in: %s", temp);
905            exit(1);
906        }
907        kernelPath = ASTRDUP(temp);
908    }
909    return kernelPath;
910}
911
912
913char*
914avdInfo_getRamdiskPath( AvdInfo* i )
915{
916    const char* imageName = _imageFileNames[ AVD_IMAGE_RAMDISK ];
917    return _avdInfo_getContentOrSdkFilePath(i, imageName);
918}
919
920char*  avdInfo_getCachePath( AvdInfo*  i )
921{
922    const char* imageName = _imageFileNames[ AVD_IMAGE_CACHE ];
923    return _avdInfo_getContentFilePath(i, imageName);
924}
925
926char*  avdInfo_getDefaultCachePath( AvdInfo*  i )
927{
928    const char* imageName = _imageFileNames[ AVD_IMAGE_CACHE ];
929    return _getFullFilePath(i->contentPath, imageName);
930}
931
932char*  avdInfo_getSdCardPath( AvdInfo* i )
933{
934    const char* imageName = _imageFileNames[ AVD_IMAGE_SDCARD ];
935    char*       path;
936
937    /* Special case, the config.ini can have a SDCARD_PATH entry
938     * that gives the full path to the SD Card.
939     */
940    if (i->configIni != NULL) {
941        path = iniFile_getString(i->configIni, SDCARD_PATH, NULL);
942        if (path != NULL) {
943            if (path_exists(path))
944                return path;
945
946            dwarning("Ignoring invalid SDCard path: %s", path);
947            AFREE(path);
948        }
949    }
950
951    /* Otherwise, simply look into the content directory */
952    return _avdInfo_getContentFilePath(i, imageName);
953}
954
955char*
956avdInfo_getSnapStoragePath( AvdInfo* i )
957{
958    const char* imageName = _imageFileNames[ AVD_IMAGE_SNAPSHOTS ];
959    return _avdInfo_getContentFilePath(i, imageName);
960}
961
962char*
963avdInfo_getSystemImagePath( AvdInfo*  i )
964{
965    const char* imageName = _imageFileNames[ AVD_IMAGE_USERSYSTEM ];
966    return _avdInfo_getContentFilePath(i, imageName);
967}
968
969char*
970avdInfo_getSystemInitImagePath( AvdInfo*  i )
971{
972    const char* imageName = _imageFileNames[ AVD_IMAGE_INITSYSTEM ];
973    return _avdInfo_getContentOrSdkFilePath(i, imageName);
974}
975
976char*
977avdInfo_getDataImagePath( AvdInfo*  i )
978{
979    const char* imageName = _imageFileNames[ AVD_IMAGE_USERDATA ];
980    return _avdInfo_getContentFilePath(i, imageName);
981}
982
983char*
984avdInfo_getDefaultDataImagePath( AvdInfo*  i )
985{
986    const char* imageName = _imageFileNames[ AVD_IMAGE_USERDATA ];
987    return _getFullFilePath(i->contentPath, imageName);
988}
989
990char*
991avdInfo_getDataInitImagePath( AvdInfo* i )
992{
993    const char* imageName = _imageFileNames[ AVD_IMAGE_INITDATA ];
994    return _avdInfo_getContentOrSdkFilePath(i, imageName);
995}
996
997int
998avdInfo_initHwConfig( AvdInfo*  i, AndroidHwConfig*  hw )
999{
1000    int  ret = 0;
1001
1002    androidHwConfig_init(hw, i->apiLevel);
1003
1004    /* First read the config.ini, if any */
1005    if (i->configIni != NULL) {
1006        ret = androidHwConfig_read(hw, i->configIni);
1007    }
1008
1009    /* The skin's hardware.ini can override values */
1010    if (ret == 0 && i->skinHardwareIni != NULL) {
1011        ret = androidHwConfig_read(hw, i->skinHardwareIni);
1012    }
1013
1014    /* Auto-disable keyboard emulation on sapphire platform builds */
1015    if (i->androidOut != NULL) {
1016        char*  p = strrchr(i->androidOut, '/');
1017        if (p != NULL && !strcmp(p,"sapphire")) {
1018            hw->hw_keyboard = 0;
1019        }
1020    }
1021
1022    return ret;
1023}
1024
1025const char*
1026avdInfo_getContentPath( AvdInfo*  i )
1027{
1028    return i->contentPath;
1029}
1030
1031int
1032avdInfo_inAndroidBuild( AvdInfo*  i )
1033{
1034    return i->inAndroidBuild;
1035}
1036
1037char*
1038avdInfo_getTargetAbi( AvdInfo* i )
1039{
1040    /* For now, we can't get the ABI from SDK AVDs */
1041    if (!i->inAndroidBuild)
1042        return NULL;
1043
1044    return path_getBuildTargetAbi(i->androidOut);
1045}
1046
1047char*
1048avdInfo_getTracePath( AvdInfo*  i, const char*  traceName )
1049{
1050    char   tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp);
1051
1052    if (i == NULL || traceName == NULL || traceName[0] == 0)
1053        return NULL;
1054
1055    if (i->inAndroidBuild) {
1056        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
1057                      i->androidOut, traceName );
1058    } else {
1059        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
1060                      i->contentPath, traceName );
1061    }
1062    return ASTRDUP(tmp);
1063}
1064
1065const char*
1066avdInfo_getCoreHwIniPath( AvdInfo* i )
1067{
1068    return i->coreHardwareIniPath;
1069}
1070
1071
1072void
1073avdInfo_getSkinInfo( AvdInfo*  i, char** pSkinName, char** pSkinDir )
1074{
1075    char*  skinName = NULL;
1076    char*  skinPath;
1077    char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
1078
1079    *pSkinName = NULL;
1080    *pSkinDir  = NULL;
1081
1082    /* First, see if the config.ini contains a SKIN_PATH entry that
1083     * names the full directory path for the skin.
1084     */
1085    if (i->configIni != NULL ) {
1086        skinPath = iniFile_getString( i->configIni, SKIN_PATH, NULL );
1087        if (skinPath != NULL) {
1088            /* If this skin name is magic or a direct directory path
1089            * we have our result right here.
1090            */
1091            if (_getSkinPathFromName(skinPath, i->sdkRootPath,
1092                                     pSkinName, pSkinDir )) {
1093                AFREE(skinPath);
1094                return;
1095            }
1096        }
1097
1098        /* The SKIN_PATH entry was not valid, so look at SKIN_NAME */
1099        D("Warning: config.ini contains invalid %s entry: %s", SKIN_PATH, skinPath);
1100        AFREE(skinPath);
1101
1102        skinName = iniFile_getString( i->configIni, SKIN_NAME, NULL );
1103    }
1104
1105    if (skinName == NULL) {
1106        /* If there is no skin listed in the config.ini, try to see if
1107         * there is one single 'skin' directory in the content directory.
1108         */
1109        p = bufprint(temp, end, "%s/skin", i->contentPath);
1110        if (p < end && _checkSkinPath(temp)) {
1111            D("using skin content from %s", temp);
1112            AFREE(i->skinName);
1113            *pSkinName = ASTRDUP("skin");
1114            *pSkinDir  = ASTRDUP(i->contentPath);
1115            return;
1116        }
1117
1118        /* otherwise, use the default name */
1119        skinName = ASTRDUP(SKIN_DEFAULT);
1120    }
1121
1122    /* now try to find the skin directory for that name -
1123     */
1124    do {
1125        /* first try the content directory, i.e. $CONTENT/skins/<name> */
1126        skinPath = _checkSkinSkinsDir(i->contentPath, skinName);
1127        if (skinPath != NULL)
1128            break;
1129
1130#define  PREBUILT_SKINS_ROOT "development/tools/emulator"
1131
1132        /* if we are in the Android build, try the prebuilt directory */
1133        if (i->inAndroidBuild) {
1134            p = bufprint( temp, end, "%s/%s",
1135                        i->androidBuildRoot, PREBUILT_SKINS_ROOT );
1136            if (p < end) {
1137                skinPath = _checkSkinSkinsDir(temp, skinName);
1138                if (skinPath != NULL)
1139                    break;
1140            }
1141
1142            /* or in the parent directory of the system dir */
1143            {
1144                char* parentDir = path_parent(i->androidOut, 1);
1145                if (parentDir != NULL) {
1146                    skinPath = _checkSkinSkinsDir(parentDir, skinName);
1147                    AFREE(parentDir);
1148                    if (skinPath != NULL)
1149                        break;
1150                }
1151            }
1152        }
1153
1154        /* look in the search paths. For each <dir> in the list,
1155         * look into <dir>/../skins/<name>/ */
1156        {
1157            int  nn;
1158            for (nn = 0; nn < i->numSearchPaths; nn++) {
1159                char*  parentDir = path_parent(i->searchPaths[nn], 1);
1160                if (parentDir == NULL)
1161                    continue;
1162                skinPath = _checkSkinSkinsDir(parentDir, skinName);
1163                AFREE(parentDir);
1164                if (skinPath != NULL)
1165                  break;
1166            }
1167            if (nn < i->numSearchPaths)
1168                break;
1169        }
1170
1171        /* We didn't find anything ! */
1172        *pSkinName = skinName;
1173        return;
1174
1175    } while (0);
1176
1177    if (path_split(skinPath, pSkinDir, pSkinName) < 0) {
1178        derror("weird skin path: %s", skinPath);
1179        AFREE(skinPath);
1180        return;
1181    }
1182    DD("found skin '%s' in directory: %s", *pSkinName, *pSkinDir);
1183    AFREE(skinPath);
1184    return;
1185}
1186
1187int
1188avdInfo_shouldUseDynamicSkin( AvdInfo* i)
1189{
1190    if (i == NULL || i->configIni == NULL)
1191        return 0;
1192    return iniFile_getBoolean( i->configIni, SKIN_DYNAMIC, "no" );
1193}
1194
1195char*
1196avdInfo_getCharmapFile( AvdInfo* i, const char* charmapName )
1197{
1198    char        fileNameBuff[PATH_MAX];
1199    const char* fileName;
1200
1201    if (charmapName == NULL || charmapName[0] == '\0')
1202        return NULL;
1203
1204    if (strstr(charmapName, ".kcm") == NULL) {
1205        snprintf(fileNameBuff, sizeof fileNameBuff, "%s.kcm", charmapName);
1206        fileName = fileNameBuff;
1207    } else {
1208        fileName = charmapName;
1209    }
1210
1211    return _avdInfo_getContentOrSdkFilePath(i, fileName);
1212}
1213
1214int avdInfo_getAdbdCommunicationMode( AvdInfo* i )
1215{
1216    return path_getAdbdCommunicationMode(i->androidOut);
1217}
1218
1219int avdInfo_getSnapshotPresent(AvdInfo* i)
1220{
1221    if (i->configIni == NULL) {
1222        return 0;
1223    } else {
1224        return iniFile_getBoolean(i->configIni, "snapshot.present", "no");
1225    }
1226}
1227