diff options
Diffstat (limited to 'android/avd')
-rw-r--r-- | android/avd/hardware-properties.ini | 158 | ||||
-rw-r--r-- | android/avd/hw-config-defs.h | 165 | ||||
-rw-r--r-- | android/avd/hw-config.c | 39 | ||||
-rw-r--r-- | android/avd/hw-config.h | 46 | ||||
-rw-r--r-- | android/avd/info.c | 1585 | ||||
-rw-r--r-- | android/avd/info.h | 161 |
6 files changed, 2154 insertions, 0 deletions
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..3018d2f --- /dev/null +++ b/android/avd/info.c @@ -0,0 +1,1585 @@ +/* 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 "qemu-common.h" +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +/* 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: + * + * 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" + +/* this is the subdirectory of $HOME/.android where all + * root configuration files (and default content directories) + * are located. + */ +#define ANDROID_AVD_DIR "avd" + +/* 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; + int platformVersion; + char* platformPath; + char* addonTarget; + char* addonPath; + 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++) + qemu_free(i->imagePath[nn]); + + qemu_free(i->skinName); + qemu_free(i->skinDirPath); + + if (i->configIni) { + iniFile_free(i->configIni); + i->configIni = NULL; + } + + if (i->rootIni) { + iniFile_free(i->rootIni); + i->rootIni = NULL; + } + + qemu_free(i->contentPath); + qemu_free(i->sdkRootPath); + + if (i->inAndroidBuild) { + qemu_free(i->androidOut); + qemu_free(i->androidBuildRoot); + } else { + qemu_free(i->platformPath); + qemu_free(i->addonTarget); + qemu_free(i->addonPath); + } + + qemu_free(i->deviceName); + qemu_free(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 = qemu_strdup(env); + 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; +} + +/* 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 = qemu_strdup(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 = qemu_strdup(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 = qemu_strdup(subdir); + break; + } + } + + dirScanner_free(scanner); + + DD("< returning %s", subdir ? subdir : "<NULL>"); + return subdir; + +FAIL: + qemu_free(targetCopy); + return NULL; +} + +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" +# define ROOT_TARGET_KEY "target" + +/* retrieve the content path and target from the root .ini file */ +static int +_getTarget( AvdInfo* i ) +{ + i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY); + i->addonTarget = iniFile_getString(i->rootIni, ROOT_TARGET_KEY); + + iniFile_free(i->rootIni); + i->rootIni = NULL; + + if (i->contentPath == NULL) { + derror("bad config: %s", + "virtual device file lacks a "ROOT_PATH_KEY" entry"); + return -1; + } + + if (i->addonTarget == NULL) { + derror("bad config: %s", + "virtual device file lacks a "ROOT_TARGET_KEY" entry"); + return -1; + } + + D("virtual device content at %s", i->contentPath); + 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; +} + + +/* 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 ? qemu_strdup(path) : NULL; + + qemu_free(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 add-on and/or platform + * 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; + char* temp = l->temp, *p = temp, *end = p + sizeof(l->temp); + + do { + /* try the add-on directory, if any */ + if (i->addonPath != NULL) { + DD("searching %s in add-on directory: %s", + l->imageFile, i->addonPath); + + p = bufprint(temp, end, "%s/images/%s", + i->addonPath, l->imageFile); + + if (p < end && path_exists(temp)) + break; + } + + /* or try the platform directory */ + DD("searching %s in platform directory: %s", + l->imageFile, i->platformPath); + + p = bufprint(temp, end, "%s/images/%s", + i->platformPath, l->imageFile); + if (p < end && path_exists(temp)) + break; + + DD("could not find %s in SDK", l->imageFile); + return NULL; + + } while (0); + + 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); + + DD("searching %s in content directory", l->imageFile); + 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("not found %s in content directory", l->imageFile); + return NULL; + } + + /* 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; + + DD("looking for %s image (%s)", l->imageText, l->imageFile); + + /* first, check user-provided path */ + path = l->params->forcePaths[l->id]; + if (path != NULL) { + imageLoader_setPath(l, path); + if (path_exists(path)) + 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 = qemu_strdup(l->temp); + + /* it's not there */ + if (flags & IMAGE_SEARCH_SDK) { + /* third, look in the SDK directory */ + path = imageLoader_lookupSdk(l); + if (path) { + qemu_free((char*)contentFile); + goto EXIT; + } + } + DD("found no %s image (%s)", l->imageText, l->imageFile); + + /* if the file is required, abort */ + if (flags & IMAGE_REQUIRED) { + derror("could not find required %s image (%s)", + l->imageText, l->imageFile); + exit(2); + } + + path = imageLoader_setPath(l, contentFile); + qemu_free((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_SYSTEM ); + 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 ); + qemu_free((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 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, *q; + int result; + + p = bufprint(temp, end, "%s/skins/%s", + skinDirRoot, skinName); + + DD("probing skin content in %s", temp); + + if (p >= end || !path_exists(temp)) { + return 0; + } + + /* first, is this a normal skin directory ? */ + q = bufprint(p, end, "/layout"); + if (q < end && path_exists(temp)) { + /* yes */ + *p = 0; + 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); + + q = bufprint(p, end, "/layout"); + if (q < end && path_exists(temp)) { + /* yes, it's an alias */ + *p = 0; + result = 1; + break; + } + } + dirScanner_free(scanner); + } + return result; +} + +static int +_getSkin( AvdInfo* i, AvdInfoParams* params ) +{ + char* skinName; + char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); + char explicitSkin = 1; + + /* determine the skin name, the default is "default" + * unless specified by the caller or in config.ini + */ + if (params->skinName) { + skinName = qemu_strdup(params->skinName); + } else { + skinName = iniFile_getString( i->configIni, "skin" ); + if (skinName == NULL) { + skinName = qemu_strdup("default"); + explicitSkin = 0; + } + } + + i->skinName = skinName; + + /* 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->skinDirPath = NULL; + return 0; + } + } + + /* 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) { + char* q; + + p = bufprint(temp, end, "%s/skin", i->contentPath); + q = bufprint(p, end, "/layout"); + if (q < end && path_exists(temp)) { + /* use this one - cheat a little */ + *p = 0; + D("using skin content from %s", temp); + qemu_free(i->skinName); + i->skinName = qemu_strdup("skin"); + i->skinDirPath = qemu_strdup(i->contentPath); + return 0; + } + } + + /* look in content directory */ + if (_checkSkinDir(temp, end, i->contentPath, skinName)) + break; + + /* 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; + + /* didn't find it */ + if (explicitSkin) + dwarning("could not find directory for skin '%s'", skinName); + + 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. + */ + p = strrchr(temp, '/'); + if (p == NULL) { + /* should not happen */ + DD("weird skin path: %s", temp); + return -1; + } + + *p = 0; + DD("found skin content in %s", temp); + i->skinDirPath = qemu_strdup(temp); + 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); + } + + i = qemu_mallocz(sizeof *i); + i->deviceName = qemu_strdup(name); + + if ( _getSdkRoot(i) < 0 || + _getRootIni(i) < 0 || + _getTarget(i) < 0 || + _getConfigIni(i) < 0 ) + goto FAIL; + + 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); + } + } + + qemu_free(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_SYSTEM ); + 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 = "HVGA"; + DD("selecting default skin name '%s'", skinName); + } + + i->skinName = qemu_strdup(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 = qemu_strdup(temp); + + return 0; +} + +AvdInfo* +avdInfo_newForAndroidBuild( const char* androidBuildRoot, + const char* androidOut, + AvdInfoParams* params ) +{ + AvdInfo* i; + + i = qemu_mallocz(sizeof *i); + + i->inAndroidBuild = 1; + i->androidBuildRoot = qemu_strdup(androidBuildRoot); + i->androidOut = qemu_strdup(androidOut); + i->contentPath = qemu_strdup(androidOut); + + /* TODO: find a way to provide better information from the build files */ + i->deviceName = qemu_strdup("<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; +} + + +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 qemu_strdup(tmp); +} diff --git a/android/avd/info.h b/android/avd/info.h new file mode 100644 index 0000000..ad8a138 --- /dev/null +++ b/android/avd/info.h @@ -0,0 +1,161 @@ +/* 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(SYSTEM,"system.img","system") \ + _AVD_IMG(USERDATA,"userdata.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), +} 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 ); + +/* 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 */ |