/* Copyright (C) 2008 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ #include "android/avd/info.h" #include "android/utils/path.h" #include "android/utils/bufprint.h" #include "android/utils/filelock.h" #include "android/utils/tempfile.h" #include "android/utils/debug.h" #include "android/utils/dirscanner.h" #include #include #include #include #include #include /* define this to 1 to support obsolete platform/add-on * specific parsing and path searching as was used temporarily * in post-1.1 / pre-cupcake SDKs. * * the corresponding code should be removed soon. */ #define SUPPORT_PLATFORM_OR_ADDON 1 /* global variables - see android/globals.h */ AvdInfoParams android_avdParams[1]; AvdInfo* android_avdInfo; /* for debugging */ #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) #define DD(...) VERBOSE_PRINT(avd_config,__VA_ARGS__) /* technical note on how all of this is supposed to work: * * Each AVD corresponds to a "content directory" that is used to * store persistent disk images and configuration files. Most remarkable * are: * * - a "config.ini" file used to hold configuration information for the * AVD * * - mandatory user data image ("userdata-qemu.img") and cache image * ("cache.img") * * - optional mutable system image ("system-qemu.img"), kernel image * ("kernel-qemu") and read-only ramdisk ("ramdisk.img") * * When starting up an AVD, the emulator looks for relevant disk images * in the content directory. If it doesn't find a given image there, it * will try to search in the list of system directories listed in the * 'config.ini' file through one of the following (key,value) pairs: * * images.sysdir.1 = * images.sysdir.2 = * * The search paths can be absolute, or relative to the root SDK installation * path (which is determined from the emulator program's location, or from the * ANDROID_SDK_ROOT environment variable). * * Individual image disk search patch can be over-riden on the command-line * with one of the usual options. */ #if SUPPORT_PLATFORM_OR_ADDON /* * BELOW IS THE DOCUMENTATION FOR AN OBSOLETE SCHEME THAT USED TO BE MORE * COMPLEX TO IMPLEMENT, AND EXPOSED TOO MUCH SDK INTERNALS WITHIN THE * EMULATOR SOURCE CODE. * * THE CORRESPONDING CODE IS STILL THERE FOR LEGACY SUPPORT REASON BUT WILL * SOON BE REMOVED FOR SIMPLIFICATION REASONS. * * we assume the following SDK layout: * * SDK/ * tools/ * emulator[.exe] * libs/ * hardware-properties.ini * ... * * platforms/ * / * build.prop * images/ * * skins/ * default/ --> default skin * layout * * / --> another skin * layout * * / --> skin alias to * alias- * * / * build.prop * images/ * * * add-ons/ * / * manifest.ini * images/ * * * / * manifest.ini * * hardware.ini * skins/ * default/ * layout * * / * layout * * * * we define a 'platform' as a directory that provides a complete * set of disk/kernel images, some skins, as well as a build.prop * file. * * we define an 'addon' as a directory that provides additionnal * or replacement files related to a given existing platform. * each add-on provides at the minimum a 'manifest.ini' file * that describes it (see below). * * important notes: * * - the build.prop file of a given platform directory contains * a line that reads 'ro.build.version.sdk=' where * is an integer corresponding to the corresponding * official API version number as defined by Android. * * each platform provided with the SDK must have a unique * version number. * * - the manifest.ini of a given addon must contain lines * that include: * * name= * vendor= * api= * * where is used to identify the platform the add-on * refers to. Note that the platform's directory name is * irrelevant to the matching algorithm. * * each addon available must have a unique * :: triplet * * - an add-on can provide a hardware.ini file. If present, this * is used to force the hardware setting of any virtual device * built from the add-on. * * - the file in SDK/tools/lib/hardware-properties.ini declares which * hardware properties are supported by the emulator binary. * these can appear in the config.ini file of a given virtual * device, or the hardware.ini of a given add-on. * * normally, a virtual device corresponds to: * * - a root configuration file, placed in ~/.android/avd/.ini * where is the name of the virtual device. * * - a "content" directory, which contains disk images for the * virtual device (e.g. at a minimum, the userdata.img file) * plus some configuration information. * * - the root config file must have at least two lines like: * * path= * target= * * the 'path' value must point to the location of * the virtual device's content directory. By default, this * should be ~/.android/avd//, though the user should be * able to choose an alternative path at creation time. * * the 'target' value can be one of: * * android- * :: * * the first form is used to refer to a given platform. * the second form is used to refer to a unique add-on. * in both forms, must be an integer that * matches one of the available platforms. * * :: must match the triplet of one * of the available add-ons * * if the target value is incorrect, or if the content path * is invalid, the emulator will abort with an error. * * - the content directory shall contain a 'config.ini' that * contains hardware properties for the virtual device * (as defined by SDK/tools/lib/hardware-properties.ini), as * well as additional lines like: * * sdcard= * skin= * options= * * * Finally, to find the skin to be used with a given virtual * device, the following logic is used: * * - if no skin name has been manually specified on * the command line, or in the config.ini file, * look in $CONTENT/skin/layout and use it if available. * * - otherwise, set SKINNAME to 'default' if not manually * specified, and look for $ADDON/skins/$SKINNAME/layout * and use it if available * * - otherwise, look for $PLATFORM/skins/$SKINNAME/layout * and use it if available. * * - otherwise, look for $PLATFORM/skins/$SKINNAME/alias-. * if a file exist by that name, look at $PLATFORM/skins//layout * and use it if available. Aliases are not recursives :-) */ /* now, things get a little bit more complicated when working * within the Android build system. In this mode, which can be * detected by looking at the definition of the ANDROID_PRODUCT_OUT * environment variable, we're going to simply pick the image files * from the out directory, or from $BUILDROOT/prebuilt */ /* the name of the $SDKROOT subdirectory that contains all platforms */ # define PLATFORMS_SUBDIR "platforms" /* the name of the $SDKROOT subdirectory that contains add-ons */ # define ADDONS_SUBDIR "add-ons" #endif /* SUPPORT_PLATFORM_OR_ADDON */ /* this is the subdirectory of $HOME/.android where all * root configuration files (and default content directories) * are located. */ #define ANDROID_AVD_DIR "avd" /* the prefix of config.ini keys that will be used for search directories * of system images. */ #define SEARCH_PREFIX "images.sysdir." /* the maximum number of search path keys we're going to read from the * config.ini file */ #define MAX_SEARCH_PATHS 2 /* the config.ini key that will be used to indicate the full relative * path to the skin directory (including the skin name). * * If SUPPORT_PLATFORM_OR_ADDON is defined, then this can also be * the name of a skin, without any path, and platform/add-on directories * will be searched for it. */ #define SKIN_PATH "skin" /* default skin name */ #define SKIN_DEFAULT "HVGA" /* certain disk image files are mounted read/write by the emulator * to ensure that several emulators referencing the same files * do not corrupt these files, we need to lock them and respond * to collision depending on the image type. * * the enumeration below is used to record information about * each image file path. * * READONLY means that the file will be mounted read-only * and this doesn't need to be locked. must be first in list * * MUSTLOCK means that the file should be locked before * being mounted by the emulator * * TEMPORARY means that the file has been copied to a * temporary image, which can be mounted read/write * but doesn't require locking. */ typedef enum { IMAGE_STATE_READONLY, /* unlocked */ IMAGE_STATE_MUSTLOCK, /* must be locked */ IMAGE_STATE_LOCKED, /* locked */ IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */ IMAGE_STATE_TEMPORARY, /* copied to temp file (no lock needed) */ } AvdImageState; struct AvdInfo { /* for the Android build system case */ char inAndroidBuild; char* androidOut; char* androidBuildRoot; /* for the normal virtual device case */ char* deviceName; char* sdkRootPath; char sdkRootPathFromEnv; char* searchPaths[ MAX_SEARCH_PATHS ]; int numSearchPaths; #if SUPPORT_PLATFORM_OR_ADDON int platformVersion; char* platformPath; char* addonTarget; char* addonPath; #endif char* contentPath; IniFile* rootIni; /* root .ini file */ IniFile* configIni; /* virtual device's config.ini */ /* for both */ char* skinName; /* skin name */ char* skinDirPath; /* skin directory */ /* image files */ char* imagePath [ AVD_IMAGE_MAX ]; char imageState[ AVD_IMAGE_MAX ]; }; void avdInfo_free( AvdInfo* i ) { if (i) { int nn; for (nn = 0; nn < AVD_IMAGE_MAX; nn++) AFREE(i->imagePath[nn]); AFREE(i->skinName); AFREE(i->skinDirPath); for (nn = 0; nn < i->numSearchPaths; nn++) AFREE(i->searchPaths[nn]); i->numSearchPaths = 0; if (i->configIni) { iniFile_free(i->configIni); i->configIni = NULL; } if (i->rootIni) { iniFile_free(i->rootIni); i->rootIni = NULL; } AFREE(i->contentPath); AFREE(i->sdkRootPath); if (i->inAndroidBuild) { AFREE(i->androidOut); AFREE(i->androidBuildRoot); } else { #if SUPPORT_PLATFORM_OR_ADDON AFREE(i->platformPath); AFREE(i->addonTarget); AFREE(i->addonPath); #endif } AFREE(i->deviceName); AFREE(i); } } /* list of default file names for each supported image file type */ static const char* const _imageFileNames[ AVD_IMAGE_MAX ] = { #define _AVD_IMG(x,y,z) y, AVD_IMAGE_LIST #undef _AVD_IMG }; /* list of short text description for each supported image file type */ static const char* const _imageFileText[ AVD_IMAGE_MAX ] = { #define _AVD_IMG(x,y,z) z, AVD_IMAGE_LIST #undef _AVD_IMG }; /*************************************************************** *************************************************************** ***** ***** NORMAL VIRTUAL DEVICE SUPPORT ***** *****/ /* compute path to the root SDK directory * assume we are in $SDKROOT/tools/emulator[.exe] */ static int _getSdkRoot( AvdInfo* i ) { const char* env; char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); #define SDK_ROOT_ENV "ANDROID_SDK_ROOT" env = getenv(SDK_ROOT_ENV); if (env != NULL && env[0] != 0) { if (path_exists(env)) { D("found " SDK_ROOT_ENV ": %s", env); i->sdkRootPath = ASTRDUP(env); i->sdkRootPathFromEnv = 1; return 0; } D(SDK_ROOT_ENV " points to unknown directory: %s", env); } (void) bufprint_app_dir(temp, end); i->sdkRootPath = path_parent(temp, 1); if (i->sdkRootPath == NULL) { derror("can't find root of SDK directory"); return -1; } D("found SDK root at %s", i->sdkRootPath); return 0; } static void _getSearchPaths( AvdInfo* i ) { char temp[PATH_MAX], *p = temp, *end= p+sizeof temp; int nn, count = 0; for (nn = 0; nn < MAX_SEARCH_PATHS; nn++) { char* path; p = bufprint(temp, end, "%s%d", SEARCH_PREFIX, nn+1 ); if (p >= end) continue; path = iniFile_getString( i->configIni, temp ); if (path != NULL) { DD(" found image search path: %s", path); if (!path_is_absolute(path)) { p = bufprint(temp, end, "%s/%s", i->sdkRootPath, path); AFREE(path); path = ASTRDUP(temp); } i->searchPaths[count++] = path; } } i->numSearchPaths = count; if (count == 0) DD("no search paths found in this AVD's config.ini"); else DD("found a total of %d search paths for this AVD", count); } #if SUPPORT_PLATFORM_OR_ADDON /* returns the full path of the platform subdirectory * corresponding to a given API version */ static char* _findPlatformByVersion( const char* sdkRoot, int version ) { char temp[PATH_MAX], *p=temp, *end=p+sizeof temp; char* subdir = NULL; DirScanner* scanner; DD("> %s(%s,%d)", __FUNCTION__, sdkRoot, version); p = bufprint(temp, end, "%s/%s", sdkRoot, PLATFORMS_SUBDIR); if (p >= end) { DD("! path too long"); return NULL; } scanner = dirScanner_new(temp); if (scanner == NULL) { DD("! cannot scan path %s: %s", temp, strerror(errno)); return NULL; } for (;;) { IniFile* ini; int apiVersion; subdir = (char*) dirScanner_nextFull(scanner); if (subdir == NULL) break; /* look for a file named "build.prop */ p = bufprint(temp, end, "%s/build.prop", subdir); if (p >= end) continue; if (!path_exists(temp)) { DD("! no file at %s", temp); continue; } ini = iniFile_newFromFile(temp); if (ini == NULL) continue; apiVersion = iniFile_getInteger(ini, "ro.build.version.sdk", -1); iniFile_free(ini); DD("! found %s (version %d)", temp, apiVersion); if (apiVersion == version) { /* Bingo */ subdir = ASTRDUP(subdir); break; } } if (!subdir) { DD("< didn't found anything"); } dirScanner_free(scanner); return subdir; } /* returns the full path of the addon corresponding to a given target, * or NULL if not found. on success, *pversion will contain the SDK * version number */ static char* _findAddonByTarget( const char* sdkRoot, const char* target, int *pversion ) { char* targetCopy = ASTRDUP(target); char* targetVendor = NULL; char* targetName = NULL; int targetVersion = -1; char temp[PATH_MAX]; char* p; char* end; DirScanner* scanner; char* subdir; DD("> %s(%s,%s)", __FUNCTION__, sdkRoot, target); /* extract triplet from target string */ targetVendor = targetCopy; p = strchr(targetVendor, ':'); if (p == NULL) { DD("< missing first column separator"); goto FAIL; } *p = 0; targetName = p + 1; p = strchr(targetName, ':'); if (p == NULL) { DD("< missing second column separator"); goto FAIL; } *p++ = 0; targetVersion = atoi(p); if (targetVersion == 0) { DD("< invalid version number"); goto FAIL; } /* now scan addons directory */ p = temp; end = p + sizeof temp; p = bufprint(p, end, "%s/%s", sdkRoot, ADDONS_SUBDIR); if (p >= end) { DD("< add-on path too long"); goto FAIL; } scanner = dirScanner_new(temp); if (scanner == NULL) { DD("< cannot scan add-on path %s: %s", temp, strerror(errno)); goto FAIL; } for (;;) { IniFile* ini; const char* vendor; const char* name; int version; int matches; subdir = (char*) dirScanner_nextFull(scanner); if (subdir == NULL) break; /* try to open the manifest.ini file */ p = bufprint(temp, end, "%s/manifest.ini", subdir); if (p >= end) continue; ini = iniFile_newFromFile(temp); if (ini == NULL) continue; DD("! scanning manifest.ini in %s", temp); /* find the vendor, name and version */ vendor = iniFile_getValue(ini, "vendor"); name = iniFile_getValue(ini, "name"); version = iniFile_getInteger(ini, "api", -1); matches = 0; matches += (version == targetVersion); matches += (vendor && !strcmp(vendor, targetVendor)); matches += (name && !strcmp(name, targetName)); DD("! matches=%d vendor=[%s] name=[%s] version=%d", matches, vendor ? vendor : "", name ? name : "", version); iniFile_free(ini); if (matches == 3) { /* bingo */ *pversion = version; subdir = ASTRDUP(subdir); break; } } dirScanner_free(scanner); DD("< returning %s", subdir ? subdir : ""); return subdir; FAIL: AFREE(targetCopy); return NULL; } #endif /* SUPPORT_PLATFORM_OR_ADDON */ static int _checkAvdName( const char* name ) { int len = strlen(name); int len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789_.-"); return (len == len2); } /* parse the root config .ini file. it is located in * ~/.android/avd/.ini or Windows equivalent */ static int _getRootIni( AvdInfo* i ) { char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); p = bufprint_config_path(temp, end); p = bufprint(p, end, "/" ANDROID_AVD_DIR "/%s.ini", i->deviceName); if (p >= end) { derror("device name too long"); return -1; } i->rootIni = iniFile_newFromFile(temp); if (i->rootIni == NULL) { derror("unknown virtual device name: '%s'", i->deviceName); return -1; } D("root virtual device file at %s", temp); return 0; } /* the .ini variable name that points to the content directory * in a root AVD ini file. This is required */ # define ROOT_PATH_KEY "path" static int _getContentPath( AvdInfo* i ) { i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY); if (i->contentPath == NULL) { derror("bad config: %s", "virtual device file lacks a "ROOT_PATH_KEY" entry"); return -1; } D("virtual device content at %s", i->contentPath); return 0; } #if SUPPORT_PLATFORM_OR_ADDON # define ROOT_TARGET_KEY "target" /* retrieve the content path and target from the root .ini file */ static int _getTarget( AvdInfo* i ) { i->addonTarget = iniFile_getString(i->rootIni, ROOT_TARGET_KEY); if (i->addonTarget == NULL) { derror("bad config: %s", "virtual device file lacks a "ROOT_TARGET_KEY" entry"); return -1; } D("virtual device target is %s", i->addonTarget); if (!strncmp(i->addonTarget, "android-", 8)) { /* target is platform */ char* end; const char* versionString = i->addonTarget+8; int version = (int) strtol(versionString, &end, 10); if (*end != 0 || version <= 0) { derror("bad config: invalid platform version: '%s'", versionString); return -1; } i->platformVersion = version; i->platformPath = _findPlatformByVersion(i->sdkRootPath, version); if (i->platformPath == NULL) { derror("bad config: unknown platform version: '%d'", version); return -1; } } else /* target is add-on */ { i->addonPath = _findAddonByTarget(i->sdkRootPath, i->addonTarget, &i->platformVersion); if (i->addonPath == NULL) { derror("bad config: %s", "unknown add-on target: '%s'", i->addonTarget); return -1; } i->platformPath = _findPlatformByVersion(i->sdkRootPath, i->platformVersion); if (i->platformPath == NULL) { derror("bad config: %s", "unknown add-on platform version: '%d'", i->platformVersion); return -1; } D("virtual device add-on path: %s", i->addonPath); } D("virtual device platform path: %s", i->platformPath); D("virtual device platform version %d", i->platformVersion); return 0; } #endif /* SUPPORT_PLATFORM_OR_ADDON */ /* find and parse the config.ini file from the content directory */ static int _getConfigIni(AvdInfo* i) { char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); p = bufprint(p, end, "%s/config.ini", i->contentPath); if (p >= end) { derror("can't access virtual device content directory"); return -1; } #if 1 /* XXX: TODO: remove this in the future */ /* for now, allow a non-existing config.ini */ if (!path_exists(temp)) { D("virtual device has no config file - no problem"); return 0; } #endif i->configIni = iniFile_newFromFile(temp); if (i->configIni == NULL) { derror("bad config: %s", "virtual device directory lacks config.ini"); return -1; } D("virtual device config file: %s", temp); return 0; } /*************************************************************** *************************************************************** ***** ***** KERNEL/DISK IMAGE LOADER ***** *****/ /* a structure used to handle the loading of * kernel/disk images. */ typedef struct { AvdInfo* info; AvdInfoParams* params; AvdImageType id; const char* imageFile; const char* imageText; char** pPath; char* pState; char temp[PATH_MAX]; } ImageLoader; static void imageLoader_init( ImageLoader* l, AvdInfo* info, AvdInfoParams* params ) { memset(l, 0, sizeof(*l)); l->info = info; l->params = params; } /* set the type of the image to load */ static void imageLoader_set( ImageLoader* l, AvdImageType id ) { l->id = id; l->imageFile = _imageFileNames[id]; l->imageText = _imageFileText[id]; l->pPath = &l->info->imagePath[id]; l->pState = &l->info->imageState[id]; l->pState[0] = IMAGE_STATE_READONLY; } /* change the image path */ static char* imageLoader_setPath( ImageLoader* l, const char* path ) { path = path ? ASTRDUP(path) : NULL; AFREE(l->pPath[0]); l->pPath[0] = (char*) path; return (char*) path; } static char* imageLoader_extractPath( ImageLoader* l ) { char* result = l->pPath[0]; l->pPath[0] = NULL; return result; } /* flags used when loading images */ enum { IMAGE_REQUIRED = (1<<0), /* image is required */ IMAGE_SEARCH_SDK = (1<<1), /* search image in SDK */ IMAGE_EMPTY_IF_MISSING = (1<<2), /* create empty file if missing */ IMAGE_DONT_LOCK = (1<<4), /* don't try to lock image */ IMAGE_IGNORE_IF_LOCKED = (1<<5), /* ignore file if it's locked */ }; #define IMAGE_OPTIONAL 0 /* find an image from the SDK search directories. * returns the full path or NULL if the file could not be found. * * note: this stores the result in the image's path as well */ static char* imageLoader_lookupSdk( ImageLoader* l ) { AvdInfo* i = l->info; const char* image = l->imageFile; char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); do { /* try the search paths */ int nn; for (nn = 0; nn < i->numSearchPaths; nn++) { const char* searchDir = i->searchPaths[nn]; p = bufprint(temp, end, "%s/%s", searchDir, image); if (p < end && path_exists(temp)) { DD("found %s in search dir: %s", image, searchDir); goto FOUND; } DD(" no %s in search dir: %s", image, searchDir); } #if SUPPORT_PLATFORM_OR_ADDON /* try the add-on directory, if any */ if (i->addonPath != NULL) { p = bufprint(temp, end, "%s/images/%s", i->addonPath, image); if (p < end && path_exists(temp)) { DD("found %s in add-on dir:", image, i->addonPath); break; } DD(" no %s in add-on dir: ", image, i->addonPath); } /* or try the platform directory */ p = bufprint(temp, end, "%s/images/%s", i->platformPath, image); if (p < end && path_exists(temp)) { DD("found %s in platform dir:", image, i->platformPath); break; } DD(" no %s in platform dir: ", image, i->platformPath); #endif return NULL; } while (0); FOUND: l->pState[0] = IMAGE_STATE_READONLY; return imageLoader_setPath(l, temp); } /* search for a file in the content directory. * returns NULL if the file cannot be found. * * note that this formats l->temp with the file's path * allowing you to retrieve it if the function returns NULL */ static char* imageLoader_lookupContent( ImageLoader* l ) { AvdInfo* i = l->info; char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); p = bufprint(temp, end, "%s/%s", i->contentPath, l->imageFile); if (p >= end) { derror("content directory path too long"); exit(2); } if (!path_exists(temp)) { DD(" no %s in content directory", l->imageFile); return NULL; } DD("found %s in content directory", l->imageFile); /* assume content image files must be locked */ l->pState[0] = IMAGE_STATE_MUSTLOCK; return imageLoader_setPath(l, temp); } /* lock a file image depending on its state and user flags * note that this clears l->pPath[0] if the lock could not * be acquired and that IMAGE_IGNORE_IF_LOCKED is used. */ static void imageLoader_lock( ImageLoader* l, unsigned flags ) { const char* path = l->pPath[0]; if (flags & IMAGE_DONT_LOCK) return; if (l->pState[0] != IMAGE_STATE_MUSTLOCK) return; D(" locking %s image at %s", l->imageText, path); if (filelock_create(path) != NULL) { /* succesful lock */ l->pState[0] = IMAGE_STATE_LOCKED; return; } if (flags & IMAGE_IGNORE_IF_LOCKED) { dwarning("ignoring locked %s image at %s", l->imageText, path); imageLoader_setPath(l, NULL); return; } derror("the %s image is used by another emulator. aborting", l->imageText); exit(2); } /* make a file image empty, this may require locking */ static void imageLoader_empty( ImageLoader* l, unsigned flags ) { const char* path; imageLoader_lock(l, flags); path = l->pPath[0]; if (path == NULL) /* failed to lock, caller will handle it */ return; if (path_empty_file(path) < 0) { derror("could not create %s image at %s: %s", l->imageText, path, strerror(errno)); exit(2); } l->pState[0] = IMAGE_STATE_LOCKED_EMPTY; } /* copy image file from a given source * assumes locking is needed. */ static void imageLoader_copyFrom( ImageLoader* l, const char* srcPath ) { const char* dstPath = NULL; /* find destination file */ if (l->params) { dstPath = l->params->forcePaths[l->id]; } if (!dstPath) { imageLoader_lookupContent(l); dstPath = l->temp; } /* lock destination */ imageLoader_setPath(l, dstPath); l->pState[0] = IMAGE_STATE_MUSTLOCK; imageLoader_lock(l, 0); /* make the copy */ if (path_copy_file(dstPath, srcPath) < 0) { derror("can't initialize %s image from SDK: %s: %s", l->imageText, dstPath, strerror(errno)); exit(2); } } /* this will load and eventually lock and image file, depending * on the flags being used. on exit, this function udpates * l->pState[0] and l->pPath[0] * * returns the path to the file. Note that it returns NULL * only if the file was optional and could not be found. * * if the file is required and missing, the function aborts * the program. */ static char* imageLoader_load( ImageLoader* l, unsigned flags ) { const char* path = NULL; /* first, check user-provided path */ path = l->params->forcePaths[l->id]; if (path != NULL) { imageLoader_setPath(l, path); if (path_exists(path)) { DD("found user-provided %s image: %s", l->imageText, l->imageFile); goto EXIT; } D("user-provided %s image does not exist: %s", l->imageText, path); /* if the file is required, abort */ if (flags & IMAGE_REQUIRED) { derror("user-provided %s image at %s doesn't exist", l->imageText, path); exit(2); } } else { const char* contentFile; /* second, look in the content directory */ path = imageLoader_lookupContent(l); if (path) goto EXIT; contentFile = ASTRDUP(l->temp); /* it's not there */ if (flags & IMAGE_SEARCH_SDK) { /* third, look in the SDK directory */ path = imageLoader_lookupSdk(l); if (path) { AFREE((char*)contentFile); goto EXIT; } } DD("found no %s image (%s)", l->imageText, l->imageFile); /* if the file is required, abort */ if (flags & IMAGE_REQUIRED) { AvdInfo* i = l->info; derror("could not find required %s image (%s).", l->imageText, l->imageFile); if (i->inAndroidBuild) { dprint( "Did you build everything ?" ); } else if (!i->sdkRootPathFromEnv) { dprint( "Maybe defining %s to point to a valid SDK " "installation path might help ?", SDK_ROOT_ENV ); } else { dprint( "Your %s is probably wrong: %s", SDK_ROOT_ENV, i->sdkRootPath ); } exit(2); } path = imageLoader_setPath(l, contentFile); AFREE((char*)contentFile); } /* otherwise, do we need to create it ? */ if (flags & IMAGE_EMPTY_IF_MISSING) { imageLoader_empty(l, flags); return l->pPath[0]; } return NULL; EXIT: imageLoader_lock(l, flags); return l->pPath[0]; } /* find the correct path of all image files we're going to need * and lock the files that need it. */ static int _getImagePaths(AvdInfo* i, AvdInfoParams* params ) { int wipeData = (params->flags & AVDINFO_WIPE_DATA) != 0; int wipeCache = (params->flags & AVDINFO_WIPE_CACHE) != 0; int noCache = (params->flags & AVDINFO_NO_CACHE) != 0; int noSdCard = (params->flags & AVDINFO_NO_SDCARD) != 0; ImageLoader l[1]; imageLoader_init(l, i, params); /* pick up the kernel and ramdisk image files - these don't * need a specific handling. */ imageLoader_set ( l, AVD_IMAGE_KERNEL ); imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); imageLoader_set ( l, AVD_IMAGE_RAMDISK ); imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK ); /* the system image * * if there is one in the content directory just lock * and use it. */ imageLoader_set ( l, AVD_IMAGE_INITSYSTEM ); imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK ); /* the data partition - this one is special because if it * is missing, we need to copy the initial image file into it. * * first, try to see if it is in the content directory * (or the user-provided path) */ imageLoader_set( l, AVD_IMAGE_USERDATA ); if ( !imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_EMPTY_IF_MISSING | IMAGE_DONT_LOCK ) ) { /* it's not, we're going to initialize it. simply * forcing a data wipe should be enough */ D("initializing new data partition image: %s", l->pPath[0]); wipeData = 1; } if (wipeData) { /* find SDK source file */ const char* srcPath; if (imageLoader_lookupSdk(l)) { derror("can't locate initial %s image in SDK", l->imageText); exit(2); } srcPath = imageLoader_extractPath(l); imageLoader_copyFrom( l, srcPath ); AFREE((char*) srcPath); } else { /* lock the data partition image */ l->pState[0] = IMAGE_STATE_MUSTLOCK; imageLoader_lock( l, 0 ); } /* the cache partition: unless the user doesn't want one, * we're going to create it in the content directory */ if (!noCache) { imageLoader_set (l, AVD_IMAGE_CACHE); imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_EMPTY_IF_MISSING ); if (wipeCache) { if (path_empty_file(l->pPath[0]) < 0) { derror("cannot wipe %s image at %s: %s", l->imageText, l->pPath[0], strerror(errno)); exit(2); } } } /* the SD Card image. unless the user doesn't want to, we're * going to mount it if available. Note that if the image is * already used, we must ignore it. */ if (!noSdCard) { imageLoader_set (l, AVD_IMAGE_SDCARD); imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED); /* if the file was not found, ignore it */ if (l->pPath[0] && !path_exists(l->pPath[0])) { D("ignoring non-existing %s at %s: %s", l->imageText, l->pPath[0], strerror(errno)); /* if the user provided the SD Card path by hand, * warn him. */ if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL) dwarning("ignoring non-existing SD Card image"); imageLoader_setPath(l, NULL); } } return 0; } /* check that a given directory contains a valid skin. * returns 1 on success, 0 on failure. */ static int _checkSkinPath( const char* skinPath ) { char temp[MAX_PATH], *p=temp, *end=p+sizeof(temp); /* for now, if it has a 'layout' file, it is a valid skin path */ p = bufprint(temp, end, "%s/layout", skinPath); if (p >= end || !path_exists(temp)) return 0; return 1; } /* check that there is a skin named 'skinName' listed from 'skinDirRoot' * this returns 1 on success, 0 on failure * on success, the 'temp' buffer will get the path containing the real * skin directory (after alias expansion), including the skin name. */ static int _checkSkinDir( char* temp, char* end, const char* skinDirRoot, const char* skinName ) { DirScanner* scanner; char *p; int result; p = bufprint(temp, end, "%s/skins/%s", skinDirRoot, skinName); if (p >= end || !path_exists(temp)) { DD(" ignore bad skin directory %s", temp); return 0; } /* first, is this a normal skin directory ? */ if (_checkSkinPath(temp)) { /* yes */ DD(" found skin directory: %s", temp); return 1; } /* second, is it an alias to another skin ? */ *p = 0; result = 0; scanner = dirScanner_new(temp); if (scanner != NULL) { for (;;) { const char* file = dirScanner_next(scanner); if (file == NULL) break; if (strncmp(file, "alias-", 6) || file[6] == 0) continue; p = bufprint(temp, end, "%s/skins/%s", skinDirRoot, file+6); if (p < end && _checkSkinPath(temp)) { /* yes, it's an alias */ DD(" skin alias '%s' points to skin directory: %s", file+6, temp); result = 1; break; } } dirScanner_free(scanner); } return result; } /* try to see if the skin name leads to a magic skin or skin path directly * returns 1 on success, 0 on error. * on success, this sets up 'skinDirPath' and 'skinName' in the AvdInfo. */ static int _getSkinPathFromName( AvdInfo* i, const char* skinName ) { char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); /* if the skin name has the format 'NNNNxNNN' where * NNN is a decimal value, then this is a 'magic' skin * name that doesn't require a skin directory */ if (isdigit(skinName[0])) { int width, height; if (sscanf(skinName, "%dx%d", &width, &height) == 2) { D("'magic' skin format detected: %s", skinName); i->skinName = ASTRDUP(skinName); i->skinDirPath = NULL; return 1; } } /* is the skin name a direct path to the skin directory ? */ if (_checkSkinPath(skinName)) { goto FOUND_IT; } /* is the skin name a relative path from the SDK root ? */ p = bufprint(temp, end, "%s/%s", i->sdkRootPath, skinName); if (p < end && _checkSkinPath(temp)) { skinName = temp; goto FOUND_IT; } /* nope */ return 0; FOUND_IT: if (path_split(skinName, &i->skinDirPath, &i->skinName) < 0) { derror("malformed skin name: %s", skinName); exit(2); } D("found skin '%s' in directory: %s", i->skinName, i->skinDirPath); return 1; } /* return 0 on success, -1 on error */ static int _getSkin( AvdInfo* i, AvdInfoParams* params ) { char* skinName; char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); char explicitSkin = 1; /* this function is used to compute the 'skinName' and 'skinDirPath' * fields of the AvdInfo. */ /* processing here is a bit tricky, so here's how it happens * * - command-line option '-skin ' can be used to specify the * name of a skin, to override the AVD settings. * * - skins are searched from /../skins for each in the * images search list, unless a '-skindir ' option has been * provided on the command-line * * - otherwise, the config.ini can also contain a SKIN_PATH key that * shall give the full path to the skin directory, either relative * to the SDK root, or an absolute path. * * - skin names like '320x480' corresponds to "magic skins" that * simply display a framebuffer, without any ornaments of the * corresponding size. They do not correspond to any real skin * directory / files and are handled later. But they must be * recognized here and report a NULL skindir. */ if (params->skinName) { skinName = ASTRDUP(params->skinName); } else { skinName = iniFile_getString( i->configIni, SKIN_PATH ); explicitSkin = 0; } /* first, check that the skin name is not magic or a direct * directory path */ if (skinName != NULL && _getSkinPathFromName(i, skinName)) { AFREE(skinName); return 0; } /* if not, the default skinName is "HVGA" */ if (skinName == NULL) { skinName = ASTRDUP(SKIN_DEFAULT); explicitSkin = 0; } i->skinName = skinName; /* now try to find the skin directory for that name - * first try the content directory */ do { /* if there is a single 'skin' directory in * the content directory, assume that's what the * user wants, unless an explicit name was given */ if (!explicitSkin) { p = bufprint(temp, end, "%s/skin", i->contentPath); if (p < end && _checkSkinPath(temp)) { D("using skin content from %s", temp); AFREE(i->skinName); i->skinName = ASTRDUP("skin"); i->skinDirPath = ASTRDUP(i->contentPath); return 0; } } /* look in content directory */ if (_checkSkinDir(temp, end, i->contentPath, skinName)) break; /* look in the search paths. For each in the list, * look the skins in /.. */ { int nn; for (nn = 0; nn < i->numSearchPaths; nn++) { char* parentDir = path_parent(i->searchPaths[nn], 1); int ret; if (parentDir == NULL) continue; ret=_checkSkinDir(temp, end, parentDir, skinName); AFREE(parentDir); if (ret) break; } if (nn < i->numSearchPaths) break; } #if SUPPORT_PLATFORM_OR_ADDON /* look in the add-on directory, if any */ if (i->addonPath && _checkSkinDir(temp, end, i->addonPath, skinName)) break; /* look in the platforms directory */ if (_checkSkinDir(temp, end, i->platformPath, skinName)) break; #endif /* didn't find it */ if (explicitSkin) { derror("could not find directory for skin '%s'," " please use a different name", skinName); exit(2); } else { dwarning("no skin directory matched '%s', so reverted to default", skinName); AFREE(i->skinName); params->skinName = SKIN_DEFAULT; return _getSkin(i, params); } return -1; } while (0); /* separate skin name from parent directory. the skin name * returned in 'temp' might be different from the original * one due to alias expansion so strip it. */ AFREE(i->skinName); if (path_split(temp, &i->skinDirPath, &i->skinName) < 0) { derror("weird skin path: %s", temp); return -1; } DD("found skin '%s' in directory: %s", i->skinName, i->skinDirPath); return 0; } AvdInfo* avdInfo_new( const char* name, AvdInfoParams* params ) { AvdInfo* i; if (name == NULL) return NULL; if (!_checkAvdName(name)) { derror("virtual device name contains invalid characters"); exit(1); } ANEW0(i); i->deviceName = ASTRDUP(name); if ( _getSdkRoot(i) < 0 || _getRootIni(i) < 0 || _getContentPath(i) < 0 || _getConfigIni(i) < 0 ) goto FAIL; /* look for image search paths. handle post 1.1/pre cupcake * obsolete SDKs. */ _getSearchPaths(i); if (i->numSearchPaths == 0) { #if SUPPORT_PLATFORM_OR_ADDON /* no search paths, look for platform/add-on */ if (_getTarget(i) < 0) goto FAIL; #endif } /* don't need this anymore */ iniFile_free(i->rootIni); i->rootIni = NULL; if ( _getImagePaths(i, params) < 0 || _getSkin (i, params) < 0 ) goto FAIL; return i; FAIL: avdInfo_free(i); return NULL; } /*************************************************************** *************************************************************** ***** ***** ANDROID BUILD SUPPORT ***** ***** The code below corresponds to the case where we're ***** starting the emulator inside the Android build ***** system. The main differences are that: ***** ***** - the $ANDROID_PRODUCT_OUT directory is used as the ***** content file. ***** ***** - built images must not be modified by the emulator, ***** so system.img must be copied to a temporary file ***** and userdata.img must be copied to userdata-qemu.img ***** if the latter doesn't exist. ***** ***** - the kernel and default skin directory are taken from ***** prebuilt ***** ***** - there is no root .ini file, or any config.ini in ***** the content directory, no SDK platform version ***** and no add-on to consider. *****/ /* used to fake a config.ini located in the content directory */ static int _getBuildConfigIni( AvdInfo* i ) { /* a blank file is ok at the moment */ i->configIni = iniFile_newFromMemory( "", 0 ); return 0; } static int _getBuildImagePaths( AvdInfo* i, AvdInfoParams* params ) { int wipeData = (params->flags & AVDINFO_WIPE_DATA) != 0; int noCache = (params->flags & AVDINFO_NO_CACHE) != 0; int noSdCard = (params->flags & AVDINFO_NO_SDCARD) != 0; char temp[PATH_MAX], *p=temp, *end=p+sizeof temp; char* srcData; ImageLoader l[1]; imageLoader_init(l, i, params); /** load the kernel image **/ /* if it is not in the out directory, get it from prebuilt */ imageLoader_set ( l, AVD_IMAGE_KERNEL ); if ( !imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK ) ) { #define PREBUILT_KERNEL_PATH "prebuilt/android-arm/kernel/kernel-qemu" p = bufprint(temp, end, "%s/%s", i->androidBuildRoot, PREBUILT_KERNEL_PATH); if (p >= end || !path_exists(temp)) { derror("bad workspace: cannot find prebuilt kernel in: %s", temp); exit(1); } imageLoader_setPath(l, temp); } /** load the data partition. note that we use userdata-qemu.img ** since we don't want to modify userdata.img at all **/ imageLoader_set ( l, AVD_IMAGE_USERDATA ); imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK ); /* get the path of the source file, and check that it actually exists * if the user didn't provide an explicit data file */ srcData = imageLoader_extractPath(l); if (srcData == NULL && params->forcePaths[AVD_IMAGE_USERDATA] == NULL) { derror("There is no %s image in your build directory. Please make a full build", l->imageText, l->imageFile); exit(2); } /* get the path of the target file */ l->imageFile = "userdata-qemu.img"; imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_EMPTY_IF_MISSING | IMAGE_IGNORE_IF_LOCKED ); /* force a data wipe if we just created the image */ if (l->pState[0] == IMAGE_STATE_LOCKED_EMPTY) wipeData = 1; /* if the image was already locked, create a temp file * then force a data wipe. */ if (l->pPath[0] == NULL) { TempFile* temp = tempfile_create(); imageLoader_setPath(l, tempfile_path(temp)); dwarning( "Another emulator is running. user data changes will *NOT* be saved"); wipeData = 1; } /* in the case of a data wipe, copy userdata.img into * the destination */ if (wipeData) { if (srcData == NULL || !path_exists(srcData)) { derror("There is no %s image in your build directory. Please make a full build", l->imageText, _imageFileNames[l->id]); exit(2); } if (path_copy_file( l->pPath[0], srcData ) < 0) { derror("could not initialize %s image from %s: %s", l->imageText, temp, strerror(errno)); exit(2); } } AFREE(srcData); /** load the ramdisk image **/ imageLoader_set ( l, AVD_IMAGE_RAMDISK ); imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK ); /** load the system image. read-only. the caller must ** take care of checking the state **/ imageLoader_set ( l, AVD_IMAGE_INITSYSTEM ); imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK ); /* force the system image to read-only status */ l->pState[0] = IMAGE_STATE_READONLY; /** cache partition handling **/ if (!noCache) { imageLoader_set (l, AVD_IMAGE_CACHE); /* if the user provided one cache image, lock & use it */ if ( params->forcePaths[l->id] != NULL ) { imageLoader_load(l, IMAGE_REQUIRED | IMAGE_IGNORE_IF_LOCKED); } } /** SD Card image **/ if (!noSdCard) { imageLoader_set (l, AVD_IMAGE_SDCARD); imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED); } return 0; } static int _getBuildSkin( AvdInfo* i, AvdInfoParams* params ) { /* the (current) default skin name for our build system */ const char* skinName = params->skinName; const char* skinDir = params->skinRootPath; char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); char* q; if (!skinName) { /* the (current) default skin name for the build system */ skinName = SKIN_DEFAULT; DD("selecting default skin name '%s'", skinName); } i->skinName = ASTRDUP(skinName); if (!skinDir) { #define PREBUILT_SKINS_DIR "development/emulator/skins" /* the (current) default skin directory */ p = bufprint( temp, end, "%s/%s", i->androidBuildRoot, PREBUILT_SKINS_DIR ); } else { p = bufprint( temp, end, "%s", skinDir ); } q = bufprint(p, end, "/%s/layout", skinName); if (q >= end || !path_exists(temp)) { DD("skin content directory does not exist: %s", temp); if (skinDir) dwarning("could not find valid skin '%s' in %s:\n", skinName, temp); return -1; } *p = 0; DD("found skin path: %s", temp); i->skinDirPath = ASTRDUP(temp); return 0; } AvdInfo* avdInfo_newForAndroidBuild( const char* androidBuildRoot, const char* androidOut, AvdInfoParams* params ) { AvdInfo* i; ANEW0(i); i->inAndroidBuild = 1; i->androidBuildRoot = ASTRDUP(androidBuildRoot); i->androidOut = ASTRDUP(androidOut); i->contentPath = ASTRDUP(androidOut); /* TODO: find a way to provide better information from the build files */ i->deviceName = ASTRDUP(""); if (_getBuildConfigIni(i) < 0 || _getBuildImagePaths(i, params) < 0 ) goto FAIL; /* we don't need to fail if there is no valid skin */ _getBuildSkin(i, params); return i; FAIL: avdInfo_free(i); return NULL; } const char* avdInfo_getName( AvdInfo* i ) { return i ? i->deviceName : NULL; } const char* avdInfo_getImageFile( AvdInfo* i, AvdImageType imageType ) { if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX) return NULL; return i->imagePath[imageType]; } int avdInfo_isImageReadOnly( AvdInfo* i, AvdImageType imageType ) { if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX) return 1; return (i->imageState[imageType] == IMAGE_STATE_READONLY); } const char* avdInfo_getSkinName( AvdInfo* i ) { return i->skinName; } const char* avdInfo_getSkinDir ( AvdInfo* i ) { return i->skinDirPath; } int avdInfo_getHwConfig( AvdInfo* i, AndroidHwConfig* hw ) { IniFile* ini = i->configIni; int ret; if (ini == NULL) ini = iniFile_newFromMemory("", 0); ret = androidHwConfig_read(hw, ini); if (ini != i->configIni) iniFile_free(ini); return ret; } const char* avdInfo_getContentPath( AvdInfo* i ) { return i->contentPath; } int avdInfo_inAndroidBuild( AvdInfo* i ) { return i->inAndroidBuild; } char* avdInfo_getTracePath( AvdInfo* i, const char* traceName ) { char tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp); if (i == NULL || traceName == NULL || traceName[0] == 0) return NULL; if (i->inAndroidBuild) { p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", i->androidOut, traceName ); } else { p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s", i->contentPath, traceName ); } return ASTRDUP(tmp); }