auto import from //depot/cupcake/@135843
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
new file mode 100644
index 0000000..f2293d1
--- /dev/null
+++ b/android/avd/hardware-properties.ini
@@ -0,0 +1,158 @@
+# This file describes the properties of a given virtual device configuration file.
+#
+# Note: Most top-level properties are boolean that control whether a feature is
+#       present or not. Sub-features that depend on it are ignored if their
+#       parent is set to 'false' or 'no'
+#
+# This file is parsed by 'android/tools/gen-hw-config.py' to generate
+# 'android/avd/hw-config-defs.h'. The latter is a special header containing
+# macro statements that is used several times:
+#
+#  - once to define the fields of the AndroidHwConfig structure
+#    (see android/avd/hw-config.h)
+#
+#  - once to implement the hardware configuration loader
+#    (see android/avd/hw-config.h)
+#
+# Hopefully, this file should also be read by a virtual device creation
+# tool/wizard to provide a nice user interface (hence the presence of
+# the 'abstract' and 'description' keys which are not currently used)
+#
+#
+# NOTE: if you remove items from this file, be sure that you do not break
+#       the emulator build.
+#
+
+# Ram size
+name        = hw.ramSize
+type        = integer
+default     = 96
+abstract    = Device ram size
+description = The amount of physical RAM on the device, in megabytes.
+
+# Touch screen support
+name        = hw.touchScreen
+type        = boolean
+default     = yes
+abstract    = Touch-screen support
+description = Whether there is a touch screen or not on the device.
+
+# Trackball support
+name        = hw.trackBall
+type        = boolean
+default     = yes
+abstract    = Track-ball support
+description = Whether there is a trackball on the device.
+
+# Keyboard support (qwerty/azerty)
+name        = hw.keyboard
+type        = boolean
+default     = yes
+abstract    = Keyboard support
+description = Whether the device has a QWERTY keyboard.
+
+# DPad keys
+name        = hw.dPad
+type        = boolean
+default     = yes
+abstract    = DPad support
+description = Whether the device has DPad keys
+
+# GSM Modem support
+name        = hw.gsmModem
+type        = boolean
+default     = yes
+abstract    = GSM modem support
+description = Whether there is a GSM modem in the device.
+
+# Wifi support
+name        = hw.wifi
+type        = boolean
+default     = no
+abstract    = Wifi support
+description = Whether the device has a Wifi chipset.
+
+# Bluetooth support
+name        = hw.bluetooth
+type        = boolean
+default     = no
+abstract    = Bluetooth support
+description = Whether the device has a Bluetooth chipset.
+
+# Camera support
+name        = hw.camera
+type        = boolean
+default     = no
+abstract    = Camera support
+description = Whether the device has a camera.
+
+name        = hw.camera.maxHorizontalPixels
+type        = integer
+default     = 640
+abstract    = Maximum horizontal camera pixels
+
+name        = hw.camera.maxVerticalPixels
+type        = integer
+default     = 480
+abstract    = Maximum vertical camera pixels
+
+# GPS support
+name        = hw.gps
+type        = boolean
+default     = no
+abstract    = GPS support
+description = Whether there is a GPS in the device.
+
+# Accelerometer
+name        = hw.accelerometer
+type        = boolean
+default     = no
+abstract    = Accelerometer support
+description = Whether there is an accelerometer in the device.
+
+# Battery
+name        = hw.battery
+type        = boolean
+default     = yes
+abstract    = Battery support
+description = Whether the device can run on a battery.
+
+# Audio input
+name        = hw.audioInput
+type        = boolean
+default     = yes
+abstract    = Audio recording support
+description = Whether the device can record audio
+
+# Audio output
+name        = hw.audioOutput
+type        = boolean
+default     = yes
+abstract    = Audio playback support
+description = Whether the device can play audio
+
+# Compass
+name        = hw.compass
+type        = boolean
+default     = no
+abstract    = Compass support
+description = Whether there is a compass in the device.
+
+# SDCard support
+name        = hw.sdCard
+type        = boolean
+default     = yes
+abstract    = SD Card support
+description = Whether the device supports insertion/removal of virtual SD Cards.
+
+# Cache partition
+name        = disk.cachePartition
+type        = boolean
+default     = yes
+abstract    = Cache partition support
+description = Whether we use a /cache partition on the device.
+
+name        = disk.cachePartition.size
+type        = diskSize
+abstract    = Cache partition size
+default     = 66MB
diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h
new file mode 100644
index 0000000..5c3b9ab
--- /dev/null
+++ b/android/avd/hw-config-defs.h
@@ -0,0 +1,165 @@
+/* this file is automatically generated from 'hardware-properties.ini'
+ * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py'
+ */
+#ifndef HWCFG_INT
+#error  HWCFG_INT not defined
+#endif
+#ifndef HWCFG_BOOL
+#error  HWCFG_BOOL not defined
+#endif
+#ifndef HWCFG_DISKSIZE
+#error  HWCFG_DISKSIZE not defined
+#endif
+#ifndef HWCFG_STRING
+#error  HWCFG_STRING not defined
+#endif
+#ifndef HWCFG_DOUBLE
+#error  HWCFG_DOUBLE not defined
+#endif
+
+HWCFG_INT(
+  hw_ramSize,
+  "hw.ramSize",
+  96,
+  "Device ram size",
+  "The amount of physical RAM on the device, in megabytes.")
+
+HWCFG_BOOL(
+  hw_touchScreen,
+  "hw.touchScreen",
+  "yes",
+  "Touch-screen support",
+  "Whether there is a touch screen or not on the device.")
+
+HWCFG_BOOL(
+  hw_trackBall,
+  "hw.trackBall",
+  "yes",
+  "Track-ball support",
+  "Whether there is a trackball on the device.")
+
+HWCFG_BOOL(
+  hw_keyboard,
+  "hw.keyboard",
+  "yes",
+  "Keyboard support",
+  "Whether the device has a QWERTY keyboard.")
+
+HWCFG_BOOL(
+  hw_dPad,
+  "hw.dPad",
+  "yes",
+  "DPad support",
+  "Whether the device has DPad keys")
+
+HWCFG_BOOL(
+  hw_gsmModem,
+  "hw.gsmModem",
+  "yes",
+  "GSM modem support",
+  "Whether there is a GSM modem in the device.")
+
+HWCFG_BOOL(
+  hw_wifi,
+  "hw.wifi",
+  "no",
+  "Wifi support",
+  "Whether the device has a Wifi chipset.")
+
+HWCFG_BOOL(
+  hw_bluetooth,
+  "hw.bluetooth",
+  "no",
+  "Bluetooth support",
+  "Whether the device has a Bluetooth chipset.")
+
+HWCFG_BOOL(
+  hw_camera,
+  "hw.camera",
+  "no",
+  "Camera support",
+  "Whether the device has a camera.")
+
+HWCFG_INT(
+  hw_camera_maxHorizontalPixels,
+  "hw.camera.maxHorizontalPixels",
+  640,
+  "Maximum horizontal camera pixels",
+  "")
+
+HWCFG_INT(
+  hw_camera_maxVerticalPixels,
+  "hw.camera.maxVerticalPixels",
+  480,
+  "Maximum vertical camera pixels",
+  "")
+
+HWCFG_BOOL(
+  hw_gps,
+  "hw.gps",
+  "no",
+  "GPS support",
+  "Whether there is a GPS in the device.")
+
+HWCFG_BOOL(
+  hw_accelerometer,
+  "hw.accelerometer",
+  "no",
+  "Accelerometer support",
+  "Whether there is an accelerometer in the device.")
+
+HWCFG_BOOL(
+  hw_battery,
+  "hw.battery",
+  "yes",
+  "Battery support",
+  "Whether the device can run on a battery.")
+
+HWCFG_BOOL(
+  hw_audioInput,
+  "hw.audioInput",
+  "yes",
+  "Audio recording support",
+  "Whether the device can record audio")
+
+HWCFG_BOOL(
+  hw_audioOutput,
+  "hw.audioOutput",
+  "yes",
+  "Audio playback support",
+  "Whether the device can play audio")
+
+HWCFG_BOOL(
+  hw_compass,
+  "hw.compass",
+  "no",
+  "Compass support",
+  "Whether there is a compass in the device.")
+
+HWCFG_BOOL(
+  hw_sdCard,
+  "hw.sdCard",
+  "yes",
+  "SD Card support",
+  "Whether the device supports insertion/removal of virtual SD Cards.")
+
+HWCFG_BOOL(
+  disk_cachePartition,
+  "disk.cachePartition",
+  "yes",
+  "Cache partition support",
+  "Whether we use a /cache partition on the device.")
+
+HWCFG_DISKSIZE(
+  disk_cachePartition_size,
+  "disk.cachePartition.size",
+  "66MB",
+  "Cache partition size",
+  "")
+
+#undef HWCFG_INT
+#undef HWCFG_BOOL
+#undef HWCFG_DISKSIZE
+#undef HWCFG_STRING
+#undef HWCFG_DOUBLE
+/* end of auto-generated file */
diff --git a/android/avd/hw-config.c b/android/avd/hw-config.c
new file mode 100644
index 0000000..2362b59
--- /dev/null
+++ b/android/avd/hw-config.c
@@ -0,0 +1,39 @@
+/* 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/hw-config.h"
+#include "android/utils/ini.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+/* the global variable containing the hardware config for this device */
+AndroidHwConfig   android_hw[1];
+
+int
+androidHwConfig_read( AndroidHwConfig*  config,
+                      IniFile*          ini )
+{
+    if (ini == NULL)
+        return -1;
+
+    /* use the magic of macros to implement the hardware configuration loaded */
+
+#define   HWCFG_BOOL(n,s,d,a,t)       config->n = iniFile_getBoolean(ini, s, d);
+#define   HWCFG_INT(n,s,d,a,t)        config->n = iniFile_getInteger(ini, s, d);
+#define   HWCFG_STRING(n,s,d,a,t)     config->n = iniFile_getString(ini, s, d);
+#define   HWCFG_DOUBLE(n,s,d,a,t)     config->n = iniFile_getDouble(ini, s, d);
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   config->n = iniFile_getDiskSize(ini, s, d);
+
+#include "android/avd/hw-config-defs.h"
+
+    return 0;
+}
diff --git a/android/avd/hw-config.h b/android/avd/hw-config.h
new file mode 100644
index 0000000..05eb828
--- /dev/null
+++ b/android/avd/hw-config.h
@@ -0,0 +1,46 @@
+/* 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.
+*/
+#ifndef _ANDROID_AVD_HW_CONFIG_H
+#define _ANDROID_AVD_HW_CONFIG_H
+
+#include <stdint.h>
+#include "android/utils/ini.h"
+
+typedef char      hw_bool_t;
+typedef int       hw_int_t;
+typedef int64_t   hw_disksize_t;
+typedef char*     hw_string_t;
+typedef double    hw_double_t;
+
+/* these macros are used to define the fields of AndroidHwConfig
+ * declared below
+ */
+#define   HWCFG_BOOL(n,s,d,a,t)       hw_bool_t      n;
+#define   HWCFG_INT(n,s,d,a,t)        hw_int_t       n;
+#define   HWCFG_STRING(n,s,d,a,t)     hw_string_t    n;
+#define   HWCFG_DOUBLE(n,s,d,a,t)     hw_double_t    n;
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   hw_disksize_t  n;
+
+typedef struct {
+#include "android/avd/hw-config-defs.h"
+} AndroidHwConfig;
+
+/* reads a hardware configuration file from disk.
+ * returns -1 if the file could not be read, or 0 in case of success.
+ *
+ * note that default values are written to hwConfig if the configuration
+ * file doesn't have the corresponding hardware properties.
+ */
+int  androidHwConfig_read( AndroidHwConfig*  hwConfig,
+                           IniFile*          configFile );
+
+#endif /* _ANDROID_AVD_HW_CONFIG_H */
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);
+}
diff --git a/android/avd/info.h b/android/avd/info.h
new file mode 100644
index 0000000..6cd97dc
--- /dev/null
+++ b/android/avd/info.h
@@ -0,0 +1,171 @@
+/* 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.
+*/
+#ifndef ANDROID_AVD_INFO_H
+#define ANDROID_AVD_INFO_H
+
+#include "android/utils/ini.h"
+#include "android/avd/hw-config.h"
+
+/* An Android Virtual Device (AVD for short) corresponds to a
+ * directory containing all kernel/disk images for a given virtual
+ * device, as well as information about its hardware capabilities,
+ * SDK version number, skin, etc...
+ *
+ * Each AVD has a human-readable name and is backed by a root
+ * configuration file and a content directory. For example, an
+ *  AVD named 'foo' will correspond to the following:
+ *
+ *  - a root configuration file named ~/.android/avd/foo.ini
+ *    describing where the AVD's content can be found
+ *
+ *  - a content directory like ~/.android/avd/foo/ containing all
+ *    disk image and configuration files for the virtual device.
+ *
+ * the 'foo.ini' file should contain at least one line of the form:
+ *
+ *    rootPath=<content-path>
+ *
+ * it may also contain other lines that cache stuff found in the
+ * content directory, like hardware properties or SDK version number.
+ *
+ * it is possible to move the content directory by updating the foo.ini
+ * file to point to the new location. This can be interesting when your
+ * $HOME directory is located on a network share or in a roaming profile
+ * (Windows), given that the content directory of a single virtual device
+ * can easily use more than 100MB of data.
+ *
+ */
+
+/* a macro used to define the list of disk images managed by the
+ * implementation. This macro will be expanded several times with
+ * varying definitions of _AVD_IMG
+ */
+#define  AVD_IMAGE_LIST \
+    _AVD_IMG(KERNEL,"kernel-qemu","kernel") \
+    _AVD_IMG(RAMDISK,"ramdisk.img","ramdisk") \
+    _AVD_IMG(INITSYSTEM,"system.img","init system") \
+    _AVD_IMG(INITDATA,"userdata.img","init data") \
+    _AVD_IMG(USERSYSTEM,"system-qemu.img","user system") \
+    _AVD_IMG(USERDATA,"userdata-qemu.img", "user data") \
+    _AVD_IMG(CACHE,"cache.img","cache") \
+    _AVD_IMG(SDCARD,"sdcard.img","SD Card") \
+
+/* define the enumared values corresponding to each AVD image type
+ * examples are: AVD_IMAGE_KERNEL, AVD_IMAGE_SYSTEM, etc..
+ */
+#define _AVD_IMG(x,y,z)   AVD_IMAGE_##x ,
+typedef enum {
+    AVD_IMAGE_LIST
+    AVD_IMAGE_MAX /* do not remove */
+} AvdImageType;
+#undef  _AVD_IMG
+
+/* AvdInfo is an opaque structure used to model the information
+ * corresponding to a given AVD instance
+ */
+typedef struct AvdInfo  AvdInfo;
+
+/* various flags used when creating an AvdInfo object */
+typedef enum {
+    /* use to force a data wipe */
+    AVDINFO_WIPE_DATA = (1 << 0),
+    /* use to ignore the cache partition */
+    AVDINFO_NO_CACHE  = (1 << 1),
+    /* use to wipe cache partition, ignored if NO_CACHE is set */
+    AVDINFO_WIPE_CACHE = (1 << 2),
+    /* use to ignore ignore SDCard image (default or provided) */
+    AVDINFO_NO_SDCARD = (1 << 3),
+    /* use to wipe the system image with new initial values */
+    AVDINFO_WIPE_SYSTEM = (1 << 4),
+} AvdFlags;
+
+typedef struct {
+    unsigned     flags;
+    const char*  skinName;
+    const char*  skinRootPath;
+    const char*  forcePaths[AVD_IMAGE_MAX];
+} AvdInfoParams;
+
+/* Creates a new AvdInfo object from a name. Returns NULL if name is NULL
+ * or contains characters that are not part of the following list:
+ * letters, digits, underscores, dashes and periods
+ */
+AvdInfo*  avdInfo_new( const char*  name, AvdInfoParams*  params );
+
+/* A special function used to setup an AvdInfo for use when starting
+ * the emulator from the Android build system. In this specific instance
+ * we're going to create temporary files to hold all writable image
+ * files, and activate all hardware features by default
+ *
+ * 'androidBuildRoot' must be the absolute path to the root of the
+ * Android build system (i.e. the 'android' directory)
+ *
+ * 'androidOut' must be the target-specific out directory where
+ * disk images will be looked for.
+ */
+AvdInfo*  avdInfo_newForAndroidBuild( const char*     androidBuildRoot,
+                                      const char*     androidOut,
+                                      AvdInfoParams*  params );
+
+/* Frees an AvdInfo object and the corresponding strings that may be
+ * returned by its getXXX() methods
+ */
+void        avdInfo_free( AvdInfo*  i );
+
+/* Return the name of the Android Virtual Device
+ */
+const char*  avdInfo_getName( AvdInfo*  i );
+
+/* Try to find the path of a given image file, returns NULL
+ * if the corresponding file could not be found. the string
+ * belongs to the AvdInfo object.
+ */
+const char*  avdInfo_getImageFile( AvdInfo*  i, AvdImageType  imageType );
+
+/* Returns 1 if the corresponding image file is read-only
+ */
+int          avdInfo_isImageReadOnly( AvdInfo*  i, AvdImageType  imageType );
+
+/* lock an image file if it is writable. returns 0 on success, or -1
+ * otherwise. note that if the file is read-only, it doesn't need to
+ * be locked and the function will return success.
+ */
+int          avdInfo_lockImageFile( AvdInfo*  i, AvdImageType  imageType, int  abortOnError);
+
+/* Manually set the path of a given image file. */
+void         avdInfo_setImageFile( AvdInfo*  i, AvdImageType  imageType, const char*  imagePath );
+
+/* Returns the path of the skin directory */
+/* the string belongs to the AvdInfo object */
+const char*  avdInfo_getSkinPath( AvdInfo*  i );
+
+/* Returns the name of the virtual device's skin */
+const char*  avdInfo_getSkinName( AvdInfo*  i );
+
+/* Returns the root skin directory for this device */
+const char*  avdInfo_getSkinDir ( AvdInfo*  i );
+
+/* Returns the content path of the virtual device */
+const char*  avdInfo_getContentPath( AvdInfo*  i );
+
+/* Returns TRUE iff in the Android build system */
+int          avdInfo_inAndroidBuild( AvdInfo*  i );
+
+/* Reads the AVD's hardware configuration into 'hw'. returns -1 on error, 0 otherwise */
+int          avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw );
+
+/* Returns a *copy* of the path used to store trace 'foo'. result must be freed by caller */
+char*        avdInfo_getTracePath( AvdInfo*  i, const char*  traceName );
+
+/* */
+
+#endif /* ANDROID_AVD_INFO_H */