aboutsummaryrefslogtreecommitdiffstats
path: root/android/avd/info.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/avd/info.c')
-rw-r--r--android/avd/info.c1865
1 files changed, 1865 insertions, 0 deletions
diff --git a/android/avd/info.c b/android/avd/info.c
new file mode 100644
index 0000000..230ddaa
--- /dev/null
+++ b/android/avd/info.c
@@ -0,0 +1,1865 @@
+/* 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 <ctype.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* 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 = <first search path>
+ * images.sysdir.2 = <second search path>
+ *
+ * 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/
+ * <platform1>/
+ * build.prop
+ * images/
+ * <default kernel/disk images>
+ * skins/
+ * default/ --> default skin
+ * layout
+ * <skin bitmaps>
+ * <skin2>/ --> another skin
+ * layout
+ * <skin bitmaps>
+ * <skin3>/ --> skin alias to <skin2>
+ * alias-<skin2>
+ *
+ * <platform2>/
+ * build.prop
+ * images/
+ * <other default kernel/disk images>
+ *
+ * add-ons/
+ * <partner1>/
+ * manifest.ini
+ * images/
+ * <replacement disk images>
+ *
+ * <partner2>/
+ * manifest.ini
+ * <replacement disk images>
+ * hardware.ini
+ * skins/
+ * default/
+ * layout
+ * <skin bitmaps>
+ * <skin2>/
+ * layout
+ * <skin bitmaps>
+ *
+ *
+ * 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=<version>' where
+ * <version> 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=<addOnName>
+ * vendor=<vendorName>
+ * api=<version>
+ *
+ * where <version> 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
+ * <vendor>:<name>:<sdk> 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/<foo>.ini
+ * where <foo> 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=<pathToContentDirectory>
+ * target=<targetAddonOrPlatform>
+ *
+ * the 'path' value must point to the location of
+ * the virtual device's content directory. By default, this
+ * should be ~/.android/avd/<foo>/, though the user should be
+ * able to choose an alternative path at creation time.
+ *
+ * the 'target' value can be one of:
+ *
+ * android-<version>
+ * <vendor>:<name>:<version>
+ *
+ * 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, <version> must be an integer that
+ * matches one of the available platforms.
+ *
+ * <vendor>:<name>:<version> 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=<pathToDefaultSDCard>
+ * skin=<defaultSkinName>
+ * options=<additionalEmulatorStartupOptions>
+ *
+ *
+ * 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-<other>.
+ * if a file exist by that name, look at $PLATFORM/skins/<other>/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 <foo>.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 : "<NULL>",
+ name ? name : "<NULL>",
+ version);
+
+ iniFile_free(ini);
+
+ if (matches == 3) {
+ /* bingo */
+ *pversion = version;
+ subdir = ASTRDUP(subdir);
+ break;
+ }
+ }
+
+ dirScanner_free(scanner);
+
+ DD("< returning %s", subdir ? subdir : "<NULL>");
+ 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/<name>.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 <name>' can be used to specify the
+ * name of a skin, to override the AVD settings.
+ *
+ * - skins are searched from <dir>/../skins for each <dir> in the
+ * images search list, unless a '-skindir <path>' 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 <dir> in the list,
+ * look the skins in <dir>/.. */
+ {
+ 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("<build>");
+
+ 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);
+}