private static AndroidSdk doInstall( Launcher launcher, BuildListener listener, String androidSdkHome) throws SdkInstallationException, IOException, InterruptedException { // We should install the SDK on the current build machine Node node = Computer.currentComputer().getNode(); // Install the SDK if required String androidHome; try { androidHome = installBasicSdk(listener, node).getRemote(); } catch (IOException e) { throw new SdkInstallationException(Messages.SDK_DOWNLOAD_FAILED(), e); } catch (SdkUnavailableException e) { throw new SdkInstallationException(Messages.SDK_DOWNLOAD_FAILED(), e); } // Check whether we need to install the SDK components if (!isSdkInstallComplete(node, androidHome)) { PrintStream logger = listener.getLogger(); log(logger, Messages.INSTALLING_REQUIRED_COMPONENTS()); AndroidSdk sdk = new AndroidSdk(androidHome, androidSdkHome); installComponent(logger, launcher, sdk, "platform-tool", "tool"); // If we made it this far, confirm completion by writing our our metadata file getInstallationInfoFilename(node).write(String.valueOf(SDK_VERSION), "UTF-8"); // As this SDK will not be used manually, opt out of the stats gathering; // this also prevents the opt-in dialog from popping up during execution optOutOfSdkStatistics(launcher, listener, androidSdkHome); } // Create an SDK object now that all the components exist return Utils.getAndroidSdk(launcher, androidHome, androidSdkHome); }
private static boolean isPlatformInstalled( PrintStream logger, Launcher launcher, AndroidSdk sdk, String platform) throws IOException, InterruptedException { ByteArrayOutputStream targetList = new ByteArrayOutputStream(); // Preferably we'd use the "--compact" flag here, but it wasn't added until r12 Utils.runAndroidTool(launcher, targetList, logger, sdk, Tool.ANDROID, "list target", null); return targetList.toString().contains('"' + platform + '"'); }
/** * Determines whether a snapshot image has already been created for this emulator. * * @throws IOException If execution of the emulator command fails. * @throws InterruptedException If execution of the emulator command is interrupted. */ public boolean hasExistingSnapshot(Launcher launcher, AndroidSdk androidSdk) throws IOException, InterruptedException { final PrintStream logger = launcher.getListener().getLogger(); // List available snapshots for this emulator ByteArrayOutputStream listOutput = new ByteArrayOutputStream(); String args = String.format("-snapshot-list -no-window -avd %s", getAvdName()); Utils.runAndroidTool(launcher, listOutput, logger, androidSdk, Tool.EMULATOR, args, null); // Check whether a Jenkins snapshot was listed in the output return Pattern.compile(Constants.REGEX_SNAPSHOT).matcher(listOutput.toString()).find(); }
/** * Installs the given SDK component(s) into the given installation. * * @param logger Logs things. * @param launcher Used to launch tasks on the remote node. * @param sdk Root of the SDK installation to install components for. * @param components Name of the component(s) to install. */ private static void installComponent( PrintStream logger, Launcher launcher, AndroidSdk sdk, String... components) throws IOException, InterruptedException { String proxySettings = getProxySettings(); String list = StringUtils.join(components, ','); log(logger, Messages.INSTALLING_SDK_COMPONENTS(list.toString())); String all = sdk.getSdkToolsVersion() < 17 ? "-o" : "-a"; String upgradeArgs = String.format("update sdk -u %s %s -t %s", all, proxySettings, list); Utils.runAndroidTool(launcher, logger, logger, sdk, Tool.ANDROID, upgradeArgs, null); }
private boolean createSdCard(File homeDir) { // Build command: mksdcard 32M /home/foo/.android/avd/whatever.avd/sdcard.img ArgumentListBuilder builder = Utils.getToolCommand(androidSdk, !Functions.isWindows(), Tool.MKSDCARD, null); builder.add(sdCardSize); builder.add(new File(getAvdDirectory(homeDir), "sdcard.img")); // Run! try { ProcessBuilder procBuilder = new ProcessBuilder(builder.toList()); if (androidSdkHome != null) { procBuilder.environment().put("ANDROID_SDK_HOME", androidSdkHome); } procBuilder.start().waitFor(); } catch (InterruptedException ex) { return false; } catch (IOException ex) { return false; } return true; }
public Void call() throws Exception { if (logger == null) { logger = listener.getLogger(); } final File homeDir = Utils.getHomeDirectory(androidSdkHome); final File androidDir = new File(homeDir, ".android"); androidDir.mkdirs(); File configFile = new File(androidDir, "ddms.cfg"); PrintWriter out; try { out = new PrintWriter(configFile); out.println("pingOptIn=false"); out.flush(); out.close(); } catch (FileNotFoundException e) { log(logger, "Failed to automatically opt out of SDK statistics gathering.", e); } return null; }
public Boolean call() throws Exception { if (logger == null) { logger = listener.getLogger(); } // Check whether the AVD exists final File homeDir = Utils.getHomeDirectory(androidSdkHome); final File avdDirectory = getAvdDirectory(homeDir); final boolean emulatorExists = avdDirectory.exists(); if (!emulatorExists) { AndroidEmulator.log(logger, Messages.AVD_DIRECTORY_NOT_FOUND(avdDirectory)); return false; } // Recursively delete the contents new FilePath(avdDirectory).deleteRecursive(); // Delete the metadata file getAvdMetadataFile().delete(); // Success! return true; }
public Void call() throws IOException { if (logger == null) { logger = listener.getLogger(); } final File homeDir = Utils.getHomeDirectory(androidSdkHome); // Parse the AVD's config Map<String, String> configValues; configValues = parseAvdConfigFile(homeDir); // Insert any hardware properties we want to override AndroidEmulator.log(logger, Messages.SETTING_HARDWARE_PROPERTIES()); for (HardwareProperty prop : hardwareProperties) { AndroidEmulator.log(logger, String.format("%s: %s", prop.key, prop.value), true); configValues.put(prop.key, prop.value); } // Update config file writeAvdConfigFile(homeDir, configValues); return null; }
/** * Downloads and extracts the basic Android SDK on a given Node, if it hasn't already been done. * * @param node Node to install the SDK on. * @return Path where the SDK is installed, regardless of whether it was installed right now. * @throws SdkUnavailableException If the Android SDK is not available on this platform. */ private static FilePath installBasicSdk(final BuildListener listener, Node node) throws SdkUnavailableException, IOException, InterruptedException { // Locate where the SDK should be installed to on this node final FilePath installDir = Utils.getSdkInstallDirectory(node); // Get the OS-specific download URL for the SDK AndroidInstaller installer = AndroidInstaller.fromNode(node); final URL downloadUrl = installer.getUrl(SDK_VERSION); // Download the SDK, if required boolean wasNowInstalled = installDir.act( new FileCallable<Boolean>() { public Boolean invoke(File f, VirtualChannel channel) throws InterruptedException, IOException { String msg = Messages.DOWNLOADING_SDK_FROM(downloadUrl); return installDir.installIfNecessaryFrom(downloadUrl, listener, msg); } private static final long serialVersionUID = 1L; }); if (wasNowInstalled) { // If the SDK was required, pull files up from the intermediate directory installDir.listDirectories().get(0).moveAllChildrenTo(installDir); // Java's ZipEntry doesn't preserve the executable bit... if (installer == AndroidInstaller.MAC_OS_X) { setPermissions(installDir.child("tools")); } // Success! log(listener.getLogger(), Messages.BASE_SDK_INSTALLED()); } return installDir; }
/** * Sends a user command to the running emulator via its telnet interface.<br> * Execution will be cancelled if it takes longer than timeout ms. * * @param command The command to execute on the emulator's telnet interface. * @param timeout The command's timeout, in ms. * @return Whether sending the command succeeded. */ public boolean sendCommand(final String command, int timeout) { return Utils.sendEmulatorCommand(launcher, logger(), userPort, command, timeout); }
/** * Generates a ready-to-use ProcStarter for one of the Android SDK tools, based on the current * context. * * @param tool The Android tool to run. * @param args Any extra arguments for the command. * @return A ready ProcStarter * @throws IOException * @throws InterruptedException */ public ProcStarter getToolProcStarter(Tool tool, String args) throws IOException, InterruptedException { return getProcStarter(Utils.getToolCommand(sdk, launcher.isUnix(), tool, args)); }
/** * Generates a ready-to-use ArgumentListBuilder for one of the Android SDK tools, based on the * current context. * * @param tool The Android tool to run. * @param args Any extra arguments for the command. * @return Arguments including the full path to the SDK and any extra Windows stuff required. */ public ArgumentListBuilder getToolCommand(Tool tool, String args) { return Utils.getToolCommand(sdk, launcher.isUnix(), tool, args); }
public Boolean call() throws AndroidEmulatorException { if (logger == null) { logger = listener.getLogger(); } final File homeDir = Utils.getHomeDirectory(androidSdk.getSdkHome()); final File avdDirectory = getAvdDirectory(homeDir); final boolean emulatorExists = getAvdConfigFile(homeDir).exists(); // Can't do anything if a named emulator doesn't exist if (isNamedEmulator() && !emulatorExists) { throw new EmulatorDiscoveryException(Messages.AVD_DOES_NOT_EXIST(avdName, avdDirectory)); } // Check whether AVD needs to be created boolean createSdCard = false; boolean createSnapshot = false; File snapshotsFile = new File(getAvdDirectory(homeDir), "snapshots.img"); if (emulatorExists) { // AVD exists: check whether there's anything still to be set up File sdCardFile = new File(getAvdDirectory(homeDir), "sdcard.img"); boolean sdCardRequired = getSdCardSize() != null; // Check if anything needs to be done for snapshot-enabled builds if (shouldUseSnapshots() && androidSdk.supportsSnapshots()) { if (!snapshotsFile.exists()) { createSnapshot = true; } // We should ensure that we start out with a clean SD card for the build if (sdCardRequired && sdCardFile.exists()) { sdCardFile.delete(); } } // Flag that we need to generate an SD card, if there isn't one existing if (sdCardRequired && !sdCardFile.exists()) { createSdCard = true; } // If everything is ready, then return if (!createSdCard && !createSnapshot) { return true; } } else { AndroidEmulator.log(logger, Messages.CREATING_AVD(avdDirectory)); } // We can't continue if we don't know where to find emulator images or tools if (!androidSdk.hasKnownRoot()) { throw new EmulatorCreationException(Messages.SDK_NOT_SPECIFIED()); } final File sdkRoot = new File(androidSdk.getSdkRoot()); if (!sdkRoot.exists()) { throw new EmulatorCreationException(Messages.SDK_NOT_FOUND(androidSdk.getSdkRoot())); } // If we need to initialise snapshot support for an existing emulator, do so if (createSnapshot) { // Copy the snapshots file into place File snapshotDir = new File(sdkRoot, "tools/lib/emulator"); Util.copyFile(new File(snapshotDir, "snapshots.img"), snapshotsFile); // Update the AVD config file mark snapshots as enabled Map<String, String> configValues; try { configValues = parseAvdConfigFile(homeDir); configValues.put("snapshot.present", "true"); writeAvdConfigFile(homeDir, configValues); } catch (IOException e) { throw new EmulatorCreationException(Messages.AVD_CONFIG_NOT_READABLE(), e); } } // If we need create an SD card for an existing emulator, do so if (createSdCard) { AndroidEmulator.log(logger, Messages.ADDING_SD_CARD(sdCardSize, getAvdName())); if (!createSdCard(homeDir)) { throw new EmulatorCreationException(Messages.SD_CARD_CREATION_FAILED()); } // Update the AVD config file Map<String, String> configValues; try { configValues = parseAvdConfigFile(homeDir); configValues.put("sdcard.size", sdCardSize); writeAvdConfigFile(homeDir, configValues); } catch (IOException e) { throw new EmulatorCreationException(Messages.AVD_CONFIG_NOT_READABLE(), e); } } // Return if everything is now ready for use if (emulatorExists) { return true; } // Build up basic arguments to `android` command final StringBuilder args = new StringBuilder(100); args.append("create avd "); // Overwrite any existing files args.append("-f "); // Initialise snapshot support, regardless of whether we will actually use it if (androidSdk.supportsSnapshots()) { args.append("-a "); } if (sdCardSize != null) { args.append("-c "); args.append(sdCardSize); args.append(" "); } args.append("-s "); args.append(screenResolution.getSkinName()); args.append(" -n "); args.append(getAvdName()); boolean isUnix = !Functions.isWindows(); ArgumentListBuilder builder = Utils.getToolCommand(androidSdk, isUnix, Tool.ANDROID, args.toString()); // Tack on quoted platform name at the end, since it can be anything builder.add("-t"); builder.add(osVersion.getTargetName()); if (targetAbi != null) { builder.add("--abi"); builder.add(targetAbi); } // Log command line used, for info AndroidEmulator.log(logger, builder.toStringWithQuote()); // Run! boolean avdCreated = false; final Process process; try { ProcessBuilder procBuilder = new ProcessBuilder(builder.toList()); if (androidSdk.hasKnownHome()) { procBuilder.environment().put("ANDROID_SDK_HOME", androidSdk.getSdkHome()); } process = procBuilder.start(); } catch (IOException ex) { throw new EmulatorCreationException(Messages.AVD_CREATION_FAILED()); } // Redirect process's stderr to a stream, for logging purposes ByteArrayOutputStream stderr = new ByteArrayOutputStream(); ByteArrayOutputStream stdout = new ByteArrayOutputStream(); new StreamCopyThread("", process.getErrorStream(), stderr).start(); // Command may prompt us whether we want to further customise the AVD. // Just "press" Enter to continue with the selected target's defaults. try { boolean processAlive = true; // Block until the command outputs something (or process ends) final PushbackInputStream in = new PushbackInputStream(process.getInputStream(), 10); int len = in.read(); if (len == -1) { // Check whether the process has exited badly, as sometimes no output is valid. // e.g. When creating an AVD with Google APIs, no user input is requested. if (process.waitFor() != 0) { AndroidEmulator.log(logger, Messages.AVD_CREATION_FAILED()); AndroidEmulator.log(logger, stderr.toString(), true); throw new EmulatorCreationException(Messages.AVD_CREATION_FAILED()); } processAlive = false; } in.unread(len); // Write CRLF, if required if (processAlive) { final OutputStream stream = process.getOutputStream(); stream.write('\r'); stream.write('\n'); stream.flush(); stream.close(); } // read the rest of stdout (for debugging purposes) Util.copyStream(in, stdout); in.close(); // Wait for happy ending if (process.waitFor() == 0) { // Do a sanity check to ensure the AVD was really created avdCreated = getAvdConfigFile(homeDir).exists(); } } catch (IOException e) { throw new EmulatorCreationException(Messages.AVD_CREATION_ABORTED(), e); } catch (InterruptedException e) { throw new EmulatorCreationException(Messages.AVD_CREATION_INTERRUPTED(), e); } finally { process.destroy(); } // For reasons unknown, the return code may not be correctly reported on Windows. // So check whether stderr contains failure info (useful for other platforms too). String errOutput = stderr.toString(); String output = stdout.toString(); if (errOutput.contains("list targets")) { AndroidEmulator.log(logger, Messages.INVALID_AVD_TARGET(osVersion.getTargetName())); avdCreated = false; errOutput = null; } else if (errOutput.contains("more than one ABI")) { AndroidEmulator.log( logger, Messages.MORE_THAN_ONE_ABI(osVersion.getTargetName(), output), true); avdCreated = false; errOutput = null; } // Check everything went ok if (!avdCreated) { if (errOutput != null && errOutput.length() != 0) { AndroidEmulator.log(logger, stderr.toString(), true); } throw new EmulatorCreationException(Messages.AVD_CREATION_FAILED()); } // Done! return false; }
private Map<String, String> parseAvdConfigFile(File homeDir) throws IOException { File configFile = getAvdConfigFile(homeDir); return Utils.parseConfigFile(configFile); }
public File getAvdMetadataFile() { final File homeDir = Utils.getHomeDirectory(androidSdkHome); return new File(getAvdHome(homeDir), getAvdName() + ".ini"); }
/** Gets the path of our installation metadata file for the given node. */ private static final FilePath getInstallationInfoFilename(Node node) { return Utils.getSdkInstallDirectory(node).child(SDK_INFO_FILENAME); }