AI 143828: am: CL 143808 am: CL 143754 SdkManager: list unknown AVDs and why they didn't load.

Original author: raphael
  Merged from: //branches/cupcake/...
  Original author: android-build
  Merged from: //branches/donutburger/...

Automated import of CL 143828
This commit is contained in:
Raphael Moll
2009-03-31 14:48:14 -07:00
committed by The Android Open Source Project
parent 94967f1cd6
commit 42ab43b51a
2 changed files with 181 additions and 45 deletions

View File

@@ -459,11 +459,30 @@ class Main {
mSdkLog.printf(" Sdcard: %s\n", sdcard);
}
}
// Are there some unused AVDs?
List<AvdInfo> badAvds = avdManager.getUnavailableAvdList();
if (badAvds == null || badAvds.size() == 0) {
return;
}
mSdkLog.printf("\nThe following Android Virtual Devices are no longer available:\n");
boolean needSeparator = false;
for (AvdInfo info : badAvds) {
if (needSeparator) {
mSdkLog.printf("---------\n");
}
mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName());
mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath());
mSdkLog.printf(" Error: %s\n", info.getError() == null ? "--" : info.getError());
needSeparator = true;
}
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
}
/**
* Creates a new AVD. This is a text based creation with command line prompt.
*/

View File

@@ -32,8 +32,11 @@ import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -118,15 +121,43 @@ public final class AvdManager {
private final String mPath;
private final IAndroidTarget mTarget;
private final Map<String, String> mProperties;
/** Creates a new AVD info. Values are immutable.
* @param properties */
private final String mError;
/**
* Creates a new valid AVD info. Values are immutable.
* <p/>
* Such an AVD is available and can be used.
* The error string is set to null.
*
* @param name The name of the AVD (for display or reference)
* @param path The path to the config.ini file
* @param target The target. Cannot be null.
* @param properties The property map. Cannot be null.
*/
public AvdInfo(String name, String path, IAndroidTarget target,
Map<String, String> properties) {
this(name, path, target, properties, null /*error*/);
}
/**
* Creates a new <em>invalid</em> AVD info. Values are immutable.
* <p/>
* Such an AVD is not complete and cannot be used.
* The error string must be non-null.
*
* @param name The name of the AVD (for display or reference)
* @param path The path to the config.ini file
* @param target The target. Can be null.
* @param properties The property map. Can be null.
* @param error The error describing why this AVD is invalid. Cannot be null.
*/
public AvdInfo(String name, String path, IAndroidTarget target,
Map<String, String> properties, String error) {
mName = name;
mPath = path;
mTarget = target;
mProperties = properties;
mError = error;
}
/** Returns the name of the AVD. */
@@ -144,6 +175,11 @@ public final class AvdManager {
return mTarget;
}
/** Returns the error describing why an AVD failed to load. Always null for valid AVDs. */
public String getError() {
return mError;
}
/**
* Helper method that returns the .ini {@link File} for a given AVD name.
* @throws AndroidLocationException if there's a problem getting android root directory.
@@ -634,29 +670,27 @@ public final class AvdManager {
}
}
private void buildAvdList(ArrayList<AvdInfo> list) throws AndroidLocationException {
/**
* Returns a list of files that are potential AVD ini files.
* <p/>
* This lists the $HOME/.android/avd/<name>.ini files.
* Such files are properties file than then indicate where the AVD folder is located.
*
* @return A new {@link File} array or null. The array might be empty.
* @throws AndroidLocationException if there's a problem getting android root directory.
*/
private File[] buildAvdFilesList() throws AndroidLocationException {
// get the Android prefs location.
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
final boolean avdListDebug = System.getenv("AVD_LIST_DEBUG") != null;
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] AVD root: '%s'\n", avdRoot);
}
// ensure folder validity.
File folder = new File(avdRoot);
if (folder.isFile()) {
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] AVD root is a file.\n");
}
throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] AVD root folder doesn't exist, creating.\n");
}
// folder is not there, we create it and return
folder.mkdirs();
return;
return null;
}
File[] avds = folder.listFiles(new FilenameFilter() {
@@ -664,10 +698,6 @@ public final class AvdManager {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
boolean isFile = new File(parent, name).isFile();
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] Item '%s': %s\n",
name, isFile ? "accepted file" : "rejected");
}
return isFile;
}
@@ -675,52 +705,130 @@ public final class AvdManager {
}
});
return avds;
}
/**
* Computes the internal list of available AVDs.
* This only contains AVDs that reference the target currently available.
*
* @param list An array list that will contain the list of AVDs.
* @throws AndroidLocationException if there's a problem getting android root directory.
*/
private void buildAvdList(ArrayList<AvdInfo> list) throws AndroidLocationException {
File[] avds = buildAvdFilesList();
for (File avd : avds) {
AvdInfo info = parseAvdInfo(avd);
AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
if (info != null) {
list.add(info);
if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] Added AVD '%s'\n", info.getPath());
}
} else if (avdListDebug) {
mSdkLog.printf("[AVD LIST DEBUG] Failed to parse AVD '%s'\n", avd.getPath());
}
}
}
private AvdInfo parseAvdInfo(File path) {
Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
public List<AvdInfo> getUnavailableAvdList() throws AndroidLocationException {
AvdInfo[] avds = getAvds();
File[] allAvds = buildAvdFilesList();
if (allAvds == null || allAvds.length == 0) {
return null;
}
TreeSet<File> list = new TreeSet<File>(Arrays.asList(allAvds));
for (AvdInfo info : avds) {
if (list.remove(info.getIniFile())) {
if (list.size() == 0) {
return null;
}
}
}
ArrayList<AvdInfo> errorAvds = new ArrayList<AvdInfo>(list.size());
for (File file : list) {
errorAvds.add(parseAvdInfo(file, true /*acceptError*/));
}
return errorAvds;
}
/**
* Parses an AVD config.ini to create an {@link AvdInfo}.
*
* @param path The path to the AVD config.ini
* @param acceptError When false, an AVD that fails to load will be discarded and null will be
* returned. When true, such an AVD will be returned with an error description.
* @return A new {@link AvdInfo} or null if the file is not valid or null if the AVD is not
* valid and acceptError is false.
*/
private AvdInfo parseAvdInfo(File path, boolean acceptError) {
String error = null;
Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
String avdPath = map.get(AVD_INFO_PATH);
if (avdPath == null) {
return null;
}
String targetHash = map.get(AVD_INFO_TARGET);
if (targetHash == null) {
return null;
IAndroidTarget target = null;
File configIniFile = null;
Map<String, String> properties = null;
if (targetHash != null) {
target = mSdk.getTargetFromHashString(targetHash);
}
IAndroidTarget target = mSdk.getTargetFromHashString(targetHash);
if (target == null) {
return null;
// load the avd properties.
if (avdPath != null) {
configIniFile = new File(avdPath, CONFIG_INI);
}
// load the avd properties.
File configIniFile = new File(avdPath, CONFIG_INI);
Map<String, String> properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
if (configIniFile != null) {
properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
}
// get name
String name = path.getName();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
if (matcher.matches()) {
name = matcher.group(1);
}
if (!acceptError) {
if (avdPath == null ||
targetHash == null ||
target == null ||
configIniFile == null ||
properties == null) {
return null;
}
} else {
if (avdPath == null || configIniFile == null) {
error = String.format("Missing AVD 'path' property in %1$s", name);
} else if (targetHash == null) {
error = String.format("Missing 'target' property in %1$s", name);
} else if (target == null) {
error = String.format("Unknown 'target=%2$s' property in %1$s", name, targetHash);
} else if (properties == null) {
error = String.format("Failed to parse properties from %1$s", avdPath);
}
}
AvdInfo info = new AvdInfo(
matcher.matches() ? matcher.group(1) : path.getName(), // should not happen
name,
avdPath,
target,
properties);
properties,
error);
return info;
}
/**
* Writes a new AVD config.ini file from a set of properties.
*
* @param iniFile The file to generate.
* @param values THe properties to place in the ini file.
* @throws IOException if {@link FileWriter} fails to open, write or close the file.
*/
private static void createConfigIni(File iniFile, Map<String, String> values)
throws IOException {
FileWriter writer = new FileWriter(iniFile);
@@ -732,6 +840,15 @@ public final class AvdManager {
}
/**
* Invokes the tool to create a new SD card image file.
*
* @param toolLocation The path to the mksdcard tool.
* @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}.
* @param location The path of the new sdcard image file to generate.
* @param log The logger object, to report errors.
* @return True if the sdcard could be created.
*/
private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
try {
String[] command = new String[3];