diff options
author | Xavier Ducrohet <xav@android.com> | 2011-01-12 17:49:55 -0800 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2011-01-12 17:49:55 -0800 |
commit | 6fb212b55eb3c2819c7a4f530f6beb3586b0a07e (patch) | |
tree | 648f78e7c047a4859ff63cb89fe6e08b6d359e7f | |
parent | f4df12efd1906b947b32e92489b241ac19fab2e6 (diff) | |
parent | 57aa211a4bff699d81f755c0d827b496ad7eef26 (diff) | |
download | sdk-6fb212b55eb3c2819c7a4f530f6beb3586b0a07e.zip sdk-6fb212b55eb3c2819c7a4f530f6beb3586b0a07e.tar.gz sdk-6fb212b55eb3c2819c7a4f530f6beb3586b0a07e.tar.bz2 |
Merge "Merge 74f725db from master: Add snapshot handling for AVD creation, details, and launch" into tools_r9
17 files changed, 651 insertions, 81 deletions
diff --git a/androidprefs/src/com/android/prefs/AndroidLocation.java b/androidprefs/src/com/android/prefs/AndroidLocation.java index 9a537d5..c36048a 100644 --- a/androidprefs/src/com/android/prefs/AndroidLocation.java +++ b/androidprefs/src/com/android/prefs/AndroidLocation.java @@ -82,6 +82,13 @@ public final class AndroidLocation { } /** + * 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 diff --git a/build/tools.atree b/build/tools.atree index 140a35e..c28f044 100644 --- a/build/tools.atree +++ b/build/tools.atree @@ -41,6 +41,7 @@ bin/zipalign tools/zipalign # emulator bin/emulator tools/emulator +sdk/emulator/snapshot/snapshots.img tools/lib/emulator/snapshots.img # Java-Based SDK Tools bin/ddms tools/ddms diff --git a/emulator/snapshot/snapshots.img b/emulator/snapshot/snapshots.img Binary files differnew file mode 100644 index 0000000..c05bbda --- /dev/null +++ b/emulator/snapshot/snapshots.img diff --git a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java index 73b0f6b..8f5dec4 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java +++ b/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java @@ -828,6 +828,7 @@ class CommandLineProcessor { * 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. diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index e951cc5..1fe6d97 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -19,17 +19,17 @@ package com.android.sdkmanager; 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; @@ -133,6 +133,11 @@ public class Main { }; } + /** 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. @@ -788,72 +793,84 @@ public class Main { } /** - * 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()); } @@ -972,6 +989,7 @@ public class Main { mSdkCommandLine.getParamSdCard(), hardwareConfig, removePrevious, + mSdkCommandLine.getFlagSnapshot(), mSdkLog); } catch (AndroidLocationException e) { diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java index 7062fae..5bb6c4e 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java @@ -78,6 +78,7 @@ class SdkCommandLine extends CommandLineProcessor { 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. @@ -166,6 +167,9 @@ class SdkCommandLine extends CommandLineProcessor { 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 --- @@ -408,6 +412,11 @@ class SdkCommandLine extends CommandLineProcessor { 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. */ diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java new file mode 100644 index 0000000..bb1ba75 --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java @@ -0,0 +1,90 @@ +/* + * 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")); + } +} diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java new file mode 100644 index 0000000..f6e3bbe --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java @@ -0,0 +1,100 @@ +/* + * 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()); + } +} diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MockLog.java b/sdkmanager/app/tests/com/android/sdkmanager/MockLog.java new file mode 100644 index 0000000..a8ca90a --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/MockLog.java @@ -0,0 +1,57 @@ +/* + * 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(); + } +} diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java index 8206e2a..036d2ec 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkCommandLineTest.java @@ -24,7 +24,7 @@ import junit.framework.TestCase; 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. @@ -32,7 +32,7 @@ public class SdkCommandLineTest extends TestCase { public static class MockSdkCommandLine extends SdkCommandLine { private boolean mExitCalled; private boolean mHelpCalled; - + public MockSdkCommandLine(ISdkLog logger) { super(logger); } @@ -48,12 +48,12 @@ public class SdkCommandLineTest extends TestCase { protected void exit() { mExitCalled = true; } - + @Override protected void stdout(String format, Object... args) { // discard } - + @Override protected void stderr(String format, Object... args) { // discard @@ -62,7 +62,7 @@ public class SdkCommandLineTest extends TestCase { public boolean wasExitCalled() { return mExitCalled; } - + public boolean wasHelpCalled() { return mHelpCalled; } @@ -139,4 +139,30 @@ public class SdkCommandLineTest extends TestCase { 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()); + } } diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java new file mode 100644 index 0000000..96efb5c --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java @@ -0,0 +1,88 @@ +/* + * 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(); + } + } + +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java index b2c1326..09e3502 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java @@ -250,6 +250,13 @@ public final class SdkConstants { 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; diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java index 27d849c..eba8e07 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java @@ -29,6 +29,7 @@ import com.android.sdklib.io.FileWrapper; 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; @@ -108,6 +109,13 @@ public final class AvdManager { * @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". @@ -117,6 +125,7 @@ public final class AvdManager { 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$ @@ -486,6 +495,19 @@ 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 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. @@ -496,13 +518,14 @@ public final class AvdManager { * 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"); } @@ -549,20 +572,9 @@ public final class AvdManager { 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>(); @@ -572,6 +584,22 @@ public final class AvdManager { 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(); @@ -804,6 +832,28 @@ public final class AvdManager { 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. @@ -1203,6 +1253,8 @@ public final class AvdManager { } } + // TODO: What about missing sdcard, skins, etc? + AvdStatus status; if (avdPath == null) { diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java index 6dbdca6..4411034 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java @@ -23,8 +23,8 @@ import com.android.sdklib.ISdkLog; 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; @@ -73,8 +73,8 @@ import java.io.File; 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; /** @@ -110,6 +110,8 @@ final class AvdCreationDialog extends GridDialog { private Button mBrowseSdCard; private Button mSdCardFileRadio; + private Button mSnapshotCheck; + private Button mSkinListRadio; private Combo mSkinCombo; @@ -346,6 +348,22 @@ final class AvdCreationDialog extends GridDialog { 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:"); @@ -715,6 +733,11 @@ final class AvdCreationDialog extends GridDialog { } } + 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); @@ -723,6 +746,7 @@ final class AvdCreationDialog extends GridDialog { 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(); @@ -1138,6 +1162,7 @@ final class AvdCreationDialog extends GridDialog { } boolean force = mForceCreation.getSelection(); + boolean snapshot = mSnapshotCheck.getSelection(); boolean success = false; AvdInfo avdInfo = mAvdManager.createAvd( @@ -1148,6 +1173,7 @@ final class AvdCreationDialog extends GridDialog { sdName, mProperties, force, + snapshot, log); success = avdInfo != null; diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java index a845056..409c25d 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java @@ -127,6 +127,11 @@ final class AvdDetailsDialog extends Dialog { 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) diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java index e002ef3..5e26a41 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java @@ -1037,9 +1037,17 @@ public final class AvdSelector { 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 diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java index a2a9218..77f47d1 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java @@ -56,13 +56,14 @@ import java.util.regex.Pattern; * <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>(); @@ -84,6 +85,10 @@ final class AvdStartDialog extends GridDialog { 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) { @@ -101,7 +106,7 @@ final class AvdStartDialog extends GridDialog { computeSkinData(); } - public boolean getWipeData() { + public boolean hasWipeData() { return mWipeData; } @@ -239,6 +244,36 @@ final class AvdStartDialog extends GridDialog { @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(); } }); @@ -303,6 +338,14 @@ final class AvdStartDialog extends GridDialog { // 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(); } @@ -520,4 +563,36 @@ final class AvdStartDialog extends GridDialog { 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); + } + } |