Force UTF-8 encoding for SDK files.

- project .properties files
- AVD ini files (<name>.ini and config.ini)
- add-on manifest
This commit is contained in:
Xavier Ducrohet
2009-06-17 17:51:22 -07:00
parent 340a79b590
commit fa0286d4d3
4 changed files with 151 additions and 140 deletions

View File

@@ -41,6 +41,11 @@ public final class SdkConstants {
*/ */
public final static int CURRENT_PLATFORM = currentPlatform(); public final static int CURRENT_PLATFORM = currentPlatform();
/**
* Charset for the ini file handled by the SDK.
*/
public final static String INI_CHARSET = "UTF-8";
/** An SDK Project's AndroidManifest.xml file */ /** An SDK Project's AndroidManifest.xml file */
public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml"; public static final String FN_ANDROID_MANIFEST_XML= "AndroidManifest.xml";
/** An SDK Project's build.xml file */ /** An SDK Project's build.xml file */

View File

@@ -606,7 +606,7 @@ public final class SdkManager {
/** /**
* Parses a property file and returns * Parses a property file (using UTF-8 encoding) and returns
* @param buildProp the property file to parse * @param buildProp the property file to parse
* @param log the ISdkLog object receiving warning/error from the parsing. * @param log the ISdkLog object receiving warning/error from the parsing.
* @return the map of (key,value) pairs, or null if the parsing failed. * @return the map of (key,value) pairs, or null if the parsing failed.
@@ -616,7 +616,7 @@ public final class SdkManager {
BufferedReader reader = null; BufferedReader reader = null;
try { try {
fis = new FileInputStream(buildProp); fis = new FileInputStream(buildProp);
reader = new BufferedReader(new InputStreamReader(fis)); reader = new BufferedReader(new InputStreamReader(fis, SdkConstants.INI_CHARSET));
String line = null; String line = null;
Map<String, String> map = new HashMap<String, String>(); Map<String, String> map = new HashMap<String, String>();

View File

@@ -32,6 +32,7 @@ import java.io.FileWriter;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -44,7 +45,7 @@ import java.util.regex.Pattern;
* Android Virtual Device Manager to manage AVDs. * Android Virtual Device Manager to manage AVDs.
*/ */
public final class AvdManager { public final class AvdManager {
/** /**
* Exception thrown when something is wrong with a target path. * Exception thrown when something is wrong with a target path.
*/ */
@@ -55,16 +56,16 @@ public final class AvdManager {
super(message); super(message);
} }
} }
public static final String AVD_FOLDER_EXTENSION = ".avd"; public static final String AVD_FOLDER_EXTENSION = ".avd";
public final static String AVD_INFO_PATH = "path"; public final static String AVD_INFO_PATH = "path";
public final static String AVD_INFO_TARGET = "target"; public final static String AVD_INFO_TARGET = "target";
/** /**
* AVD/config.ini key name representing the SDK-relative path of the skin folder, if any, * AVD/config.ini key name representing the SDK-relative path of the skin folder, if any,
* or a 320x480 like constant for a numeric skin size. * or a 320x480 like constant for a numeric skin size.
* *
* @see #NUMERIC_SKIN_SIZE * @see #NUMERIC_SKIN_SIZE
*/ */
public final static String AVD_INI_SKIN_PATH = "skin.path"; public final static String AVD_INI_SKIN_PATH = "skin.path";
@@ -79,14 +80,14 @@ public final class AvdManager {
* AVD/config.ini key name representing the path to the sdcard file. * AVD/config.ini key name representing the path to the sdcard file.
* If missing, the default name "sdcard.img" will be used for the sdcard, if there's such * If missing, the default name "sdcard.img" will be used for the sdcard, if there's such
* a file. * a file.
* *
* @see #SDCARD_IMG * @see #SDCARD_IMG
*/ */
public final static String AVD_INI_SDCARD_PATH = "sdcard.path"; public final static String AVD_INI_SDCARD_PATH = "sdcard.path";
/** /**
* AVD/config.ini key name representing the size of the SD card. * AVD/config.ini key name representing the size of the SD card.
* This property is for UI purposes only. It is not used by the emulator. * This property is for UI purposes only. It is not used by the emulator.
* *
* @see #SDCARD_SIZE_PATTERN * @see #SDCARD_SIZE_PATTERN
*/ */
public final static String AVD_INI_SDCARD_SIZE = "sdcard.size"; public final static String AVD_INI_SDCARD_SIZE = "sdcard.size";
@@ -101,7 +102,7 @@ public final class AvdManager {
/** /**
* AVD/config.ini key name representing the second path where the emulator looks * AVD/config.ini key name representing the second path where the emulator looks
* for system images. Typically this is the path to the platform system image. * for system images. Typically this is the path to the platform system image.
* *
* @see #AVD_INI_IMAGES_1 * @see #AVD_INI_IMAGES_1
*/ */
public final static String AVD_INI_IMAGES_2 = "image.sysdir.2"; public final static String AVD_INI_IMAGES_2 = "image.sysdir.2";
@@ -135,7 +136,7 @@ public final class AvdManager {
/** An immutable structure describing an Android Virtual Device. */ /** An immutable structure describing an Android Virtual Device. */
public static final class AvdInfo { public static final class AvdInfo {
/** /**
* Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid. * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid.
*/ */
@@ -155,7 +156,7 @@ public final class AvdManager {
/** System Image folder in config.ini doesn't exist */ /** System Image folder in config.ini doesn't exist */
ERROR_IMAGE_DIR; ERROR_IMAGE_DIR;
} }
private final String mName; private final String mName;
private final String mPath; private final String mPath;
private final String mTargetHash; private final String mTargetHash;
@@ -164,11 +165,11 @@ public final class AvdManager {
private final AvdStatus mStatus; private final AvdStatus mStatus;
/** /**
* Creates a new valid AVD info. Values are immutable. * Creates a new valid AVD info. Values are immutable.
* <p/> * <p/>
* Such an AVD is available and can be used. * Such an AVD is available and can be used.
* The error string is set to null. * The error string is set to null.
* *
* @param name The name of the AVD (for display or reference) * @param name The name of the AVD (for display or reference)
* @param path The path to the config.ini file * @param path The path to the config.ini file
* @param targetHash the target hash * @param targetHash the target hash
@@ -181,11 +182,11 @@ public final class AvdManager {
} }
/** /**
* Creates a new <em>invalid</em> AVD info. Values are immutable. * Creates a new <em>invalid</em> AVD info. Values are immutable.
* <p/> * <p/>
* Such an AVD is not complete and cannot be used. * Such an AVD is not complete and cannot be used.
* The error string must be non-null. * The error string must be non-null.
* *
* @param name The name of the AVD (for display or reference) * @param name The name of the AVD (for display or reference)
* @param path The path to the config.ini file * @param path The path to the config.ini file
* @param targetHash the target hash * @param targetHash the target hash
@@ -212,7 +213,7 @@ public final class AvdManager {
public String getPath() { public String getPath() {
return mPath; return mPath;
} }
/** /**
* Returns the target hash string. * Returns the target hash string.
*/ */
@@ -230,8 +231,8 @@ public final class AvdManager {
return mStatus; return mStatus;
} }
/** /**
* 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.
*/ */
public static File getIniFile(String name) throws AndroidLocationException { public static File getIniFile(String name) throws AndroidLocationException {
@@ -240,23 +241,23 @@ public final class AvdManager {
return new File(avdRoot, name + INI_EXTENSION); return new File(avdRoot, name + INI_EXTENSION);
} }
/** /**
* Returns the .ini {@link File} for this AVD. * Returns the .ini {@link File} for this AVD.
* @throws AndroidLocationException if there's a problem getting android root directory. * @throws AndroidLocationException if there's a problem getting android root directory.
*/ */
public File getIniFile() throws AndroidLocationException { public File getIniFile() throws AndroidLocationException {
return getIniFile(mName); return getIniFile(mName);
} }
/** /**
* Helper method that returns the Config {@link File} for a given AVD name. * Helper method that returns the Config {@link File} for a given AVD name.
*/ */
public static File getConfigFile(String path) { public static File getConfigFile(String path) {
return new File(path, CONFIG_INI); return new File(path, CONFIG_INI);
} }
/** /**
* Returns the Config {@link File} for this AVD. * Returns the Config {@link File} for this AVD.
*/ */
public File getConfigFile() { public File getConfigFile() {
return getConfigFile(mPath); return getConfigFile(mPath);
@@ -268,7 +269,7 @@ public final class AvdManager {
public Map<String, String> getProperties() { public Map<String, String> getProperties() {
return mProperties; return mProperties;
} }
/** /**
* Returns the error message for the AVD or <code>null</code> if {@link #getStatus()} * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()}
* returns {@link AvdStatus#OK} * returns {@link AvdStatus#OK}
@@ -299,7 +300,7 @@ public final class AvdManager {
} catch (AndroidLocationException e) { } catch (AndroidLocationException e) {
return "Unable to get HOME folder."; return "Unable to get HOME folder.";
} }
return null; return null;
} }
} }
@@ -309,14 +310,14 @@ public final class AvdManager {
private AvdInfo[] mBrokenAvdList; private AvdInfo[] mBrokenAvdList;
private final SdkManager mSdk; private final SdkManager mSdk;
/** /**
* TODO remove this field. Each caller should give its own logger to the methods * TODO remove this field. Each caller should give its own logger to the methods
* here instead of relying on a global logger. Otherwise that means that each * here instead of relying on a global logger. Otherwise that means that each
* caller needs to re-instantiate this just to change the logger, which is plain * caller needs to re-instantiate this just to change the logger, which is plain
* ugly. * ugly.
* *
* We'll revisit this in the final AVD Manager UI for donut. * We'll revisit this in the final AVD Manager UI for donut.
*/ */
private ISdkLog mSdkLog; private ISdkLog mSdkLog;
public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException { public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
@@ -327,9 +328,9 @@ public final class AvdManager {
/** /**
* Changes the current {@link ISdkLog}. * Changes the current {@link ISdkLog}.
* *
* This method should not exist. See comments for {@link #mSdkLog}. * This method should not exist. See comments for {@link #mSdkLog}.
* *
* @return The {@link ISdkLog} that was currently being used. * @return The {@link ISdkLog} that was currently being used.
* @see #mSdkLog * @see #mSdkLog
*/ */
@@ -362,7 +363,7 @@ public final class AvdManager {
list.add(avd); list.add(avd);
} }
} }
mValidAvdList = list.toArray(new AvdInfo[list.size()]); mValidAvdList = list.toArray(new AvdInfo[list.size()]);
} }
return mValidAvdList; return mValidAvdList;
@@ -413,7 +414,7 @@ public final class AvdManager {
return null; return null;
} }
/** /**
* Reloads the AVD list. * Reloads the AVD list.
* @throws AndroidLocationException if there was an error finding the location of the * @throws AndroidLocationException if there was an error finding the location of the
@@ -434,7 +435,7 @@ public final class AvdManager {
/** /**
* Creates a new AVD. It is expected that there is no existing AVD with this name already. * Creates a new AVD. It is expected that there is no existing AVD with this name already.
* *
* @param avdFolder the data folder for the AVD. It will be created as needed. * @param avdFolder the data folder for the AVD. It will be created as needed.
* @param name the name of the AVD * @param name the name of the AVD
* @param target the target of the AVD * @param target the target of the AVD
@@ -444,12 +445,12 @@ public final class AvdManager {
* @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults. * @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults.
* @param removePrevious If true remove any previous files. * @param removePrevious If true remove any previous files.
* @return The new {@link AvdInfo} in case of success (which has just been added to the * @return The new {@link AvdInfo} in case of success (which has just been added to the
* internal list) or null in case of failure. * internal list) or null in case of failure.
*/ */
public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target, public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
String skinName, String sdcard, Map<String,String> hardwareConfig, String skinName, String sdcard, Map<String,String> hardwareConfig,
boolean removePrevious) { boolean removePrevious) {
File iniFile = null; File iniFile = null;
boolean needCleanup = false; boolean needCleanup = false;
try { try {
@@ -478,33 +479,33 @@ public final class AvdManager {
// writes the userdata.img in it. // writes the userdata.img in it.
String imagePath = target.getPath(IAndroidTarget.IMAGES); String imagePath = target.getPath(IAndroidTarget.IMAGES);
File userdataSrc = new File(imagePath, USERDATA_IMG); File userdataSrc = new File(imagePath, USERDATA_IMG);
if (userdataSrc.exists() == false && target.isPlatform() == false) { if (userdataSrc.exists() == false && target.isPlatform() == false) {
imagePath = target.getParent().getPath(IAndroidTarget.IMAGES); imagePath = target.getParent().getPath(IAndroidTarget.IMAGES);
userdataSrc = new File(imagePath, USERDATA_IMG); userdataSrc = new File(imagePath, USERDATA_IMG);
} }
if (userdataSrc.exists() == false) { if (userdataSrc.exists() == false) {
mSdkLog.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.", mSdkLog.error(null, "Unable to find a '%1$s' file to copy into the AVD folder.",
USERDATA_IMG); USERDATA_IMG);
needCleanup = true; needCleanup = true;
return null; return null;
} }
FileInputStream fis = new FileInputStream(userdataSrc); FileInputStream fis = new FileInputStream(userdataSrc);
File userdataDest = new File(avdFolder, USERDATA_IMG); File userdataDest = new File(avdFolder, USERDATA_IMG);
FileOutputStream fos = new FileOutputStream(userdataDest); FileOutputStream fos = new FileOutputStream(userdataDest);
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int count; int count;
while ((count = fis.read(buffer)) != -1) { while ((count = fis.read(buffer)) != -1) {
fos.write(buffer, 0, count); fos.write(buffer, 0, count);
} }
fos.close(); fos.close();
fis.close(); fis.close();
// Config file. // Config file.
HashMap<String, String> values = new HashMap<String, String>(); HashMap<String, String> values = new HashMap<String, String>();
@@ -512,7 +513,7 @@ public final class AvdManager {
needCleanup = true; needCleanup = true;
return null; return null;
} }
// Now the skin. // Now the skin.
if (skinName == null || skinName.length() == 0) { if (skinName == null || skinName.length() == 0) {
skinName = target.getDefaultSkin(); skinName = target.getDefaultSkin();
@@ -545,31 +546,31 @@ public final class AvdManager {
} else { } else {
// Sdcard is possibly a size. In that case we create a file called 'sdcard.img' // Sdcard is possibly a size. In that case we create a file called 'sdcard.img'
// in the AVD folder, and do not put any value in config.ini. // in the AVD folder, and do not put any value in config.ini.
// First, check that it matches the pattern for sdcard size // First, check that it matches the pattern for sdcard size
Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard); Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
if (m.matches()) { if (m.matches()) {
// create the sdcard. // create the sdcard.
sdcardFile = new File(avdFolder, SDCARD_IMG); sdcardFile = new File(avdFolder, SDCARD_IMG);
String path = sdcardFile.getAbsolutePath(); String path = sdcardFile.getAbsolutePath();
// execute mksdcard with the proper parameters. // execute mksdcard with the proper parameters.
File toolsFolder = new File(mSdk.getLocation(), SdkConstants.FD_TOOLS); File toolsFolder = new File(mSdk.getLocation(), SdkConstants.FD_TOOLS);
File mkSdCard = new File(toolsFolder, SdkConstants.mkSdCardCmdName()); File mkSdCard = new File(toolsFolder, SdkConstants.mkSdCardCmdName());
if (mkSdCard.isFile() == false) { if (mkSdCard.isFile() == false) {
mSdkLog.error(null, "'%1$s' is missing from the SDK tools folder.", mSdkLog.error(null, "'%1$s' is missing from the SDK tools folder.",
mkSdCard.getName()); mkSdCard.getName());
needCleanup = true; needCleanup = true;
return null; return null;
} }
if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path) == false) { if (createSdCard(mkSdCard.getAbsolutePath(), sdcard, path) == false) {
needCleanup = true; needCleanup = true;
return null; // mksdcard output has already been displayed, no need to return null; // mksdcard output has already been displayed, no need to
// output anything else. // output anything else.
} }
// add a property containing the size of the sdcard for display purpose // add a property containing the size of the sdcard for display purpose
// only when the dev does 'android list avd' // only when the dev does 'android list avd'
values.put(AVD_INI_SDCARD_SIZE, sdcard); values.put(AVD_INI_SDCARD_SIZE, sdcard);
@@ -592,7 +593,7 @@ public final class AvdManager {
File configIniFile = new File(avdFolder, CONFIG_INI); File configIniFile = new File(avdFolder, CONFIG_INI);
writeIniFile(configIniFile, values); writeIniFile(configIniFile, values);
if (mSdkLog != null) { if (mSdkLog != null) {
if (target.isPlatform()) { if (target.isPlatform()) {
mSdkLog.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName()); mSdkLog.printf("Created AVD '%1$s' based on %2$s\n", name, target.getName());
@@ -601,7 +602,7 @@ public final class AvdManager {
target.getVendor()); target.getVendor());
} }
} }
// create the AvdInfo object, and add it to the list // create the AvdInfo object, and add it to the list
AvdInfo newAvdInfo = new AvdInfo(name, AvdInfo newAvdInfo = new AvdInfo(name,
avdFolder.getAbsolutePath(), avdFolder.getAbsolutePath(),
@@ -609,7 +610,7 @@ public final class AvdManager {
target, values); target, values);
AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/); AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/);
synchronized (mAllAvdList) { synchronized (mAllAvdList) {
if (oldAvdInfo != null && removePrevious) { if (oldAvdInfo != null && removePrevious) {
mAllAvdList.remove(oldAvdInfo); mAllAvdList.remove(oldAvdInfo);
@@ -619,7 +620,7 @@ public final class AvdManager {
} }
if (removePrevious && if (removePrevious &&
newAvdInfo != null && newAvdInfo != null &&
oldAvdInfo != null && oldAvdInfo != null &&
!oldAvdInfo.getPath().equals(newAvdInfo.getPath())) { !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath()); mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
@@ -643,12 +644,12 @@ public final class AvdManager {
if (iniFile != null && iniFile.exists()) { if (iniFile != null && iniFile.exists()) {
iniFile.delete(); iniFile.delete();
} }
recursiveDelete(avdFolder); recursiveDelete(avdFolder);
avdFolder.delete(); avdFolder.delete();
} }
} }
return null; return null;
} }
@@ -668,7 +669,7 @@ public final class AvdManager {
assert false; assert false;
throw new InvalidTargetPathException("Target location is not inside the SDK."); throw new InvalidTargetPathException("Target location is not inside the SDK.");
} }
File folder = new File(imageFullPath); File folder = new File(imageFullPath);
if (folder.isDirectory()) { if (folder.isDirectory()) {
String[] list = folder.list(new FilenameFilter() { String[] list = folder.list(new FilenameFilter() {
@@ -682,36 +683,36 @@ public final class AvdManager {
if (imageFullPath.charAt(0) == File.separatorChar) { if (imageFullPath.charAt(0) == File.separatorChar) {
imageFullPath = imageFullPath.substring(1); imageFullPath = imageFullPath.substring(1);
} }
return imageFullPath; return imageFullPath;
} }
} }
return null; return null;
} }
/** /**
* Returns the path to the skin, as a relative path to the SDK. * Returns the path to the skin, as a relative path to the SDK.
*/ */
private String getSkinRelativePath(String skinName, IAndroidTarget target) { private String getSkinRelativePath(String skinName, IAndroidTarget target) {
// first look to see if the skin is in the target // first look to see if the skin is in the target
String path = target.getPath(IAndroidTarget.SKINS); String path = target.getPath(IAndroidTarget.SKINS);
File skin = new File(path, skinName); File skin = new File(path, skinName);
if (skin.exists() == false && target.isPlatform() == false) { if (skin.exists() == false && target.isPlatform() == false) {
target = target.getParent(); target = target.getParent();
path = target.getPath(IAndroidTarget.SKINS); path = target.getPath(IAndroidTarget.SKINS);
skin = new File(path, skinName); skin = new File(path, skinName);
} }
// skin really does not exist! // skin really does not exist!
if (skin.exists() == false) { if (skin.exists() == false) {
mSdkLog.error(null, "Skin '%1$s' does not exist.", skinName); mSdkLog.error(null, "Skin '%1$s' does not exist.", skinName);
return null; return null;
} }
// get the skin path // get the skin path
path = skin.getAbsolutePath(); path = skin.getAbsolutePath();
@@ -733,7 +734,7 @@ public final class AvdManager {
/** /**
* Creates the ini file for an AVD. * Creates the ini file for an AVD.
* *
* @param name of the AVD. * @param name of the AVD.
* @param avdFolder path for the data folder of the AVD. * @param avdFolder path for the data folder of the AVD.
* @param target of the AVD. * @param target of the AVD.
@@ -750,10 +751,10 @@ public final class AvdManager {
return iniFile; return iniFile;
} }
/** /**
* Creates the ini file for an AVD. * Creates the ini file for an AVD.
* *
* @param info of the AVD. * @param info of the AVD.
* @throws AndroidLocationException if there's a problem getting android root directory. * @throws AndroidLocationException if there's a problem getting android root directory.
* @throws IOException if {@link File#getAbsolutePath()} fails. * @throws IOException if {@link File#getAbsolutePath()} fails.
@@ -772,14 +773,14 @@ public final class AvdManager {
* could not be loaded due to some error. That means this method still tries to remove * could not be loaded due to some error. That means this method still tries to remove
* the AVD ini file or its folder if it can be found. An error will be output if any of * the AVD ini file or its folder if it can be found. An error will be output if any of
* these operations fail. * these operations fail.
* *
* @param avdInfo the information on the AVD to delete * @param avdInfo the information on the AVD to delete
* @return True if the AVD was deleted with no error. * @return True if the AVD was deleted with no error.
*/ */
public boolean deleteAvd(AvdInfo avdInfo, ISdkLog log) { public boolean deleteAvd(AvdInfo avdInfo, ISdkLog log) {
try { try {
boolean error = false; boolean error = false;
File f = avdInfo.getIniFile(); File f = avdInfo.getIniFile();
if (f != null && f.exists()) { if (f != null && f.exists()) {
log.warning("Deleting file %1$s", f.getCanonicalPath()); log.warning("Deleting file %1$s", f.getCanonicalPath());
@@ -819,22 +820,22 @@ public final class AvdManager {
} }
return false; return false;
} }
/** /**
* Moves and/or rename an existing AVD and its files. * Moves and/or rename an existing AVD and its files.
* This also change it in the manager's list. * This also change it in the manager's list.
* <p/> * <p/>
* The caller should make sure the name or path given are valid, do not exist and are * The caller should make sure the name or path given are valid, do not exist and are
* actually different than current values. * actually different than current values.
* *
* @param avdInfo the information on the AVD to move. * @param avdInfo the information on the AVD to move.
* @param newName the new name of the AVD if non null. * @param newName the new name of the AVD if non null.
* @param paramFolderPath the new data folder if non null. * @param paramFolderPath the new data folder if non null.
* @return True if the move succeeded or there was nothing to do. * @return True if the move succeeded or there was nothing to do.
* If false, this method will have had already output error in the log. * If false, this method will have had already output error in the log.
*/ */
public boolean moveAvd(AvdInfo avdInfo, String newName, String paramFolderPath, ISdkLog log) { public boolean moveAvd(AvdInfo avdInfo, String newName, String paramFolderPath, ISdkLog log) {
try { try {
if (paramFolderPath != null) { if (paramFolderPath != null) {
File f = new File(avdInfo.getPath()); File f = new File(avdInfo.getPath());
@@ -844,7 +845,7 @@ public final class AvdManager {
avdInfo.getPath(), paramFolderPath); avdInfo.getPath(), paramFolderPath);
return false; return false;
} }
// update AVD info // update AVD info
AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath,
avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties()); avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getProperties());
@@ -857,10 +858,10 @@ public final class AvdManager {
if (newName != null) { if (newName != null) {
File oldIniFile = avdInfo.getIniFile(); File oldIniFile = avdInfo.getIniFile();
File newIniFile = AvdInfo.getIniFile(newName); File newIniFile = AvdInfo.getIniFile(newName);
log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath()); log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath());
if (!oldIniFile.renameTo(newIniFile)) { if (!oldIniFile.renameTo(newIniFile)) {
log.error(null, "Failed to move '%1$s' to '%2$s'.", log.error(null, "Failed to move '%1$s' to '%2$s'.",
oldIniFile.getPath(), newIniFile.getPath()); oldIniFile.getPath(), newIniFile.getPath());
return false; return false;
} }
@@ -885,7 +886,7 @@ public final class AvdManager {
/** /**
* Helper method to recursively delete a folder's content (but not the folder itself). * Helper method to recursively delete a folder's content (but not the folder itself).
* *
* @throws SecurityException like {@link File#delete()} does if file/folder is not writable. * @throws SecurityException like {@link File#delete()} does if file/folder is not writable.
*/ */
public void recursiveDelete(File folder) { public void recursiveDelete(File folder) {
@@ -902,7 +903,7 @@ public final class AvdManager {
* <p/> * <p/>
* This lists the $HOME/.android/avd/<name>.ini files. * This lists the $HOME/.android/avd/<name>.ini files.
* Such files are properties file than then indicate where the AVD folder is located. * 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. * @return A new {@link File} array or null. The array might be empty.
* @throws AndroidLocationException if there's a problem getting android root directory. * @throws AndroidLocationException if there's a problem getting android root directory.
*/ */
@@ -920,7 +921,7 @@ public final class AvdManager {
folder.mkdirs(); folder.mkdirs();
return null; return null;
} }
File[] avds = folder.listFiles(new FilenameFilter() { File[] avds = folder.listFiles(new FilenameFilter() {
public boolean accept(File parent, String name) { public boolean accept(File parent, String name) {
if (INI_NAME_PATTERN.matcher(name).matches()) { if (INI_NAME_PATTERN.matcher(name).matches()) {
@@ -932,14 +933,14 @@ public final class AvdManager {
return false; return false;
} }
}); });
return avds; return avds;
} }
/** /**
* Computes the internal list of available AVDs * Computes the internal list of available AVDs
* @param allList the list to contain all the AVDs * @param allList the list to contain all the AVDs
* *
* @throws AndroidLocationException if there's a problem getting android root directory. * @throws AndroidLocationException if there's a problem getting android root directory.
*/ */
private void buildAvdList(ArrayList<AvdInfo> allList) throws AndroidLocationException { private void buildAvdList(ArrayList<AvdInfo> allList) throws AndroidLocationException {
@@ -956,7 +957,7 @@ public final class AvdManager {
/** /**
* Parses an AVD .ini file to create an {@link AvdInfo}. * Parses an AVD .ini file to create an {@link AvdInfo}.
* *
* @param path The path to the AVD .ini file * @param path The path to the AVD .ini file
* @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is * @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is
* valid or not. * valid or not.
@@ -970,7 +971,7 @@ public final class AvdManager {
IAndroidTarget target = null; IAndroidTarget target = null;
File configIniFile = null; File configIniFile = null;
Map<String, String> properties = null; Map<String, String> properties = null;
if (targetHash != null) { if (targetHash != null) {
target = mSdk.getTargetFromHashString(targetHash); target = mSdk.getTargetFromHashString(targetHash);
} }
@@ -979,7 +980,7 @@ public final class AvdManager {
if (avdPath != null) { if (avdPath != null) {
configIniFile = new File(avdPath, CONFIG_INI); configIniFile = new File(avdPath, CONFIG_INI);
} }
if (configIniFile != null) { if (configIniFile != null) {
properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog); properties = SdkManager.parsePropertyFile(configIniFile, mSdkLog);
} }
@@ -990,7 +991,7 @@ public final class AvdManager {
if (matcher.matches()) { if (matcher.matches()) {
name = matcher.group(1); name = matcher.group(1);
} }
// check the image.sysdir are valid // check the image.sysdir are valid
boolean validImageSysdir = true; boolean validImageSysdir = true;
if (properties != null) { if (properties != null) {
@@ -1012,7 +1013,7 @@ public final class AvdManager {
} }
AvdStatus status; AvdStatus status;
if (avdPath == null) { if (avdPath == null) {
status = AvdStatus.ERROR_PATH; status = AvdStatus.ERROR_PATH;
} else if (configIniFile == null) { } else if (configIniFile == null) {
@@ -1028,7 +1029,7 @@ public final class AvdManager {
} else { } else {
status = AvdStatus.OK; status = AvdStatus.OK;
} }
AvdInfo info = new AvdInfo( AvdInfo info = new AvdInfo(
name, name,
avdPath, avdPath,
@@ -1036,30 +1037,31 @@ public final class AvdManager {
target, target,
properties, properties,
status); status);
return info; return info;
} }
/** /**
* Writes a .ini file from a set of properties. * Writes a .ini file from a set of properties, using UTF-8 encoding.
* *
* @param iniFile The file to generate. * @param iniFile The file to generate.
* @param values THe properties to place in the ini file. * @param values THe properties to place in the ini file.
* @throws IOException if {@link FileWriter} fails to open, write or close the file. * @throws IOException if {@link FileWriter} fails to open, write or close the file.
*/ */
private static void writeIniFile(File iniFile, Map<String, String> values) private static void writeIniFile(File iniFile, Map<String, String> values)
throws IOException { throws IOException {
FileWriter writer = new FileWriter(iniFile); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(iniFile),
SdkConstants.INI_CHARSET);
for (Entry<String, String> entry : values.entrySet()) { for (Entry<String, String> entry : values.entrySet()) {
writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue())); writer.write(String.format("%1$s=%2$s\n", entry.getKey(), entry.getValue()));
} }
writer.close(); writer.close();
} }
/** /**
* Invokes the tool to create a new SD card image file. * Invokes the tool to create a new SD card image file.
* *
* @param toolLocation The path to the mksdcard tool. * @param toolLocation The path to the mksdcard tool.
* @param size The size of the new SD Card, compatible with {@link #SDCARD_SIZE_PATTERN}. * @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 location The path of the new sdcard image file to generate.
@@ -1072,7 +1074,7 @@ public final class AvdManager {
command[1] = size; command[1] = size;
command[2] = location; command[2] = location;
Process process = Runtime.getRuntime().exec(command); Process process = Runtime.getRuntime().exec(command);
ArrayList<String> errorOutput = new ArrayList<String>(); ArrayList<String> errorOutput = new ArrayList<String>();
ArrayList<String> stdOutput = new ArrayList<String>(); ArrayList<String> stdOutput = new ArrayList<String>();
int status = grabProcessOutput(process, errorOutput, stdOutput, int status = grabProcessOutput(process, errorOutput, stdOutput,
@@ -1091,7 +1093,7 @@ public final class AvdManager {
} catch (IOException e) { } catch (IOException e) {
// pass, print error below // pass, print error below
} }
mSdkLog.error(null, "Failed to create the SD card."); mSdkLog.error(null, "Failed to create the SD card.");
return false; return false;
} }
@@ -1102,7 +1104,7 @@ public final class AvdManager {
* @param process The process to get the ouput from * @param process The process to get the ouput from
* @param errorOutput The array to store the stderr output. cannot be null. * @param errorOutput The array to store the stderr output. cannot be null.
* @param stdOutput The array to store the stdout output. cannot be null. * @param stdOutput The array to store the stdout output. cannot be null.
* @param waitforReaders if true, this will wait for the reader threads. * @param waitforReaders if true, this will wait for the reader threads.
* @return the process return code. * @return the process return code.
* @throws InterruptedException * @throws InterruptedException
*/ */
@@ -1179,7 +1181,7 @@ public final class AvdManager {
/** /**
* Removes an {@link AvdInfo} from the internal list. * Removes an {@link AvdInfo} from the internal list.
* *
* @param avdInfo The {@link AvdInfo} to remove. * @param avdInfo The {@link AvdInfo} to remove.
* @return true if this {@link AvdInfo} was present and has been removed. * @return true if this {@link AvdInfo} was present and has been removed.
*/ */
@@ -1190,15 +1192,15 @@ public final class AvdManager {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Updates an AVD with new path to the system image folders. * Updates an AVD with new path to the system image folders.
* @param name the name of the AVD to update. * @param name the name of the AVD to update.
* @throws IOException * @throws IOException
* @throws AndroidLocationException * @throws AndroidLocationException
*/ */
public void updateAvd(String name) throws IOException, AndroidLocationException { public void updateAvd(String name) throws IOException, AndroidLocationException {
// find the AVD to update. It should be be in the broken list. // find the AVD to update. It should be be in the broken list.
@@ -1211,7 +1213,7 @@ public final class AvdManager {
} }
} }
} }
if (avd == null) { if (avd == null) {
// not in the broken list, just return. // not in the broken list, just return.
mSdkLog.error(null, "There is no Android Virtual Device named '%s'.", name); mSdkLog.error(null, "There is no Android Virtual Device named '%s'.", name);
@@ -1226,21 +1228,21 @@ public final class AvdManager {
if (oldProperties != null) { if (oldProperties != null) {
properties.putAll(oldProperties); properties.putAll(oldProperties);
} }
AvdStatus status; AvdStatus status;
// create the path to the new system images. // create the path to the new system images.
if (setImagePathProperties(avd.getTarget(), properties)) { if (setImagePathProperties(avd.getTarget(), properties)) {
if (properties.containsKey(AVD_INI_IMAGES_1)) { if (properties.containsKey(AVD_INI_IMAGES_1)) {
mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_1, mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_1,
properties.get(AVD_INI_IMAGES_1)); properties.get(AVD_INI_IMAGES_1));
} }
if (properties.containsKey(AVD_INI_IMAGES_2)) { if (properties.containsKey(AVD_INI_IMAGES_2)) {
mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_2, mSdkLog.printf("Updated '%1$s' with value '%2$s'\n", AVD_INI_IMAGES_2,
properties.get(AVD_INI_IMAGES_2)); properties.get(AVD_INI_IMAGES_2));
} }
status = AvdStatus.OK; status = AvdStatus.OK;
} else { } else {
mSdkLog.error(null, "Unable to find non empty system images folders for %1$s", name); mSdkLog.error(null, "Unable to find non empty system images folders for %1$s", name);
@@ -1263,7 +1265,7 @@ public final class AvdManager {
avd.getTarget(), avd.getTarget(),
properties, properties,
status); status);
replaceAvd(avd, newAvd); replaceAvd(avd, newAvd);
} }
@@ -1276,18 +1278,18 @@ public final class AvdManager {
private boolean setImagePathProperties(IAndroidTarget target, Map<String, String> properties) { private boolean setImagePathProperties(IAndroidTarget target, Map<String, String> properties) {
properties.remove(AVD_INI_IMAGES_1); properties.remove(AVD_INI_IMAGES_1);
properties.remove(AVD_INI_IMAGES_2); properties.remove(AVD_INI_IMAGES_2);
try { try {
String property = AVD_INI_IMAGES_1; String property = AVD_INI_IMAGES_1;
// First the image folders of the target itself // First the image folders of the target itself
String imagePath = getImageRelativePath(target); String imagePath = getImageRelativePath(target);
if (imagePath != null) { if (imagePath != null) {
properties.put(property, imagePath); properties.put(property, imagePath);
property = AVD_INI_IMAGES_2; property = AVD_INI_IMAGES_2;
} }
// If the target is an add-on we need to add the Platform image as a backup. // If the target is an add-on we need to add the Platform image as a backup.
IAndroidTarget parent = target.getParent(); IAndroidTarget parent = target.getParent();
if (parent != null) { if (parent != null) {
@@ -1296,16 +1298,16 @@ public final class AvdManager {
properties.put(property, imagePath); properties.put(property, imagePath);
} }
} }
// we need at least one path! // we need at least one path!
return properties.containsKey(AVD_INI_IMAGES_1); return properties.containsKey(AVD_INI_IMAGES_1);
} catch (InvalidTargetPathException e) { } catch (InvalidTargetPathException e) {
mSdkLog.error(e, e.getMessage()); mSdkLog.error(e, e.getMessage());
} }
return false; return false;
} }
/** /**
* Replaces an old {@link AvdInfo} with a new one in the lists storing them. * Replaces an old {@link AvdInfo} with a new one in the lists storing them.
* @param oldAvd the {@link AvdInfo} to remove. * @param oldAvd the {@link AvdInfo} to remove.

View File

@@ -17,11 +17,14 @@
package com.android.sdklib.internal.project; package com.android.sdklib.internal.project;
import com.android.sdklib.IAndroidTarget; import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager; import com.android.sdklib.SdkManager;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -35,12 +38,12 @@ public final class ProjectProperties {
public final static String PROPERTY_TARGET = "target"; public final static String PROPERTY_TARGET = "target";
public final static String PROPERTY_APK_CONFIGS = "apk-configurations"; public final static String PROPERTY_APK_CONFIGS = "apk-configurations";
public final static String PROPERTY_SDK = "sdk-location"; public final static String PROPERTY_SDK = "sdk-location";
public static enum PropertyType { public static enum PropertyType {
BUILD("build.properties", BUILD_HEADER), BUILD("build.properties", BUILD_HEADER),
DEFAULT("default.properties", DEFAULT_HEADER), DEFAULT("default.properties", DEFAULT_HEADER),
LOCAL("local.properties", LOCAL_HEADER); LOCAL("local.properties", LOCAL_HEADER);
private final String mFilename; private final String mFilename;
private final String mHeader; private final String mHeader;
@@ -48,14 +51,14 @@ public final class ProjectProperties {
mFilename = filename; mFilename = filename;
mHeader = header; mHeader = header;
} }
public String getFilename() { public String getFilename() {
return mFilename; return mFilename;
} }
} }
private final static String LOCAL_HEADER = private final static String LOCAL_HEADER =
// 1-------10--------20--------30--------40--------50--------60--------70--------80 // 1-------10--------20--------30--------40--------50--------60--------70--------80
"# This file is automatically generated by Android Tools.\n" + "# This file is automatically generated by Android Tools.\n" +
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" + "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
"# \n" + "# \n" +
@@ -64,7 +67,7 @@ public final class ProjectProperties {
"\n"; "\n";
private final static String DEFAULT_HEADER = private final static String DEFAULT_HEADER =
// 1-------10--------20--------30--------40--------50--------60--------70--------80 // 1-------10--------20--------30--------40--------50--------60--------70--------80
"# This file is automatically generated by Android Tools.\n" + "# This file is automatically generated by Android Tools.\n" +
"# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" + "# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n" +
"# \n" + "# \n" +
@@ -76,7 +79,7 @@ public final class ProjectProperties {
"\n"; "\n";
private final static String BUILD_HEADER = private final static String BUILD_HEADER =
// 1-------10--------20--------30--------40--------50--------60--------70--------80 // 1-------10--------20--------30--------40--------50--------60--------70--------80
"# This file is used to override default values used by the Ant build system.\n" + "# This file is used to override default values used by the Ant build system.\n" +
"# \n" + "# \n" +
"# This file must be checked in Version Control Systems, as it is\n" + "# This file must be checked in Version Control Systems, as it is\n" +
@@ -95,7 +98,7 @@ public final class ProjectProperties {
private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>(); private final static Map<String, String> COMMENT_MAP = new HashMap<String, String>();
static { static {
// 1-------10--------20--------30--------40--------50--------60--------70--------80 // 1-------10--------20--------30--------40--------50--------60--------70--------80
COMMENT_MAP.put(PROPERTY_TARGET, COMMENT_MAP.put(PROPERTY_TARGET,
"# Project target.\n"); "# Project target.\n");
COMMENT_MAP.put(PROPERTY_APK_CONFIGS, COMMENT_MAP.put(PROPERTY_APK_CONFIGS,
@@ -114,7 +117,7 @@ public final class ProjectProperties {
"# For customization when using a Version Control System, please read the\n" + "# For customization when using a Version Control System, please read the\n" +
"# header note.\n"); "# header note.\n");
} }
private final String mProjectFolderOsPath; private final String mProjectFolderOsPath;
private final Map<String, String> mProperties; private final Map<String, String> mProperties;
private final PropertyType mType; private final PropertyType mType;
@@ -122,9 +125,9 @@ public final class ProjectProperties {
/** /**
* Loads a project properties file and return a {@link ProjectProperties} object * Loads a project properties file and return a {@link ProjectProperties} object
* containing the properties * containing the properties
* *
* @param projectFolderOsPath the project folder. * @param projectFolderOsPath the project folder.
* @param type One the possible {@link PropertyType}s. * @param type One the possible {@link PropertyType}s.
*/ */
public static ProjectProperties load(String projectFolderOsPath, PropertyType type) { public static ProjectProperties load(String projectFolderOsPath, PropertyType type) {
File projectFolder = new File(projectFolderOsPath); File projectFolder = new File(projectFolderOsPath);
@@ -139,7 +142,7 @@ public final class ProjectProperties {
} }
return null; return null;
} }
/** /**
* Merges all properties from the given file into the current properties. * Merges all properties from the given file into the current properties.
* <p/> * <p/>
@@ -153,8 +156,8 @@ public final class ProjectProperties {
* <li>The result is that this contains all the properties from default plus those * <li>The result is that this contains all the properties from default plus those
* overridden by the build.properties file. * overridden by the build.properties file.
* </ul> * </ul>
* *
* @param type One the possible {@link PropertyType}s. * @param type One the possible {@link PropertyType}s.
* @return this object, for chaining. * @return this object, for chaining.
*/ */
public ProjectProperties merge(PropertyType type) { public ProjectProperties merge(PropertyType type) {
@@ -187,7 +190,7 @@ public final class ProjectProperties {
// create and return a ProjectProperties with an empty map. // create and return a ProjectProperties with an empty map.
return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type); return new ProjectProperties(projectFolderOsPath, new HashMap<String, String>(), type);
} }
/** /**
* Sets a new properties. If a property with the same name already exists, it is replaced. * Sets a new properties. If a property with the same name already exists, it is replaced.
* @param name the name of the property. * @param name the name of the property.
@@ -196,7 +199,7 @@ public final class ProjectProperties {
public void setProperty(String name, String value) { public void setProperty(String name, String value) {
mProperties.put(name, value); mProperties.put(name, value);
} }
/** /**
* Sets the target property to the given {@link IAndroidTarget} object. * Sets the target property to the given {@link IAndroidTarget} object.
* @param target the Android target. * @param target the Android target.
@@ -205,7 +208,7 @@ public final class ProjectProperties {
assert mType == PropertyType.DEFAULT; assert mType == PropertyType.DEFAULT;
mProperties.put(PROPERTY_TARGET, target.hashString()); mProperties.put(PROPERTY_TARGET, target.hashString());
} }
/** /**
* Returns the value of a property. * Returns the value of a property.
* @param name the name of the property. * @param name the name of the property.
@@ -214,7 +217,7 @@ public final class ProjectProperties {
public String getProperty(String name) { public String getProperty(String name) {
return mProperties.get(name); return mProperties.get(name);
} }
/** /**
* Removes a property and returns its previous value (or null if the property did not exist). * Removes a property and returns its previous value (or null if the property did not exist).
* @param name the name of the property to remove. * @param name the name of the property to remove.
@@ -224,17 +227,18 @@ public final class ProjectProperties {
} }
/** /**
* Saves the property file. * Saves the property file, using UTF-8 encoding.
* @throws IOException * @throws IOException
*/ */
public void save() throws IOException { public void save() throws IOException {
File toSave = new File(mProjectFolderOsPath, mType.mFilename); File toSave = new File(mProjectFolderOsPath, mType.mFilename);
FileWriter writer = new FileWriter(toSave); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(toSave),
SdkConstants.INI_CHARSET);
// write the header // write the header
writer.write(mType.mHeader); writer.write(mType.mHeader);
// write the properties. // write the properties.
for (Entry<String, String> entry : mProperties.entrySet()) { for (Entry<String, String> entry : mProperties.entrySet()) {
String comment = COMMENT_MAP.get(entry.getKey()); String comment = COMMENT_MAP.get(entry.getKey());
@@ -245,11 +249,11 @@ public final class ProjectProperties {
value = value.replaceAll("\\\\", "\\\\\\\\"); value = value.replaceAll("\\\\", "\\\\\\\\");
writer.write(String.format("%s=%s\n", entry.getKey(), value)); writer.write(String.format("%s=%s\n", entry.getKey(), value));
} }
// close the file to flush // close the file to flush
writer.close(); writer.close();
} }
/** /**
* Private constructor. * Private constructor.
* <p/> * <p/>