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