/** * Installs the given platform and its dependencies into the given installation, if necessary. * * @param logger Logs things. * @param launcher Used to launch tasks on the remote node. * @param sdk SDK installation to install components for. * @param platform Specifies the platform to be installed. */ public static void installPlatform( PrintStream logger, Launcher launcher, AndroidSdk sdk, String platform) throws IOException, InterruptedException { // Check whether this platform is already installed if (isPlatformInstalled(logger, launcher, sdk, platform)) { return; } // Check whether we are capable of installing individual components log(logger, Messages.PLATFORM_INSTALL_REQUIRED(platform)); if (!launcher.isUnix() && platform.contains(":") && sdk.getSdkToolsVersion() < 16) { // SDK add-ons can't be installed on Windows until r16 due to http://b.android.com/18868 log(logger, Messages.SDK_ADDON_INSTALLATION_UNSUPPORTED()); return; } if (!sdk.supportsComponentInstallation()) { log(logger, Messages.SDK_COMPONENT_INSTALLATION_UNSUPPORTED()); return; } // Automated installation of ABIs (required for android-14+) is not possible until r17, so // we should warn the user that we can't automatically set up an AVD with older SDK Tools. // See http://b.android.com/21880 if ((platform.endsWith("14") || platform.endsWith("15")) && !sdk.supportsSystemImageInstallation()) { log(logger, Messages.ABI_INSTALLATION_UNSUPPORTED(), true); } // Determine which individual component(s) need to be installed for this platform List<String> components = getSdkComponentsForPlatform(logger, platform); if (components == null || components.size() == 0) { return; } // If a platform expanded to multiple dependencies (e.g. "GoogleMaps:7" -> android-7 + Maps) // then check whether we really need to install android-7, as it may already be installed if (components.size() > 1) { for (Iterator<String> it = components.iterator(); it.hasNext(); ) { String component = it.next(); if (isPlatformInstalled(logger, launcher, sdk, component)) { it.remove(); } } } // Grab the lock and attempt installation Semaphore semaphore = acquireLock(); try { installComponent(logger, launcher, sdk, components.toArray(new String[0])); } finally { semaphore.release(); } }
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); }
/** * Writes the configuration file required to opt out of SDK usage statistics gathering. * * @param launcher Used for running tasks on the remote node. * @param listener Used to access logger. */ public static void optOutOfSdkStatistics( Launcher launcher, BuildListener listener, String androidSdkHome) { Callable<Void, Exception> optOutTask = new StatsOptOutTask(androidSdkHome, listener); try { launcher.getChannel().call(optOutTask); } catch (Exception e) { log(listener.getLogger(), "SDK statistics opt-out failed.", e); } }
/** * 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 static List<String> getSdkComponentsForPlatform(PrintStream logger, String platform) { // Gather list of required components List<String> components = new ArrayList<String>(); // Check for dependent platform int dependentPlatform = -1; Matcher matcher = Pattern.compile("[0-9]{1,2}$").matcher(platform); if (matcher.find()) { String end = matcher.group(); try { dependentPlatform = Integer.parseInt(end); } catch (NumberFormatException e) { } } // Add dependent platform if (dependentPlatform > 0) { components.add(String.format("android-%s", dependentPlatform)); } // Add system image, if required // Even if a system image doesn't exist for this platform, the installer silently ignores it if (dependentPlatform >= 14) { components.add(String.format("sysimg-%s", dependentPlatform)); } // If it's a straightforward case like "android-10", we're done if (!platform.contains(":")) { return components; } // As of SDK r17-ish, we can't always map addon names directly to installable components. // But replacing display name "Google Inc." with vendor ID "google" should cover most cases platform = platform.replace("Google Inc.", "google"); String parts[] = platform.toLowerCase().split(":"); if (parts.length != 3) { log(logger, Messages.SDK_ADDON_FORMAT_UNRECOGNISED(platform)); return null; } // Determine addon name String vendor = parts[0].replaceAll("[^a-z0-9_-]+", "_").replaceAll("_+", "_").replace("_$", ""); String addon = parts[1].replaceAll("[^a-z0-9_-]+", "_").replaceAll("_+", "_").replace("_$", ""); String component = String.format("addon-%s-%s-%s", addon, vendor, parts[2]); components.add(component); return components; }
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; }
/** * 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; }