OSDN Git Service

Don't erase SDCard when editing existing AVD.
authorRaphael Moll <ralf@android.com>
Mon, 28 Feb 2011 22:11:44 +0000 (14:11 -0800)
committerRaphael Moll <ralf@android.com>
Fri, 4 Mar 2011 21:39:40 +0000 (13:39 -0800)
SDK Bug 14162

Change-Id: I22493443e706bda830916ed0fc09741a1d6d1f15

sdkmanager/app/src/com/android/sdkmanager/Main.java
sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java
sdkmanager/app/tests/com/android/sdkmanager/MainTest.java
sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java

index 8cb88ae..4b0581f 100644 (file)
@@ -985,15 +985,16 @@ public class Main {
             String preferredAbi = SdkConstants.ABI_ARMEABI;
             @SuppressWarnings("unused") // newAvdInfo is never read, yet useful for debugging
             AvdInfo newAvdInfo = avdManager.createAvd(avdFolder,
-                        avdName,
-                        target,
-                        preferredAbi,
-                        skin,
-                        mSdkCommandLine.getParamSdCard(),
-                        hardwareConfig,
-                        removePrevious,
-                        mSdkCommandLine.getFlagSnapshot(),
-                        mSdkLog);
+                    avdName,
+                    target,
+                    preferredAbi,
+                    skin,
+                    mSdkCommandLine.getParamSdCard(),
+                    hardwareConfig,
+                    mSdkCommandLine.getFlagSnapshot(),
+                    removePrevious,
+                    false, //edit existing
+                    mSdkLog);
 
         } catch (AndroidLocationException e) {
             errorAndExit(e.getMessage());
index cc8d6a4..9579bf5 100644 (file)
@@ -59,8 +59,18 @@ public class AvdManagerTest extends TestCase {
 
     public void testCreateAvdWithoutSnapshot() {
         mAvdManager.createAvd(
-                mAvdFolder, this.getName(), mTarget, SdkConstants.ABI_ARMEABI,
-                null, null, null, false, false, mLog);
+                mAvdFolder,
+                this.getName(),
+                mTarget,
+                SdkConstants.ABI_ARMEABI,
+                null,   // skinName
+                null,   // sdName
+                null,   // properties
+                false,  // createSnapshot
+                false,  // removePrevious
+                false,  // editExisting
+                mLog);
+
         assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]",
                 mLog.toString());
         assertTrue("Expected config.ini in " + mAvdFolder,
@@ -78,9 +88,19 @@ public class AvdManagerTest extends TestCase {
     }
 
     public void testCreateAvdWithSnapshot() {
+
         mAvdManager.createAvd(
-                mAvdFolder, this.getName(), mTarget, SdkConstants.ABI_ARMEABI,
-                null, null, null, false, true, mLog);
+                mAvdFolder,
+                this.getName(),
+                mTarget,
+                SdkConstants.ABI_ARMEABI,
+                null,   // skinName
+                null,   // sdName
+                null,   // properties
+                true,   // createSnapshot
+                false,  // removePrevious
+                false,  // editExisting
+                mLog);
 
         assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]",
                 mLog.toString());
index e4352ff..c4a7a53 100644 (file)
@@ -69,8 +69,18 @@ public class MainTest extends TestCase {
         Main main = new Main();
         main.setLogger(mLog);
         mAvdManager.createAvd(
-                mAvdFolder, this.getName(), mTarget, SdkConstants.ABI_ARMEABI,
-                null, null, null, false, false, mLog);
+                mAvdFolder,
+                this.getName(),
+                mTarget,
+                SdkConstants.ABI_ARMEABI,
+                null,   // skinName
+                null,   // sdName
+                null,   // properties
+                false,  // createSnapshot
+                false,  // removePrevious
+                false,  // editExisting
+                mLog);
+
         mLog.clear();
         main.displayAvdList(mAvdManager);
         assertEquals(
@@ -86,9 +96,20 @@ public class MainTest extends TestCase {
     public void testDisplayAvdListOfOneSnapshot() {
         Main main = new Main();
         main.setLogger(mLog);
+
         mAvdManager.createAvd(
-                mAvdFolder, this.getName(), mTarget, SdkConstants.ABI_ARMEABI,
-                null, null, null, false, true, mLog);
+                mAvdFolder,
+                this.getName(),
+                mTarget,
+                SdkConstants.ABI_ARMEABI,
+                null,   // skinName
+                null,   // sdName
+                null,   // properties
+                true,  // createSnapshot
+                false,  // removePrevious
+                false,  // editExisting
+                mLog);
+
         mLog.clear();
         main.displayAvdList(mAvdManager);
         assertEquals(
index fefe891..038a59e 100644 (file)
@@ -100,6 +100,7 @@ public final class AvdManager {
      * This property is for UI purposes only. It is not used by the emulator.
      *
      * @see #SDCARD_SIZE_PATTERN
+     * @see #parseSdcardSize(String, String[])
      */
     public final static String AVD_INI_SDCARD_SIZE = "sdcard.size"; //$NON-NLS-1$
     /**
@@ -145,18 +146,27 @@ public final class AvdManager {
 
     /**
      * Pattern for matching SD Card sizes, e.g. "4K" or "16M".
+     * Callers should use {@link #parseSdcardSize(String, String[])} instead of using this directly.
      */
-    public final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([KMG])"); //$NON-NLS-1$
+    private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("(\\d+)([KMG])"); //$NON-NLS-1$
 
     /**
      * Minimal size of an SDCard image file in bytes. Currently 9 MiB.
      */
+
     public static final long SDCARD_MIN_BYTE_SIZE = 9<<20;
     /**
      * Maximal size of an SDCard image file in bytes. Currently 1023 GiB.
      */
     public static final long SDCARD_MAX_BYTE_SIZE = 1023L<<30;
 
+    /** The sdcard string represents a valid number but the size is outside of the allowed range. */
+    public final static int SDCARD_SIZE_NOT_IN_RANGE = 0;
+    /** The sdcard string looks like a size number+suffix but the number failed to decode. */
+    public final static int SDCARD_SIZE_INVALID = -1;
+    /** The sdcard string doesn't look like a size, it might be a path instead. */
+    public final static int SDCARD_NOT_SIZE_PATTERN = -2;
+
     /** Regex used to validate characters that compose an AVD name. */
     public final static Pattern RE_AVD_NAME = Pattern.compile("[a-zA-Z0-9._-]+"); //$NON-NLS-1$
 
@@ -487,6 +497,71 @@ public final class AvdManager {
     }
 
     /**
+     * Parse the sdcard string to decode the size.
+     * Returns:
+     * <ul>
+     * <li> The size in bytes > 0 if the sdcard string is a valid size in the allowed range.
+     * <li> {@link #SDCARD_SIZE_NOT_IN_RANGE} (0)
+     *          if the sdcard string is a valid size NOT in the allowed range.
+     * <li> {@link #SDCARD_SIZE_INVALID} (-1)
+     *          if the sdcard string is number that fails to parse correctly.
+     * <li> {@link #SDCARD_NOT_SIZE_PATTERN} (-2)
+     *          if the sdcard string is not a number, in which case it's probably a file path.
+     * </ul>
+     *
+     * @param sdcard The sdcard string, which can be a file path, a size string or something else.
+     * @param parsedStrings If non-null, an array of 2 strings. The first string will be
+     *  filled with the parsed numeric size and the second one will be filled with the
+     *  parsed suffix. This is filled even if the returned size is deemed out of range or
+     *  failed to parse. The values are null if the sdcard is not a size pattern.
+     * @return A size in byte if > 0, or {@link #SDCARD_SIZE_NOT_IN_RANGE},
+     *  {@link #SDCARD_SIZE_INVALID} or {@link #SDCARD_NOT_SIZE_PATTERN} as error codes.
+     */
+    public static long parseSdcardSize(String sdcard, String[] parsedStrings) {
+
+        if (parsedStrings != null) {
+            assert parsedStrings.length == 2;
+            parsedStrings[0] = null;
+            parsedStrings[1] = null;
+        }
+
+        Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
+        if (m.matches()) {
+            if (parsedStrings != null) {
+                assert parsedStrings.length == 2;
+                parsedStrings[0] = m.group(1);
+                parsedStrings[1] = m.group(2);
+            }
+
+            // get the sdcard values for checks
+            try {
+                long sdcardSize = Long.parseLong(m.group(1));
+
+                String sdcardSizeModifier = m.group(2);
+                if ("K".equals(sdcardSizeModifier)) {           //$NON-NLS-1$
+                    sdcardSize <<= 10;
+                } else if ("M".equals(sdcardSizeModifier)) {    //$NON-NLS-1$
+                    sdcardSize <<= 20;
+                } else if ("G".equals(sdcardSizeModifier)) {    //$NON-NLS-1$
+                    sdcardSize <<= 30;
+                }
+
+                if (sdcardSize < SDCARD_MIN_BYTE_SIZE ||
+                        sdcardSize > SDCARD_MAX_BYTE_SIZE) {
+                    return SDCARD_SIZE_NOT_IN_RANGE;
+                }
+
+                return sdcardSize;
+            } catch (NumberFormatException e) {
+                // This could happen if the number is too large to fit in a long.
+                return SDCARD_SIZE_INVALID;
+            }
+        }
+
+        return SDCARD_NOT_SIZE_PATTERN;
+    }
+
+    /**
      * Returns all the existing AVDs.
      * @return a newly allocated array containing all the AVDs.
      */
@@ -636,22 +711,6 @@ public final class AvdManager {
     }
 
     /**
-     * Creates a new AVD, but with no snapshot.
-     *
-     * See {@link #createAvd(File, String, IAndroidTarget,
-     *                 String, String,
-     *                 Map, boolean, boolean, ISdkLog)}
-     **/
-    @Deprecated
-    public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
-            String abiType, String skinName,
-            String sdcard, Map<String, String> hardwareConfig, boolean removePrevious,
-            ISdkLog log) {
-       return createAvd(avdFolder, name, target, abiType, skinName, sdcard,
-                hardwareConfig, removePrevious, false, log);
-    }
-
-    /**
      * 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.
@@ -662,15 +721,26 @@ public final class AvdManager {
      * @param sdcard the parameter value for the sdCard. Can be null. This is either a path to
      *        an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
      * @param hardwareConfig the hardware setup for the AVD. Can be null to use defaults.
-     * @param removePrevious If true remove any previous files.
      * @param createSnapshot If true copy a blank snapshot image into the AVD.
+     * @param removePrevious If true remove any previous files.
+     * @param editExisting If true, edit an existing AVD, changing only the minimum required.
+     *          This won't remove files unless required or unless {@code removePrevious} is set.
      * @param log the log object to receive action logs. Cannot be null.
      * @return The new {@link AvdInfo} in case of success (which has just been added to the
      *         internal list) or null in case of failure.
      */
-    public AvdInfo createAvd(File avdFolder, String name, IAndroidTarget target,
-           String abiType, String skinName, String sdcard, Map<String,String> hardwareConfig,
-           boolean removePrevious, boolean createSnapshot, ISdkLog log){
+    public AvdInfo createAvd(
+            File avdFolder,
+            String name,
+            IAndroidTarget target,
+            String abiType,
+            String skinName,
+            String sdcard,
+            Map<String,String> hardwareConfig,
+            boolean createSnapshot,
+            boolean removePrevious,
+            boolean editExisting,
+            ISdkLog log) {
         if (log == null) {
             throw new IllegalArgumentException("log cannot be null");
         }
@@ -687,8 +757,9 @@ public final class AvdManager {
                     } catch (SecurityException e) {
                         log.error(e, "Failed to delete %1$s", avdFolder.getAbsolutePath());
                     }
-                } else {
-                    // AVD shouldn't already exist if removePrevious is false.
+                } else if (!editExisting) {
+                    // AVD shouldn't already exist if removePrevious is false and
+                    // we're not editing an existing AVD.
                     log.error(null,
                             "Folder %1$s is in the way. Use --force if you want to overwrite.",
                             avdFolder.getAbsolutePath());
@@ -697,6 +768,8 @@ public final class AvdManager {
             } else {
                 // create the AVD folder.
                 avdFolder.mkdir();
+                // We're not editing an existing AVD.
+                editExisting = false;
             }
 
             // actually write the ini file
@@ -742,17 +815,24 @@ public final class AvdManager {
 
             // Create the snapshot file
             if (createSnapshot) {
-                String toolsLib = mSdkManager.getLocation() + File.separator
-                        + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER;
-                File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG);
-                if (snapshotBlank.exists() == false) {
-                   log.error(null, "Unable to find a '%2$s%1$s' file to copy into the AVD folder.",
-                            SNAPSHOTS_IMG, toolsLib);
-                    needCleanup = true;
-                    return null;
-                }
                 File snapshotDest = new File(avdFolder, SNAPSHOTS_IMG);
-                copyImageFile(snapshotBlank, snapshotDest);
+                if (snapshotDest.isFile() && editExisting) {
+                    log.printf("Snapshot image already present, was not changed.");
+
+                } else {
+                    String toolsLib = mSdkManager.getLocation() + File.separator
+                                      + SdkConstants.OS_SDK_TOOLS_LIB_EMULATOR_FOLDER;
+                    File snapshotBlank = new File(toolsLib, SNAPSHOTS_IMG);
+                    if (snapshotBlank.exists() == false) {
+                        log.error(null,
+                                "Unable to find a '%2$s%1$s' file to copy into the AVD folder.",
+                                SNAPSHOTS_IMG, toolsLib);
+                        needCleanup = true;
+                        return null;
+                    }
+
+                    copyImageFile(snapshotBlank, snapshotDest);
+                }
                 values.put(AVD_INI_SNAPSHOT_PRESENT, "true");
             }
 
@@ -785,46 +865,48 @@ public final class AvdManager {
             }
 
             if (sdcard != null && sdcard.length() > 0) {
-                File sdcardFile = new File(sdcard);
-                if (sdcardFile.isFile()) {
-                    // sdcard value is an external sdcard, so we put its path into the config.ini
-                    values.put(AVD_INI_SDCARD_PATH, sdcard);
+                // 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.
+
+                long sdcardSize = parseSdcardSize(sdcard, null/*parsedStrings*/);
+
+                if (sdcardSize == SDCARD_SIZE_NOT_IN_RANGE) {
+                    log.error(null, "SD Card size must be in the range 9 MiB..1023 GiB.");
+                    needCleanup = true;
+                    return null;
+
+                } else if (sdcardSize == SDCARD_SIZE_INVALID) {
+                    log.error(null, "Unable to parse SD Card size");
+                    needCleanup = true;
+                    return null;
+
+                } else if (sdcardSize == SDCARD_NOT_SIZE_PATTERN) {
+                    File sdcardFile = new File(sdcard);
+                    if (sdcardFile.isFile()) {
+                        // sdcard value is an external sdcard, so we put its path into the config.ini
+                        values.put(AVD_INI_SDCARD_PATH, sdcard);
+                    } else {
+                        log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n"
+                                + "Value should be:\n" + "1. path to an sdcard.\n"
+                                + "2. size of the sdcard to create: <size>[K|M]", sdcard);
+                        needCleanup = true;
+                        return null;
+                    }
                 } else {
-                    // 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.
-
-                    // First, check that it matches the pattern for sdcard size
-                    Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
-                    if (m.matches()) {
-                        // get the sdcard values for checks
-                        try {
-                            long sdcardSize = Long.parseLong(m.group(1));
-
-                            String sdcardSizeModifier = m.group(2);
-                            if ("K".equals(sdcardSizeModifier)) {           //$NON-NLS-1$
-                                sdcardSize <<= 10;
-                            } else if ("M".equals(sdcardSizeModifier)) {    //$NON-NLS-1$
-                                sdcardSize <<= 20;
-                            } else if ("G".equals(sdcardSizeModifier)) {    //$NON-NLS-1$
-                                sdcardSize <<= 30;
-                            }
-
-                            if (sdcardSize < SDCARD_MIN_BYTE_SIZE ||
-                                    sdcardSize > SDCARD_MAX_BYTE_SIZE) {
-                                log.error(null, "SD Card size must be in the range 9 MiB..1023 GiB.");
-                                needCleanup = true;
-                                return null;
-                            }
-                        } catch (NumberFormatException e) {
-                            // this should never happen since the string is validated
-                            // by the regexp
-                            log.error(null, "Unable to parse SD Card size");
-                            needCleanup = true;
-                            return null;
+                    // create the sdcard.
+                    File sdcardFile = new File(avdFolder, SDCARD_IMG);
+
+                    boolean runMkSdcard = true;
+                    if (sdcardFile.exists()) {
+                        if (sdcardFile.length() == sdcardSize && editExisting) {
+                            // There's already an sdcard file with the right size and we're
+                            // not overriding it... so don't remove it.
+                            runMkSdcard = false;
+                            log.printf("SD Card already present with same size, was not changed.");
                         }
+                    }
 
-                        // create the sdcard.
-                        sdcardFile = new File(avdFolder, SDCARD_IMG);
+                    if (runMkSdcard) {
                         String path = sdcardFile.getAbsolutePath();
 
                         // execute mksdcard with the proper parameters.
@@ -845,17 +927,11 @@ public final class AvdManager {
                             return null; // mksdcard output has already been displayed, no need to
                                          // output anything else.
                         }
-
-                        // add a property containing the size of the sdcard for display purpose
-                        // only when the dev does 'android list avd'
-                        values.put(AVD_INI_SDCARD_SIZE, sdcard);
-                    } else {
-                        log.error(null, "'%1$s' is not recognized as a valid sdcard value.\n"
-                                + "Value should be:\n" + "1. path to an sdcard.\n"
-                                + "2. size of the sdcard to create: <size>[K|M]", sdcard);
-                        needCleanup = true;
-                        return null;
                     }
+
+                    // add a property containing the size of the sdcard for display purpose
+                    // only when the dev does 'android list avd'
+                    values.put(AVD_INI_SDCARD_SIZE, sdcard);
                 }
             }
 
@@ -910,11 +986,21 @@ public final class AvdManager {
             StringBuilder report = new StringBuilder();
 
             if (target.isPlatform()) {
-                report.append(String.format("Created AVD '%1$s' based on %2$s",
-                        name, target.getName()));
+                if (editExisting) {
+                    report.append(String.format("Updated AVD '%1$s' based on %2$s",
+                            name, target.getName()));
+                } else {
+                    report.append(String.format("Created AVD '%1$s' based on %2$s",
+                            name, target.getName()));
+                }
             } else {
-                report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", name,
-                        target.getName(), target.getVendor()));
+                if (editExisting) {
+                    report.append(String.format("Updated AVD '%1$s' based on %2$s (%3$s)", name,
+                            target.getName(), target.getVendor()));
+                } else {
+                    report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", name,
+                            target.getName(), target.getVendor()));
+                }
             }
             report.append(String.format(", %s processor", AvdInfo.getPrettyAbiType(abiType)));
 
@@ -939,14 +1025,14 @@ public final class AvdManager {
             AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/);
 
             synchronized (mAllAvdList) {
-                if (oldAvdInfo != null && removePrevious) {
+                if (oldAvdInfo != null && (removePrevious || editExisting)) {
                     mAllAvdList.remove(oldAvdInfo);
                 }
                 mAllAvdList.add(newAvdInfo);
                 mValidAvdList = mBrokenAvdList = null;
             }
 
-            if (removePrevious &&
+            if ((removePrevious || editExisting) &&
                     newAvdInfo != null &&
                     oldAvdInfo != null &&
                     !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
@@ -1120,14 +1206,18 @@ public final class AvdManager {
             IAndroidTarget target,
             boolean removePrevious)
             throws AndroidLocationException, IOException {
-        HashMap<String, String> values = new HashMap<String, String>();
         File iniFile = AvdInfo.getIniFile(name);
-        if (iniFile.isFile()) {
-            iniFile.delete();
-        } else if (iniFile.isDirectory()) {
-            deleteContentOf(iniFile);
-            iniFile.delete();
+
+        if (removePrevious) {
+            if (iniFile.isFile()) {
+                iniFile.delete();
+            } else if (iniFile.isDirectory()) {
+                deleteContentOf(iniFile);
+                iniFile.delete();
+            }
         }
+
+        HashMap<String, String> values = new HashMap<String, String>();
         values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
         values.put(AVD_INFO_TARGET, target.hashString());
         writeIniFile(iniFile, values);
index db6034a..8955c52 100644 (file)
@@ -98,6 +98,10 @@ final class AvdCreationDialog extends GridDialog {
     private final ArrayList<String> mEditedProperties = new ArrayList<String>();
     private final ImageFactory mImageFactory;
     private final ISdkLog mSdkLog;
+    /**
+     * The original AvdInfo if we're editing an existing AVD.
+     * Null when we're creating a new AVD.
+     */
     private final AvdInfo mEditAvdInfo;
 
     private Text mAvdName;
@@ -157,6 +161,9 @@ final class AvdCreationDialog extends GridDialog {
         public void modifyText(ModifyEvent e) {
             String name = mAvdName.getText().trim();
             if (mEditAvdInfo == null || !name.equals(mEditAvdInfo.getName())) {
+                // Case where we're creating a new AVD or editing an existing one
+                // and the AVD name has been changed... check for name uniqueness.
+
                 Pair<AvdConflict, String> conflict = mAvdManager.isAvdNameConflicting(name);
                 if (conflict.getFirst() != AvdManager.AvdConflict.NO_CONFLICT) {
                     // If we're changing the state from disabled to enabled, make sure
@@ -172,8 +179,10 @@ final class AvdCreationDialog extends GridDialog {
                     mForceCreation.setSelection(false);
                 }
             } else {
+                // Case where we're editing an existing AVD with the name unchanged.
+
                 mForceCreation.setEnabled(false);
-                mForceCreation.setSelection(true);
+                mForceCreation.setSelection(false);
             }
             validatePage();
         }
@@ -714,71 +723,78 @@ final class AvdCreationDialog extends GridDialog {
         }
 
         Map<String, String> props = mEditAvdInfo.getProperties();
-
-        // First try the skin name and if it doesn't work fallback on the skin path
-        nextSkin: for (int s = 0; s < 2; s++) {
-            String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME
-                                           : AvdManager.AVD_INI_SKIN_PATH);
-            if (skin != null && skin.length() > 0) {
-                Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin);
-                if (m.matches() && m.groupCount() == 2) {
-                    enableSkinWidgets(false);
-                    mSkinListRadio.setSelection(false);
-                    mSkinSizeRadio.setSelection(true);
-                    mSkinSizeWidth.setText(m.group(1));
-                    mSkinSizeHeight.setText(m.group(2));
-                    break nextSkin;
-                } else {
-                    enableSkinWidgets(true);
-                    mSkinSizeRadio.setSelection(false);
-                    mSkinListRadio.setSelection(true);
-
-                    int n = mSkinCombo.getItemCount();
-                    for (int i = 0; i < n; i++) {
-                        if (skin.equals(mSkinCombo.getItem(i))) {
-                            mSkinCombo.select(i);
-                            break nextSkin;
+        if (props != null) {
+            // First try the skin name and if it doesn't work fallback on the skin path
+            nextSkin: for (int s = 0; s < 2; s++) {
+                String skin = props.get(s == 0 ? AvdManager.AVD_INI_SKIN_NAME
+                                               : AvdManager.AVD_INI_SKIN_PATH);
+                if (skin != null && skin.length() > 0) {
+                    Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin);
+                    if (m.matches() && m.groupCount() == 2) {
+                        enableSkinWidgets(false);
+                        mSkinListRadio.setSelection(false);
+                        mSkinSizeRadio.setSelection(true);
+                        mSkinSizeWidth.setText(m.group(1));
+                        mSkinSizeHeight.setText(m.group(2));
+                        break nextSkin;
+                    } else {
+                        enableSkinWidgets(true);
+                        mSkinSizeRadio.setSelection(false);
+                        mSkinListRadio.setSelection(true);
+
+                        int n = mSkinCombo.getItemCount();
+                        for (int i = 0; i < n; i++) {
+                            if (skin.equals(mSkinCombo.getItem(i))) {
+                                mSkinCombo.select(i);
+                                break nextSkin;
+                            }
                         }
                     }
                 }
             }
-        }
 
-        String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH);
-        if (sdcard != null && sdcard.length() > 0) {
-            enableSdCardWidgets(false);
-            mSdCardSizeRadio.setSelection(false);
-            mSdCardFileRadio.setSelection(true);
-            mSdCardFile.setText(sdcard);
-        }
+            String sdcard = props.get(AvdManager.AVD_INI_SDCARD_PATH);
+            if (sdcard != null && sdcard.length() > 0) {
+                enableSdCardWidgets(false);
+                mSdCardSizeRadio.setSelection(false);
+                mSdCardFileRadio.setSelection(true);
+                mSdCardFile.setText(sdcard);
+            }
+
+            sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE);
+            if (sdcard != null && sdcard.length() > 0) {
 
-        sdcard = props.get(AvdManager.AVD_INI_SDCARD_SIZE);
-        if (sdcard != null && sdcard.length() > 0) {
-            Matcher m = AvdManager.SDCARD_SIZE_PATTERN.matcher(sdcard);
-            if (m.matches() && m.groupCount() == 2) {
-                enableSdCardWidgets(true);
-                mSdCardFileRadio.setSelection(false);
-                mSdCardSizeRadio.setSelection(true);
-
-                mSdCardSize.setText(m.group(1));
-
-                String suffix = m.group(2);
-                int n = mSdCardSizeCombo.getItemCount();
-                for (int i = 0; i < n; i++) {
-                    if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) {
-                        mSdCardSizeCombo.select(i);
+                String[] values = new String[2];
+                long sdcardSize = AvdManager.parseSdcardSize(sdcard, values);
+
+                if (sdcardSize != AvdManager.SDCARD_NOT_SIZE_PATTERN) {
+                    enableSdCardWidgets(true);
+                    mSdCardFileRadio.setSelection(false);
+                    mSdCardSizeRadio.setSelection(true);
+
+                    mSdCardSize.setText(values[0]);
+
+                    String suffix = values[1];
+                    int n = mSdCardSizeCombo.getItemCount();
+                    for (int i = 0; i < n; i++) {
+                        if (mSdCardSizeCombo.getItem(i).startsWith(suffix)) {
+                            mSdCardSizeCombo.select(i);
+                        }
                     }
                 }
             }
-        }
 
-        String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
-        if (snapshots != null && snapshots.length() > 0) {
-            mSnapshotCheck.setSelection(snapshots.equals("true"));
+            String snapshots = props.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
+            if (snapshots != null && snapshots.length() > 0) {
+                mSnapshotCheck.setSelection(snapshots.equals("true"));
+            }
         }
 
         mProperties.clear();
-        mProperties.putAll(props);
+
+        if (props != null) {
+            mProperties.putAll(props);
+        }
 
         // Cleanup known non-hardware properties
         mProperties.remove(AvdManager.AVD_INI_ABI_TYPE);
@@ -1030,6 +1046,22 @@ final class AvdCreationDialog extends GridDialog {
                     }
                     if (value <= 0) {
                         error = "SD Card size is invalid. Range is 9 MiB..1023 GiB.";
+                    } else if (mEditAvdInfo != null) {
+                        // When editing an existing AVD, compare with the existing
+                        // sdcard size, if any. It only matters if there was an sdcard setting
+                        // before.
+                        Map<String, String> props = mEditAvdInfo.getProperties();
+                        if (props != null) {
+                            String original =
+                                mEditAvdInfo.getProperties().get(AvdManager.AVD_INI_SDCARD_SIZE);
+                            if (original != null && original.length() > 0) {
+                                long originalSize =
+                                    AvdManager.parseSdcardSize(original, null/*parsedStrings*/);
+                                if (originalSize > 0 && value != originalSize) {
+                                    warning = "A new SD Card file will be created.\nThe current SD Card file will be lost.";
+                                }
+                            }
+                        }
                     }
                 }
             }
@@ -1298,8 +1330,9 @@ final class AvdCreationDialog extends GridDialog {
                 skinName,
                 sdName,
                 mProperties,
-                force,
                 snapshot,
+                force,
+                mEditAvdInfo != null, //edit existing
                 log);
 
         success = avdInfo != null;