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:
committed by
The Android Open Source Project
parent
94967f1cd6
commit
42ab43b51a
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
Reference in New Issue
Block a user