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,6 +459,25 @@ class Main {
mSdkLog.printf(" Sdcard: %s\n", sdcard); 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) { } catch (AndroidLocationException e) {
errorAndExit(e.getMessage()); errorAndExit(e.getMessage());
} }

View File

@@ -32,8 +32,11 @@ import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeSet;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -118,15 +121,43 @@ public final class AvdManager {
private final String mPath; private final String mPath;
private final IAndroidTarget mTarget; private final IAndroidTarget mTarget;
private final Map<String, String> mProperties; private final Map<String, String> mProperties;
private final String mError;
/** Creates a new AVD info. Values are immutable. /**
* @param properties */ * 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, public AvdInfo(String name, String path, IAndroidTarget target,
Map<String, String> properties) { 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; mName = name;
mPath = path; mPath = path;
mTarget = target; mTarget = target;
mProperties = properties; mProperties = properties;
mError = error;
} }
/** Returns the name of the AVD. */ /** Returns the name of the AVD. */
@@ -144,6 +175,11 @@ public final class AvdManager {
return mTarget; 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. * Helper method that returns the .ini {@link File} for a given AVD name.
* @throws AndroidLocationException if there's a problem getting android root directory. * @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. // get the Android prefs location.
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; 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. // ensure folder validity.
File folder = new File(avdRoot); File folder = new File(avdRoot);
if (folder.isFile()) { 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)); throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) { } 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 is not there, we create it and return
folder.mkdirs(); folder.mkdirs();
return; return null;
} }
File[] avds = folder.listFiles(new FilenameFilter() { File[] avds = folder.listFiles(new FilenameFilter() {
@@ -664,10 +698,6 @@ public final class AvdManager {
if (INI_NAME_PATTERN.matcher(name).matches()) { if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder // check it's a file and not a folder
boolean isFile = new File(parent, name).isFile(); 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; 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) { for (File avd : avds) {
AvdInfo info = parseAvdInfo(avd); AvdInfo info = parseAvdInfo(avd, false /*acceptError*/);
if (info != null) { if (info != null) {
list.add(info); 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) { 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); Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
String avdPath = map.get(AVD_INFO_PATH); String avdPath = map.get(AVD_INFO_PATH);
if (avdPath == null) {
return null;
}
String targetHash = map.get(AVD_INFO_TARGET); String targetHash = map.get(AVD_INFO_TARGET);
if (targetHash == null) {
return null;
}
IAndroidTarget target = mSdk.getTargetFromHashString(targetHash); IAndroidTarget target = null;
if (target == null) { File configIniFile = null;
return null; Map<String, String> properties = null;
if (targetHash != null) {
target = mSdk.getTargetFromHashString(targetHash);
} }
// load the avd properties. // load the avd properties.
File configIniFile = new File(avdPath, CONFIG_INI); if (avdPath != null) {
Map<String, String> properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog); configIniFile = new File(avdPath, CONFIG_INI);
}
if (configIniFile != null) {
properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
}
// get name
String name = path.getName();
Matcher matcher = INI_NAME_PATTERN.matcher(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( AvdInfo info = new AvdInfo(
matcher.matches() ? matcher.group(1) : path.getName(), // should not happen name,
avdPath, avdPath,
target, target,
properties); properties,
error);
return info; 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) private static void createConfigIni(File iniFile, Map<String, String> values)
throws IOException { throws IOException {
FileWriter writer = new FileWriter(iniFile); 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) { private boolean createSdCard(String toolLocation, String size, String location, ISdkLog log) {
try { try {
String[] command = new String[3]; String[] command = new String[3];