Also respect 'Wipe User Data' by disabling snapshot launch, and support 'Edit AVD'.
Change-Id: I5bc94c316e550b2585ca80185a02ffbe6d3e8401
}
/**
+ * Resets the folder used to store android related files. For testing.
+ */
+ public final static void resetFolder() {
+ sPrefsLocation = null;
+ }
+
+ /**
* Checks a list of system properties and/or system environment variables for validity, and
* existing director, and returns the first one.
* @param names
# emulator
bin/emulator tools/emulator
+sdk/emulator/snapshot/snapshots.img tools/lib/emulator/snapshots.img
# Java-Based SDK Tools
bin/ddms tools/ddms
* Internal helper to define a new argument for a give action.
*
* @param mode The {@link Mode} for the argument.
+ * @param mandatory The argument is required (never if {@link Mode.BOOLEAN})
* @param verb The verb name. Can be #INTERNAL_VERB.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Can be empty but not null.
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
-import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.HardwareProperties;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
+import com.android.sdklib.internal.avd.HardwareProperties;
import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.internal.project.ProjectCreator;
-import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectCreator.OutputLevel;
+import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
import com.android.sdklib.io.FileWrapper;
import com.android.sdklib.repository.SdkRepoConstants;
};
}
+ /** For testing */
+ public void setLogger(ISdkLog logger) {
+ mSdkLog = logger;
+ }
+
/**
* Init the application by making sure the SDK path is available and
* doing basic parsing of the SDK.
}
/**
- * Displays the list of available AVDs.
+ * Displays the list of available AVDs for the given AvdManager.
+ *
+ * @param avdManager
*/
- private void displayAvdList() {
- try {
- AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
+ public void displayAvdList(AvdManager avdManager) {
+ mSdkLog.printf("Available Android Virtual Devices:\n");
+
+ AvdInfo[] avds = avdManager.getValidAvds();
+ for (int index = 0 ; index < avds.length ; index++) {
+ AvdInfo info = avds[index];
+ if (index > 0) {
+ mSdkLog.printf("---------\n");
+ }
+ mSdkLog.printf(" Name: %s\n", info.getName());
+ mSdkLog.printf(" Path: %s\n", info.getPath());
- mSdkLog.printf("Available Android Virtual Devices:\n");
+ // get the target of the AVD
+ IAndroidTarget target = info.getTarget();
+ if (target.isPlatform()) {
+ mSdkLog.printf(" Target: %s (API level %s)\n", target.getName(),
+ target.getVersion().getApiString());
+ } else {
+ mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target
+ .getVendor());
+ mSdkLog.printf(" Based on Android %s (API level %s)\n",
+ target.getVersionName(), target.getVersion().getApiString());
+ }
- AvdInfo[] avds = avdManager.getValidAvds();
- for (int index = 0 ; index < avds.length ; index++) {
- AvdInfo info = avds[index];
- if (index > 0) {
- mSdkLog.printf("---------\n");
+ // display some extra values.
+ Map<String, String> properties = info.getProperties();
+ if (properties != null) {
+ String skin = properties.get(AvdManager.AVD_INI_SKIN_NAME);
+ if (skin != null) {
+ mSdkLog.printf(" Skin: %s\n", skin);
}
- mSdkLog.printf(" Name: %s\n", info.getName());
- mSdkLog.printf(" Path: %s\n", info.getPath());
-
- // get the target of the AVD
- IAndroidTarget target = info.getTarget();
- if (target.isPlatform()) {
- mSdkLog.printf(" Target: %s (API level %s)\n", target.getName(),
- target.getVersion().getApiString());
- } else {
- mSdkLog.printf(" Target: %s (%s)\n", target.getName(), target
- .getVendor());
- mSdkLog.printf(" Based on Android %s (API level %s)\n",
- target.getVersionName(), target.getVersion().getApiString());
+ String sdcard = properties.get(AvdManager.AVD_INI_SDCARD_SIZE);
+ if (sdcard == null) {
+ sdcard = properties.get(AvdManager.AVD_INI_SDCARD_PATH);
}
-
- // display some extra values.
- Map<String, String> properties = info.getProperties();
- if (properties != null) {
- String skin = properties.get(AvdManager.AVD_INI_SKIN_NAME);
- if (skin != null) {
- mSdkLog.printf(" Skin: %s\n", skin);
- }
- String sdcard = properties.get(AvdManager.AVD_INI_SDCARD_SIZE);
- if (sdcard == null) {
- sdcard = properties.get(AvdManager.AVD_INI_SDCARD_PATH);
- }
- if (sdcard != null) {
- mSdkLog.printf(" Sdcard: %s\n", sdcard);
- }
+ if (sdcard != null) {
+ mSdkLog.printf(" Sdcard: %s\n", sdcard);
+ }
+ String snapshot = properties.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
+ if (snapshot != null) {
+ mSdkLog.printf("Snapshot: %s\n", snapshot);
}
}
+ }
- // Are there some unused AVDs?
- AvdInfo[] badAvds = avdManager.getBrokenAvds();
+ // Are there some unused AVDs?
+ AvdInfo[] badAvds = avdManager.getBrokenAvds();
- if (badAvds.length == 0) {
- return;
+ if (badAvds.length == 0) {
+ return;
+ }
+
+ mSdkLog.printf("\nThe following Android Virtual Devices could not be loaded:\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("\nThe following Android Virtual Devices could not be loaded:\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());
+ String error = info.getErrorMessage();
+ mSdkLog.printf(" Error: %s\n", error == null ? "Uknown error" : error);
+ needSeparator = true;
+ }
+ }
- String error = info.getErrorMessage();
- mSdkLog.printf(" Error: %s\n", error == null ? "Uknown error" : error);
- needSeparator = true;
- }
+ /**
+ * Displays the list of available AVDs.
+ */
+ private void displayAvdList() {
+ try {
+ AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
+ displayAvdList(avdManager);
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
mSdkCommandLine.getParamSdCard(),
hardwareConfig,
removePrevious,
+ mSdkCommandLine.getFlagSnapshot(),
mSdkLog);
} catch (AndroidLocationException e) {
public static final String KEY_PROXY_HOST = "proxy-host";
public static final String KEY_DRY_MODE = "dry-mode";
public static final String KEY_OBSOLETE = "obsolete";
+ public static final String KEY_SNAPSHOT = "snapshot";
/**
* Action definitions for SdkManager command line.
define(Mode.BOOLEAN, false,
VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE,
"Forces creation (overwrites an existing AVD)", false);
+ define(Mode.BOOLEAN, false,
+ VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT,
+ "Place a snapshots file in the AVD, to enable persistence.", false);
// --- delete avd ---
return ((Boolean) getValue(null, null, KEY_FORCE)).booleanValue();
}
+ /** Helper to retrieve the --snapshot flag. */
+ public boolean getFlagSnapshot() {
+ return ((Boolean) getValue(null, null, KEY_SNAPSHOT)).booleanValue();
+ }
+
// -- some helpers for avd action flags
/** Helper to retrieve the --rename value for a move verb. */
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import static java.io.File.createTempFile;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.internal.avd.AvdManager;
+import com.android.sdklib.internal.project.ProjectProperties;
+import com.android.sdklib.io.FileWrapper;
+
+import java.io.File;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class AvdManagerTest extends TestCase {
+
+ private AvdManager mAvdManager;
+ private SdkManager mSdkManager;
+ private MockLog mLog;
+ private File mFakeSdk;
+ private File mAvdFolder;
+ private IAndroidTarget mTarget;
+
+ @Override
+ public void setUp() throws Exception {
+ mLog = new MockLog();
+ mFakeSdk = SdkManagerTestUtil.makeFakeSdk(createTempFile(this.getClass().getSimpleName(), null));
+ mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
+ assertNotNull("sdkManager location was invalid", mSdkManager);
+
+ mAvdManager = new AvdManager(mSdkManager, mLog);
+ mAvdFolder = new File(mFakeSdk, "avdData");
+ mTarget = mSdkManager.getTargets()[0];
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ SdkManagerTestUtil.deleteDir(mFakeSdk);
+ }
+
+ public void testCreateAvdWithoutSnapshot() {
+ mAvdManager.createAvd(
+ mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog);
+
+ assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]",
+ mLog.toString());
+ assertTrue("Expected config.ini in " + mAvdFolder,
+ new File(mAvdFolder, "config.ini").exists());
+ Map<String, String> map = ProjectProperties.parsePropertyFile(
+ new FileWrapper(mAvdFolder, "config.ini"), mLog);
+ assertEquals("HVGA", map.get("skin.name"));
+ assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path"));
+ assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1"));
+ assertEquals(null, map.get("snapshot.present"));
+ assertTrue("Expected userdata.img in " + mAvdFolder,
+ new File(mAvdFolder, "userdata.img").exists());
+ assertFalse("Expected NO snapshots.img in " + mAvdFolder,
+ new File(mAvdFolder, "snapshots.img").exists());
+ }
+
+ public void testCreateAvdWithSnapshot() {
+ mAvdManager.createAvd(
+ mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog);
+
+ assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0\n]",
+ mLog.toString());
+ assertTrue("Expected snapshots.img in " + mAvdFolder,
+ new File(mAvdFolder, "snapshots.img").exists());
+ Map<String, String> map = ProjectProperties.parsePropertyFile(
+ new FileWrapper(mAvdFolder, "config.ini"), mLog);
+ assertEquals("true", map.get("snapshot.present"));
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+
+import static java.io.File.createTempFile;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkManager;
+import com.android.sdklib.internal.avd.AvdManager;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+public class MainTest extends TestCase {
+
+ private File mFakeSdk;
+ private MockLog mLog;
+ private SdkManager mSdkManager;
+ private AvdManager mAvdManager;
+ private File mAvdFolder;
+ private IAndroidTarget mTarget;
+ private File fakeSdkDir;
+
+ @Override
+ public void setUp() throws Exception {
+ mLog = new MockLog();
+ fakeSdkDir = createTempFile(this.getClass().getSimpleName() + "_" + this.getName(), null);
+ mFakeSdk = SdkManagerTestUtil.makeFakeSdk(fakeSdkDir);
+ mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog);
+ assertNotNull("sdkManager location was invalid", mSdkManager);
+
+ mAvdManager = new AvdManager(mSdkManager, mLog);
+ mAvdFolder = new File(mFakeSdk, "avdData");
+ mTarget = mSdkManager.getTargets()[0];
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ SdkManagerTestUtil.deleteDir(mFakeSdk);
+ }
+
+ public void txestDisplayEmptyAvdList() {
+ Main main = new Main();
+ main.setLogger(mLog);
+ mLog.clear();
+ main.displayAvdList(mAvdManager);
+ assertEquals("P Available Android Virtual Devices:\n", mLog.toString());
+ }
+
+ public void testDisplayAvdListOfOneNonSnapshot() {
+ Main main = new Main();
+ main.setLogger(mLog);
+ mAvdManager.createAvd(
+ mAvdFolder, this.getName(), mTarget, null, null, null, false, false, mLog);
+ mLog.clear();
+ main.displayAvdList(mAvdManager);
+ assertEquals(
+ "[P Available Android Virtual Devices:\n"
+ + ", P Name: " + this.getName() + "\n"
+ + ", P Path: " + mAvdFolder + "\n"
+ + ", P Target: Android 0.0 (API level 0)\n"
+ + ", P Skin: HVGA\n"
+ + "]",
+ mLog.toString());
+ }
+
+ public void testDisplayAvdListOfOneSnapshot() {
+ Main main = new Main();
+ main.setLogger(mLog);
+ mAvdManager.createAvd(
+ mAvdFolder, this.getName(), mTarget, null, null, null, false, true, mLog);
+ mLog.clear();
+ main.displayAvdList(mAvdManager);
+ assertEquals(
+ "[P Available Android Virtual Devices:\n"
+ + ", P Name: " + this.getName() + "\n"
+ + ", P Path: " + mAvdFolder + "\n"
+ + ", P Target: Android 0.0 (API level 0)\n"
+ + ", P Skin: HVGA\n"
+ + ", P Snapshot: true\n"
+ + "]",
+ mLog.toString());
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import com.android.sdklib.ISdkLog;
+
+import java.util.ArrayList;
+import java.util.Formatter;
+
+public class MockLog implements ISdkLog {
+ private ArrayList<String> mMessages = new ArrayList<String>();
+
+ private void add(String code, String format, Object... args) {
+ mMessages.add(new Formatter().format(code + format, args).toString());
+ }
+
+ @Override
+ public void warning(String format, Object... args) {
+ add("W ", format, args);
+ }
+
+ @Override
+ public void printf(String format, Object... args) {
+ add("P ", format, args);
+ }
+
+ @Override
+ public void error(Throwable t, String format, Object... args) {
+ if (t != null) {
+ add("T", "%s", t.toString());
+ }
+ add("E ", format, args);
+ }
+
+ @Override
+ public String toString() {
+ return mMessages.toString();
+ }
+
+ public void clear() {
+ mMessages.clear();
+ }
+}
public class SdkCommandLineTest extends TestCase {
private StdSdkLog mLog;
-
+
/**
* A mock version of the {@link SdkCommandLine} class that does not
* exits and discards its stdout/stderr output.
public static class MockSdkCommandLine extends SdkCommandLine {
private boolean mExitCalled;
private boolean mHelpCalled;
-
+
public MockSdkCommandLine(ISdkLog logger) {
super(logger);
}
protected void exit() {
mExitCalled = true;
}
-
+
@Override
protected void stdout(String format, Object... args) {
// discard
}
-
+
@Override
protected void stderr(String format, Object... args) {
// discard
public boolean wasExitCalled() {
return mExitCalled;
}
-
+
public boolean wasHelpCalled() {
return mHelpCalled;
}
assertEquals("target", c.getDirectObject());
assertFalse(c.isVerbose());
}
+
+ public final void testCreate_Avd() {
+ MockSdkCommandLine c = new MockSdkCommandLine(mLog);
+ c.parseArgs(new String[] { "create", "avd", "-t", "android-100", "-n", "myProject" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("create", c.getVerb());
+ assertEquals("avd", c.getDirectObject());
+ assertFalse(c.getFlagSnapshot());
+ assertEquals("android-100", c.getParamTargetId());
+ assertEquals("myProject", c.getParamName());
+ assertFalse(c.isVerbose());
+ }
+
+ public final void testCreate_Avd_Snapshot() {
+ MockSdkCommandLine c = new MockSdkCommandLine(mLog);
+ c.parseArgs(new String[] { "create", "avd", "-t", "android-100", "-n", "myProject", "-a" });
+ assertFalse(c.wasHelpCalled());
+ assertFalse(c.wasExitCalled());
+ assertEquals("create", c.getVerb());
+ assertEquals("avd", c.getDirectObject());
+ assertTrue(c.getFlagSnapshot());
+ assertEquals("android-100", c.getParamTargetId());
+ assertEquals("myProject", c.getParamName());
+ assertFalse(c.isVerbose());
+ }
}
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdkmanager;
+
+import com.android.prefs.AndroidLocation;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.SdkManager;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class SdkManagerTestUtil {
+ /**
+ * Build enough of a skeleton SDK to make the tests pass.
+ *<p>
+ * Ideally this wouldn't touch the file system, but I'm not inclined to
+ * fiddle around with mock file systems just at the moment.
+ *
+ * @return an sdk manager to a fake sdk
+ * @throws IOException
+ */
+ public static File makeFakeSdk(File fakeSdk) throws IOException {
+ fakeSdk.delete();
+ fakeSdk.mkdirs();
+ AndroidLocation.resetFolder();
+ System.setProperty("user.home", fakeSdk.getAbsolutePath());
+ File addonsDir = new File(fakeSdk, SdkConstants.FD_ADDONS);
+ addonsDir.mkdir();
+ File toolsLibEmuDir = new File(fakeSdk, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator");
+ toolsLibEmuDir.mkdirs();
+ new File(toolsLibEmuDir, "snapshots.img").createNewFile();
+ File platformsDir = new File(fakeSdk, SdkConstants.FD_PLATFORMS);
+
+ // Creating a fake target here on down
+ File targetDir = new File(platformsDir, "v0_0");
+ targetDir.mkdirs();
+ new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile();
+ new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile();
+ new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile();
+ File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP);
+ FileWriter out = new FileWriter(buildProp);
+ out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n");
+ out.write(SdkManager.PROP_VERSION_SDK + "=0\n");
+ out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n");
+ out.close();
+ File imagesDir = new File(targetDir, "images");
+ imagesDir.mkdirs();
+ new File(imagesDir, "userdata.img").createNewFile();
+ File skinsDir = new File(targetDir, "skins");
+ File hvgaDir = new File(skinsDir, "HVGA");
+ hvgaDir.mkdirs();
+ return fakeSdk;
+ }
+
+ /**
+ * Recursive delete directory. Mostly for fake SDKs.
+ *
+ * @param root directory to delete
+ */
+ public static void deleteDir(File root) {
+ if (root.exists()) {
+ for (File file : root.listFiles()) {
+ if (file.isDirectory()) {
+ deleteDir(file);
+ } else {
+ file.delete();
+ }
+ }
+ root.delete();
+ }
+ }
+
+}
public final static String OS_SDK_TOOLS_LIB_FOLDER =
OS_SDK_TOOLS_FOLDER + FD_LIB + File.separator;
+ /**
+ * Path of the lib directory relative to the sdk folder, or to a platform
+ * folder. This is an OS path, ending with a separator.
+ */
+ public final static String OS_SDK_TOOLS_LIB_EMULATOR_FOLDER = OS_SDK_TOOLS_LIB_FOLDER
+ + "emulator" + File.separator;
+
/** Path of the platform tools directory relative to the sdk folder.
* This is an OS path, ending with a separator. */
public final static String OS_SDK_PLATFORM_TOOLS_FOLDER = FD_PLATFORM_TOOLS + File.separator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
* @see #AVD_INI_IMAGES_1
*/
public final static String AVD_INI_IMAGES_2 = "image.sysdir.2"; //$NON-NLS-1$
+ /**
+ * AVD/config.ini key name representing the presence of the snapshots file.
+ * This property is for UI purposes only. It is not used by the emulator.
+ *
+ * @see #SNAPSHOTS_IMG
+ */
+ public final static String AVD_INI_SNAPSHOT_PRESENT = "snapshot.present"; //$NON-NLS-1$
/**
* Pattern to match pixel-sized skin "names", e.g. "320x480".
private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$
private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$
private final static String SDCARD_IMG = "sdcard.img"; //$NON-NLS-1$
+ private final static String SNAPSHOTS_IMG = "snapshots.img"; //$NON-NLS-1$
private final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + //$NON-NLS-1$
}
/**
+ * 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 skinName,
+ String sdcard, Map<String, String> hardwareConfig, boolean removePrevious,
+ ISdkLog log) {
+ return createAvd(avdFolder, name, target, 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.
* 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 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 skinName, String sdcard, Map<String,String> hardwareConfig,
- boolean removePrevious, ISdkLog log) {
+ boolean removePrevious, boolean createSnapshot, ISdkLog log) {
if (log == null) {
throw new IllegalArgumentException("log cannot be null");
}
needCleanup = true;
return null;
}
-
- FileInputStream fis = new FileInputStream(userdataSrc);
-
File userdataDest = new File(avdFolder, USERDATA_IMG);
- FileOutputStream fos = new FileOutputStream(userdataDest);
-
- byte[] buffer = new byte[4096];
- int count;
- while ((count = fis.read(buffer)) != -1) {
- fos.write(buffer, 0, count);
- }
- fos.close();
- fis.close();
+ copyImageFile(userdataSrc, userdataDest);
// Config file.
HashMap<String, String> values = new HashMap<String, String>();
return null;
}
+ // 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);
+ values.put(AVD_INI_SNAPSHOT_PRESENT, "true");
+ }
+
// Now the skin.
if (skinName == null || skinName.length() == 0) {
skinName = target.getDefaultSkin();
return null;
}
+ /** Copy the nominated file to the given destination.
+ * @param source
+ * @param destination
+ *
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private void copyImageFile(File source, File destination)
+ throws FileNotFoundException, IOException {
+ FileInputStream fis = new FileInputStream(source);
+ FileOutputStream fos = new FileOutputStream(destination);
+
+ byte[] buffer = new byte[4096];
+ int count;
+ while ((count = fis.read(buffer)) != -1) {
+ fos.write(buffer, 0, count);
+ }
+
+ fos.close();
+ fis.close();
+ }
+
/**
* Returns the path to the target images folder as a relative path to the SDK, if the folder
* is not empty. If the image folder is empty or does not exist, <code>null</code> is returned.
}
}
+ // TODO: What about missing sdcard, skins, etc?
+
AvdStatus status;
if (avdPath == null) {
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.internal.avd.AvdManager;
-import com.android.sdklib.internal.avd.HardwareProperties;
import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
+import com.android.sdklib.internal.avd.HardwareProperties;
import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.internal.project.ProjectProperties;
import com.android.sdklib.io.FileWrapper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
-import java.util.TreeMap;
import java.util.Map.Entry;
+import java.util.TreeMap;
import java.util.regex.Matcher;
/**
private Button mBrowseSdCard;
private Button mSdCardFileRadio;
+ private Button mSnapshotCheck;
+
private Button mSkinListRadio;
private Combo mSkinCombo;
mSdCardSizeRadio.setSelection(true);
enableSdCardWidgets(true);
+ // --- snapshot group
+
+ label = new Label(parent, SWT.NONE);
+ label.setText("Snapshot:");
+ label.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
+ false, false));
+
+ final Group snapshotGroup = new Group(parent, SWT.NONE);
+ snapshotGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ snapshotGroup.setLayout(new GridLayout(3, false));
+
+ mSnapshotCheck = new Button(snapshotGroup, SWT.CHECK);
+ mSnapshotCheck.setText("Enabled");
+ mSnapshotCheck.setToolTipText(
+ "Emulator's state will be persisted between emulator executions");
+
// --- skin group
label = new Label(parent, SWT.NONE);
label.setText("Skin:");
}
}
+ 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);
mProperties.remove(AvdManager.AVD_INI_SKIN_NAME);
mProperties.remove(AvdManager.AVD_INI_SDCARD_SIZE);
mProperties.remove(AvdManager.AVD_INI_SDCARD_PATH);
+ mProperties.remove(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
mProperties.remove(AvdManager.AVD_INI_IMAGES_1);
mProperties.remove(AvdManager.AVD_INI_IMAGES_2);
mHardwareViewer.refresh();
}
boolean force = mForceCreation.getSelection();
+ boolean snapshot = mSnapshotCheck.getSelection();
boolean success = false;
AvdInfo avdInfo = mAvdManager.createAvd(
sdName,
mProperties,
force,
+ snapshot,
log);
success = avdInfo != null;
displayValue(c, "SD Card:", sdcard);
}
+ String snapshot = properties.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
+ if (snapshot != null) {
+ displayValue(c, "Snapshot:", snapshot);
+ }
+
// display other hardware
HashMap<String, String> copy = new HashMap<String, String>(properties);
// remove stuff we already displayed (or that we don't want to display)
list.add(path);
list.add("-avd"); //$NON-NLS-1$
list.add(avdName);
- if (dialog.getWipeData()) {
+ if (dialog.hasWipeData()) {
list.add("-wipe-data"); //$NON-NLS-1$
}
+ if (dialog.hasSnapshot()) {
+ if (!dialog.hasSnapshotLaunch()) {
+ list.add("-no-snapshot-load");
+ }
+ if (!dialog.hasSnapshotSave()) {
+ list.add("-no-snapshot-save");
+ }
+ }
float scale = dialog.getScale();
if (scale != 0.f) {
// do the rounding ourselves. This is because %.1f will write .4899 as .4
* <li>-wipe-data</li>
* <li>-scale</li>
* </ul>
- *
* Values are stored (in the class as static field) to be reused while the app is still running.
- * The Monitor dpi is stored in the settings if availabe.
+ * The Monitor dpi is stored in the settings if available.
*/
final class AvdStartDialog extends GridDialog {
// static field to reuse values during the same session.
private static boolean sWipeData = false;
+ private static boolean sSnapshotSave = true;
+ private static boolean sSnapshotLaunch = true;
private static int sMonitorDpi = 72; // used if there's no setting controller.
private static final Map<String, String> sSkinScaling = new HashMap<String, String>();
private String mSkinDisplay;
private boolean mEnableScaling = true;
private Label mScaleField;
+ private boolean mHasSnapshot = true;
+ private boolean mSnapshotSave = true;
+ private boolean mSnapshotLaunch = true;
+ private Button mSnapshotLaunchCheckbox;
AvdStartDialog(Shell parentShell, AvdInfo avd, String sdkLocation,
SettingsController settingsController) {
computeSkinData();
}
- public boolean getWipeData() {
+ public boolean hasWipeData() {
return mWipeData;
}
@Override
public void widgetSelected(SelectionEvent arg0) {
mWipeData = wipeButton.getSelection();
+ updateSnapshotLaunchAvailability();
+ }
+ });
+
+ Map<String, String> prop = mAvd.getProperties();
+ String snapshotPresent = prop.get(AvdManager.AVD_INI_SNAPSHOT_PRESENT);
+ mHasSnapshot = (snapshotPresent != null) && snapshotPresent.equals("true");
+
+ mSnapshotLaunchCheckbox = new Button(parent, SWT.CHECK);
+ mSnapshotLaunchCheckbox.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.horizontalSpan = 2;
+ mSnapshotLaunchCheckbox.setText("Launch from snapshot");
+ updateSnapshotLaunchAvailability();
+ mSnapshotLaunchCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent arg0) {
+ mSnapshotLaunch = mSnapshotLaunchCheckbox.getSelection();
+ }
+ });
+
+ final Button snapshotSaveCheckbox = new Button(parent, SWT.CHECK);
+ snapshotSaveCheckbox.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.horizontalSpan = 2;
+ snapshotSaveCheckbox.setText("Save to snapshot");
+ snapshotSaveCheckbox.setSelection((mSnapshotSave = sSnapshotSave) && mHasSnapshot);
+ snapshotSaveCheckbox.setEnabled(mHasSnapshot);
+ snapshotSaveCheckbox.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent arg0) {
+ mSnapshotSave = snapshotSaveCheckbox.getSelection();
}
});
// and then the wipe-data checkbox
sWipeData = mWipeData;
+ // and the snapshot handling if those checkboxes are enabled.
+ if (mHasSnapshot) {
+ sSnapshotSave = mSnapshotSave;
+ if (!mWipeData) {
+ sSnapshotLaunch = mSnapshotLaunch;
+ }
+ }
+
// finally continue with the ok action
super.okPressed();
}
return false;
}
+
+ /**
+ * @return Whether there's a snapshot file available.
+ */
+ public boolean hasSnapshot() {
+ return mHasSnapshot;
+ }
+
+ /**
+ * @return Whether to launch and load snapshot.
+ */
+ public boolean hasSnapshotLaunch() {
+ return mSnapshotLaunch && !hasWipeData();
+ }
+
+ /**
+ * @return Whether to preserve emulator state to snapshot.
+ */
+ public boolean hasSnapshotSave() {
+ return mSnapshotSave;
+ }
+
+ /**
+ * Updates snapshot launch availability, for when mWipeData value changes.
+ */
+ private void updateSnapshotLaunchAvailability() {
+ boolean enabled = !mWipeData && mHasSnapshot;
+ mSnapshotLaunchCheckbox.setEnabled(enabled);
+ mSnapshotLaunch = enabled && sSnapshotLaunch;
+ mSnapshotLaunchCheckbox.setSelection(mSnapshotLaunch);
+ }
+
}