Skip to content
Browse files
…upport for Android Emulator 2.0

Added support for the 'new' Android Emulator 2.0, which got support in SDK Tools v25 but
was lacking some functionality the plugin relies on. Since SDK Tools v26 the new emulator
engine supports all needed flags and parameters.

The plugin creates AVDs and starts the emulator in legacy format or in the new format,
dependent of which SDK Tools version is installed (<= 25 legacy, >= 26 new format).

A new configuration option was added ('Device definition') which is needed (to avoid
user input) on AVD creation. It is used to define a specific device ('avdmanager list device'),
but it is only respected if SDK Tools >= 26 are used.

Added a helper class AndroidPlatformVersions which holds the current data of platforms,
which was moved from AndroidPlatform. Additional AndroidPlatform got refactored to
handle and retrieve more information needed for the emulator (package path) and
for more convenient access to vendor and api information of the used platform.
  • Loading branch information
mmusenbr committed Jul 29, 2017
1 parent 8423c82 commit 3b9e5b948f4c542301aa048f4147189f7e7856d7
Show file tree
Hide file tree
Showing 15 changed files with 758 additions and 176 deletions.
@@ -44,7 +44,6 @@
@@ -80,6 +79,7 @@ public class AndroidEmulator extends BuildWrapper implements Serializable {
@Exported public final String screenResolution;
@Exported public final String deviceLocale;
@Exported public final String targetAbi;
@Exported public final String deviceDefinition;
@Exported public final String sdCardSize;
@Exported public final String avdNameSuffix;
@Exported public final HardwareProperty[] hardwareProperties;
@@ -102,7 +102,8 @@ public AndroidEmulator(String avdName, String osVersion, String screenDensity,
String screenResolution, String deviceLocale, String sdCardSize,
HardwareProperty[] hardwareProperties, boolean wipeData, boolean showWindow,
boolean useSnapshots, boolean deleteAfterBuild, int startupDelay, int startupTimeout,
String commandLineOptions, String targetAbi, String executable, String avdNameSuffix) {
String commandLineOptions, String targetAbi, String deviceDefinition,
String executable, String avdNameSuffix) {
this.avdName = avdName;
this.osVersion = osVersion;
this.screenDensity = screenDensity;
@@ -119,6 +120,7 @@ public AndroidEmulator(String avdName, String osVersion, String screenDensity,
this.startupTimeout = Math.abs(startupTimeout);
this.commandLineOptions = commandLineOptions;
this.targetAbi = targetAbi;
this.deviceDefinition = deviceDefinition;
this.avdNameSuffix = avdNameSuffix;

@@ -161,10 +163,11 @@ public String getConfigHash(Node node, Combination combination) {
String screenResolution = Utils.expandVariables(envVars, combination, this.screenResolution);
String deviceLocale = Utils.expandVariables(envVars, combination, this.deviceLocale);
String targetAbi = Utils.expandVariables(envVars, combination, this.targetAbi);
String deviceDefinition = Utils.expandVariables(envVars, combination, this.deviceDefinition);
String avdNameSuffix = Utils.expandVariables(envVars, combination, this.avdNameSuffix);

return EmulatorConfig.getAvdName(avdName, osVersion, screenDensity, screenResolution,
deviceLocale, targetAbi, avdNameSuffix);
deviceLocale, targetAbi, deviceDefinition, avdNameSuffix);

@@ -191,6 +194,7 @@ public Environment setUp(AbstractBuild build, final Launcher launcher, BuildList
sdCardSize = sdCardSize.toUpperCase().replaceAll("[ B]", "");
String targetAbi = Utils.expandVariables(envVars, buildVars, this.targetAbi);
String deviceDefinition = Utils.expandVariables(envVars, buildVars, this.deviceDefinition);
String avdNameSuffix = Utils.expandVariables(envVars, buildVars, this.avdNameSuffix);

// Expand macros within hardware property values
@@ -229,7 +233,7 @@ public Environment setUp(AbstractBuild build, final Launcher launcher, BuildList
try {
emuConfig = EmulatorConfig.create(avdName, osVersion, screenDensity,
screenResolution, deviceLocale, sdCardSize, wipeData, showWindow, useSnapshots,
commandLineOptions, targetAbi, androidSdkHome, executable, avdNameSuffix);
commandLineOptions, targetAbi, deviceDefinition, androidSdkHome, executable, avdNameSuffix);
} catch (IllegalArgumentException e) {
log(logger, Messages.EMULATOR_CONFIGURATION_BAD(e.getLocalizedMessage()));
@@ -337,8 +341,7 @@ private Environment doSetUp(final AbstractBuild<?, ?> build, final Launcher laun

// Compile complete command for starting emulator
final String emulatorArgs = emuConfig.getCommandArguments(snapshotState,
androidSdk.supportsSnapshots(), androidSdk.supportsEmulatorEngineFlag(),
final String emulatorArgs = emuConfig.getCommandArguments(snapshotState, androidSdk,
emu.userPort(), emu.adbPort(), emu.getEmulatorCallbackPort(),

@@ -791,6 +794,7 @@ public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws
String deviceLocale = null;
String sdCardSize = null;
String targetAbi = null;
String deviceDefinition = null;
List<HardwareProperty> hardware = new ArrayList<HardwareProperty>();
boolean wipeData = false;
boolean showWindow = true;
@@ -814,6 +818,7 @@ public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws
sdCardSize = Util.fixEmptyAndTrim(emulatorData.getString("sdCardSize"));
hardware = req.bindJSONToList(HardwareProperty.class, emulatorData.get("hardwareProperties"));
targetAbi = Util.fixEmptyAndTrim(emulatorData.getString("targetAbi"));
deviceDefinition = Util.fixEmptyAndTrim(emulatorData.getString("deviceDefinition"));
avdNameSuffix = Util.fixEmptyAndTrim(emulatorData.getString("avdNameSuffix"));
wipeData = formData.getBoolean("wipeData");
@@ -833,7 +838,7 @@ public BuildWrapper newInstance(StaplerRequest req, JSONObject formData) throws
return new AndroidEmulator(avdName, osVersion, screenDensity, screenResolution,
deviceLocale, sdCardSize, hardware.toArray(new HardwareProperty[0]), wipeData,
showWindow, useSnapshots, deleteAfterBuild, startupDelay, startupTimeout, commandLineOptions,
targetAbi, executable, avdNameSuffix);
targetAbi, deviceDefinition, executable, avdNameSuffix);

@@ -847,8 +852,8 @@ public boolean isApplicable(AbstractProject<?, ?> item) {

/** Used in config.jelly: Lists the OS versions available. */
public AndroidPlatform[] getAndroidVersions() {
return AndroidPlatform.ALL;
public String[] getAndroidVersions() {
return AndroidPlatform.getAllPossibleVersionNames();

/** Used in config.jelly: Lists the screen densities available. */
@@ -1010,6 +1015,18 @@ private ValidationResult checkTargetAbi(String value, boolean allowVariables) {
return ValidationResult.error(Messages.INVALID_TARGET_ABI());

public FormValidation doCheckDeviceDefinition(@QueryParameter String value) {
return checkDeviceDefinition(value, true).getFormValidation();

private ValidationResult checkDeviceDefinition(String value, boolean allowVariables) {
if (value == null || "".equals(value.trim())) {
return ValidationResult.warning(Messages.EMPTY_DEVICE_DEFINITION());

return ValidationResult.ok();

public FormValidation doCheckExecutable(@QueryParameter String value) {
if (value == null || "".equals(value.trim())) {
return ValidationResult.ok().getFormValidation();

0 comments on commit 3b9e5b9

Please sign in to comment.