aboutsummaryrefslogtreecommitdiffstats
path: root/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java')
-rw-r--r--sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java1368
1 files changed, 0 insertions, 1368 deletions
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
deleted file mode 100644
index 0bca185..0000000
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ /dev/null
@@ -1,1368 +0,0 @@
-/*
- * 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.sdklib;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.annotations.VisibleForTesting;
-import com.android.annotations.VisibleForTesting.Visibility;
-import com.android.io.FileWrapper;
-import com.android.prefs.AndroidLocation;
-import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdklib.AndroidVersion.AndroidVersionException;
-import com.android.sdklib.ISystemImage.LocationType;
-import com.android.sdklib.internal.project.ProjectProperties;
-import com.android.sdklib.internal.repository.LocalSdkParser;
-import com.android.sdklib.internal.repository.NullTaskMonitor;
-import com.android.sdklib.internal.repository.archives.Archive;
-import com.android.sdklib.internal.repository.packages.ExtraPackage;
-import com.android.sdklib.internal.repository.packages.Package;
-import com.android.sdklib.internal.repository.packages.PlatformToolPackage;
-import com.android.sdklib.repository.PkgProps;
-import com.android.utils.ILogger;
-import com.android.utils.NullLogger;
-import com.android.utils.Pair;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.Adler32;
-
-/**
- * The SDK manager parses the SDK folder and gives access to the content.
- * @see PlatformTarget
- * @see AddOnTarget
- */
-public class SdkManager {
-
- private static final boolean DEBUG = System.getenv("SDKMAN_DEBUG") != null; //$NON-NLS-1$
-
- public final static String PROP_VERSION_SDK = "ro.build.version.sdk"; //$NON-NLS-1$
- public final static String PROP_VERSION_CODENAME = "ro.build.version.codename"; //$NON-NLS-1$
- public final static String PROP_VERSION_RELEASE = "ro.build.version.release"; //$NON-NLS-1$
-
- public final static String ADDON_NAME = "name"; //$NON-NLS-1$
- public final static String ADDON_VENDOR = "vendor"; //$NON-NLS-1$
- public final static String ADDON_API = "api"; //$NON-NLS-1$
- public final static String ADDON_DESCRIPTION = "description"; //$NON-NLS-1$
- public final static String ADDON_LIBRARIES = "libraries"; //$NON-NLS-1$
- public final static String ADDON_DEFAULT_SKIN = "skin"; //$NON-NLS-1$
- public final static String ADDON_USB_VENDOR = "usb-vendor"; //$NON-NLS-1$
- public final static String ADDON_REVISION = "revision"; //$NON-NLS-1$
- public final static String ADDON_REVISION_OLD = "version"; //$NON-NLS-1$
-
-
- private final static Pattern PATTERN_LIB_DATA = Pattern.compile(
- "^([a-zA-Z0-9._-]+\\.jar);(.*)$", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
-
- // usb ids are 16-bit hexadecimal values.
- private final static Pattern PATTERN_USB_IDS = Pattern.compile(
- "^0x[a-f0-9]{4}$", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
-
- /** List of items in the platform to check when parsing it. These paths are relative to the
- * platform root folder. */
- private final static String[] sPlatformContentList = new String[] {
- SdkConstants.FN_FRAMEWORK_LIBRARY,
- SdkConstants.FN_FRAMEWORK_AIDL,
- };
-
- /** Preference file containing the usb ids for adb */
- private final static String ADB_INI_FILE = "adb_usb.ini"; //$NON-NLS-1$
- //0--------90--------90--------90--------90--------90--------90--------90--------9
- private final static String ADB_INI_HEADER =
- "# ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT.\n" + //$NON-NLS-1$
- "# USE 'android update adb' TO GENERATE.\n" + //$NON-NLS-1$
- "# 1 USB VENDOR ID PER LINE.\n"; //$NON-NLS-1$
-
- /** The location of the SDK as an OS path */
- private final String mOsSdkPath;
- /** Valid targets that have been loaded. Can be empty but not null. */
- private IAndroidTarget[] mTargets = new IAndroidTarget[0];
- /** A map to keep information on directories to see if they change later. */
- private final Map<File, DirInfo> mTargetDirs = new HashMap<File, SdkManager.DirInfo>();
-
- /**
- * Create a new {@link SdkManager} instance.
- * External users should use {@link #createManager(String, ILogger)}.
- *
- * @param osSdkPath the location of the SDK.
- */
- @VisibleForTesting(visibility=Visibility.PRIVATE)
- protected SdkManager(String osSdkPath) {
- mOsSdkPath = osSdkPath;
- }
-
- /**
- * Creates an {@link SdkManager} for a given sdk location.
- * @param osSdkPath the location of the SDK.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- * @return the created {@link SdkManager} or null if the location is not valid.
- */
- public static SdkManager createManager(String osSdkPath, ILogger log) {
- try {
- SdkManager manager = new SdkManager(osSdkPath);
- manager.reloadSdk(log);
-
- return manager;
- } catch (IllegalArgumentException e) {
- log.error(e, "Error parsing the sdk.");
- }
-
- return null;
- }
-
- /**
- * Reloads the content of the SDK.
- *
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- */
- public void reloadSdk(ILogger log) {
- // get the current target list.
- mTargetDirs.clear();
- ArrayList<IAndroidTarget> targets = new ArrayList<IAndroidTarget>();
- loadPlatforms(mOsSdkPath, targets, mTargetDirs, log);
- loadAddOns(mOsSdkPath, targets, mTargetDirs, log);
-
- // For now replace the old list with the new one.
- // In the future we may want to keep the current objects, so that ADT doesn't have to deal
- // with new IAndroidTarget objects when a target didn't actually change.
-
- // sort the targets/add-ons
- Collections.sort(targets);
- setTargets(targets.toArray(new IAndroidTarget[targets.size()]));
-
- // load the samples, after the targets have been set.
- initializeSamplePaths(log);
- }
-
- /**
- * Checks whether any of the SDK platforms/add-ons have changed on-disk
- * since we last loaded the SDK. This does not reload the SDK nor does it
- * change the underlying targets.
- *
- * @return True if at least one directory or source.prop has changed.
- */
- public boolean hasChanged() {
- Set<File> visited = new HashSet<File>();
- boolean changed = false;
-
- File platformFolder = new File(mOsSdkPath, SdkConstants.FD_PLATFORMS);
- if (platformFolder.isDirectory()) {
- File[] platforms = platformFolder.listFiles();
- if (platforms != null) {
- for (File platform : platforms) {
- if (!platform.isDirectory()) {
- continue;
- }
- visited.add(platform);
- DirInfo dirInfo = mTargetDirs.get(platform);
- if (dirInfo == null) {
- // This is a new platform directory.
- changed = true;
- } else {
- changed = dirInfo.hasChanged();
- }
- if (changed) {
- if (DEBUG) {
- System.out.println("SDK changed due to " + //$NON-NLS-1$
- (dirInfo != null ? dirInfo.toString() : platform.getPath()));
- }
- }
- }
- }
- }
-
- File addonFolder = new File(mOsSdkPath, SdkConstants.FD_ADDONS);
-
- if (!changed && addonFolder.isDirectory()) {
- File[] addons = addonFolder.listFiles();
- if (addons != null) {
- for (File addon : addons) {
- if (!addon.isDirectory()) {
- continue;
- }
- visited.add(addon);
- DirInfo dirInfo = mTargetDirs.get(addon);
- if (dirInfo == null) {
- // This is a new add-on directory.
- changed = true;
- } else {
- changed = dirInfo.hasChanged();
- }
- if (changed) {
- if (DEBUG) {
- System.out.println("SDK changed due to " + //$NON-NLS-1$
- (dirInfo != null ? dirInfo.toString() : addon.getPath()));
- }
- }
- }
- }
- }
-
- if (!changed) {
- // Check whether some pre-existing target directories have vanished.
- for (File previousDir : mTargetDirs.keySet()) {
- if (!visited.contains(previousDir)) {
- // This directory is no longer present.
- changed = true;
- if (DEBUG) {
- System.out.println("SDK changed: " + //$NON-NLS-1$
- previousDir.getPath() + " removed"); //$NON-NLS-1$
- }
- break;
- }
- }
- }
-
- return changed;
- }
-
- /**
- * Returns the location of the SDK.
- */
- public String getLocation() {
- return mOsSdkPath;
- }
-
- /**
- * Returns the targets that are available in the SDK.
- * <p/>
- * The array can be empty but not null.
- */
- public IAndroidTarget[] getTargets() {
- return mTargets;
- }
-
- /**
- * Sets the targets that are available in the SDK.
- * <p/>
- * The array can be empty but not null.
- */
- @VisibleForTesting(visibility=Visibility.PRIVATE)
- protected void setTargets(IAndroidTarget[] targets) {
- assert targets != null;
- mTargets = targets;
- }
-
- /**
- * Returns a target from a hash that was generated by {@link IAndroidTarget#hashString()}.
- *
- * @param hash the {@link IAndroidTarget} hash string.
- * @return The matching {@link IAndroidTarget} or null.
- */
- public IAndroidTarget getTargetFromHashString(String hash) {
- if (hash != null) {
- for (IAndroidTarget target : mTargets) {
- if (hash.equals(target.hashString())) {
- return target;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Updates adb with the USB devices declared in the SDK add-ons.
- * @throws AndroidLocationException
- * @throws IOException
- */
- public void updateAdb() throws AndroidLocationException, IOException {
- FileWriter writer = null;
- try {
- // get the android prefs location to know where to write the file.
- File adbIni = new File(AndroidLocation.getFolder(), ADB_INI_FILE);
- writer = new FileWriter(adbIni);
-
- // first, put all the vendor id in an HashSet to remove duplicate.
- HashSet<Integer> set = new HashSet<Integer>();
- IAndroidTarget[] targets = getTargets();
- for (IAndroidTarget target : targets) {
- if (target.getUsbVendorId() != IAndroidTarget.NO_USB_ID) {
- set.add(target.getUsbVendorId());
- }
- }
-
- // write file header.
- writer.write(ADB_INI_HEADER);
-
- // now write the Id in a text file, one per line.
- for (Integer i : set) {
- writer.write(String.format("0x%04x\n", i)); //$NON-NLS-1$
- }
- } finally {
- if (writer != null) {
- writer.close();
- }
- }
- }
-
- /**
- * Returns the greatest {@link LayoutlibVersion} found amongst all platform
- * targets currently loaded in the SDK.
- * <p/>
- * We only started recording Layoutlib Versions recently in the platform meta data
- * so it's possible to have an SDK with many platforms loaded but no layoutlib
- * version defined.
- *
- * @return The greatest {@link LayoutlibVersion} or null if none is found.
- * @deprecated This does NOT solve the right problem and will be changed later.
- */
- @Deprecated
- public LayoutlibVersion getMaxLayoutlibVersion() {
- LayoutlibVersion maxVersion = null;
-
- for (IAndroidTarget target : getTargets()) {
- if (target instanceof PlatformTarget) {
- LayoutlibVersion lv = ((PlatformTarget) target).getLayoutlibVersion();
- if (lv != null) {
- if (maxVersion == null || lv.compareTo(maxVersion) > 0) {
- maxVersion = lv;
- }
- }
- }
- }
-
- return maxVersion;
- }
-
- /**
- * Returns a map of the <em>root samples directories</em> located in the SDK/extras packages.
- * No guarantee is made that the extras' samples directory actually contain any valid samples.
- * The only guarantee is that the root samples directory actually exists.
- * The map is { File: Samples root directory => String: Extra package display name. }
- *
- * @return A non-null possibly empty map of extra samples directories and their associated
- * extra package display name.
- */
- public @NonNull Map<File, String> getExtraSamples() {
- LocalSdkParser parser = new LocalSdkParser();
- Package[] packages = parser.parseSdk(mOsSdkPath,
- this,
- LocalSdkParser.PARSE_EXTRAS,
- new NullTaskMonitor(NullLogger.getLogger()));
-
- Map<File, String> samples = new HashMap<File, String>();
-
- for (Package pkg : packages) {
- if (pkg instanceof ExtraPackage && pkg.isLocal()) {
- // isLocal()==true implies there's a single locally-installed archive.
- assert pkg.getArchives() != null && pkg.getArchives().length == 1;
- Archive a = pkg.getArchives()[0];
- assert a != null;
- File path = new File(a.getLocalOsPath(), SdkConstants.FD_SAMPLES);
- if (path.isDirectory()) {
- samples.put(path, pkg.getListDescription());
- continue;
- }
- // Some old-style extras simply have a single "sample" directory.
- // Accept it if it contains an AndroidManifest.xml.
- path = new File(a.getLocalOsPath(), SdkConstants.FD_SAMPLE);
- if (path.isDirectory() &&
- new File(path, SdkConstants.FN_ANDROID_MANIFEST_XML).isFile()) {
- samples.put(path, pkg.getListDescription());
- }
- }
- }
-
- return samples;
- }
-
- /**
- * Returns a map of all the extras found in the <em>local</em> SDK with their major revision.
- * <p/>
- * Map keys are in the form "vendor-id/path-id". These ids uniquely identify an extra package.
- * The version is the incremental integer major revision of the package.
- *
- * @return A non-null possibly empty map of { string "vendor/path" => integer major revision }
- */
- public @NonNull Map<String, Integer> getExtrasVersions() {
- LocalSdkParser parser = new LocalSdkParser();
- Package[] packages = parser.parseSdk(mOsSdkPath,
- this,
- LocalSdkParser.PARSE_EXTRAS,
- new NullTaskMonitor(NullLogger.getLogger()));
-
- Map<String, Integer> extraVersions = new TreeMap<String, Integer>();
-
- for (Package pkg : packages) {
- if (pkg instanceof ExtraPackage && pkg.isLocal()) {
- ExtraPackage ep = (ExtraPackage) pkg;
- String vendor = ep.getVendorId();
- String path = ep.getPath();
- int majorRev = ep.getRevision().getMajor();
-
- extraVersions.put(vendor + '/' + path, majorRev);
- }
- }
-
- return extraVersions;
- }
-
- /** Returns the platform tools version if installed, null otherwise. */
- public @Nullable String getPlatformToolsVersion() {
- LocalSdkParser parser = new LocalSdkParser();
- Package[] packages = parser.parseSdk(mOsSdkPath, this, LocalSdkParser.PARSE_PLATFORM_TOOLS,
- new NullTaskMonitor(NullLogger.getLogger()));
-
- for (Package pkg : packages) {
- if (pkg instanceof PlatformToolPackage && pkg.isLocal()) {
- return pkg.getRevision().toShortString();
- }
- }
-
- return null;
- }
-
-
- // -------- private methods ----------
-
- /**
- * Loads the Platforms from the SDK.
- * Creates the "platforms" folder if necessary.
- *
- * @param sdkOsPath Location of the SDK
- * @param targets the list to fill with the platforms.
- * @param dirInfos a map to keep information on directories to see if they change later.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- * @throws RuntimeException when the "platforms" folder is missing and cannot be created.
- */
- private static void loadPlatforms(
- String sdkOsPath,
- ArrayList<IAndroidTarget> targets,
- Map<File, DirInfo> dirInfos, ILogger log) {
- File platformFolder = new File(sdkOsPath, SdkConstants.FD_PLATFORMS);
-
- if (platformFolder.isDirectory()) {
- File[] platforms = platformFolder.listFiles();
-
- for (File platform : platforms) {
- PlatformTarget target = null;
- if (platform.isDirectory()) {
- target = loadPlatform(sdkOsPath, platform, log);
- if (target != null) {
- targets.add(target);
- }
- // Remember we visited this file/directory,
- // even if we failed to load anything from it.
- dirInfos.put(platform, new DirInfo(platform));
- } else {
- log.warning("Ignoring platform '%1$s', not a folder.", platform.getName());
- }
- }
-
- return;
- }
-
- // Try to create it or complain if something else is in the way.
- if (!platformFolder.exists()) {
- if (!platformFolder.mkdir()) {
- throw new RuntimeException(
- String.format("Failed to create %1$s.",
- platformFolder.getAbsolutePath()));
- }
- } else {
- throw new RuntimeException(
- String.format("%1$s is not a folder.",
- platformFolder.getAbsolutePath()));
- }
- }
-
- /**
- * Loads a specific Platform at a given location.
- * @param sdkOsPath Location of the SDK
- * @param platformFolder the root folder of the platform.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- */
- private static PlatformTarget loadPlatform(
- String sdkOsPath,
- File platformFolder,
- ILogger log) {
- FileWrapper buildProp = new FileWrapper(platformFolder, SdkConstants.FN_BUILD_PROP);
- FileWrapper sourcePropFile = new FileWrapper(platformFolder, SdkConstants.FN_SOURCE_PROP);
-
- if (buildProp.isFile() && sourcePropFile.isFile()) {
- Map<String, String> platformProp = new HashMap<String, String>();
-
- // add all the property files
- Map<String, String> map = ProjectProperties.parsePropertyFile(buildProp, log);
- if (map != null) {
- platformProp.putAll(map);
- }
-
- map = ProjectProperties.parsePropertyFile(sourcePropFile, log);
- if (map != null) {
- platformProp.putAll(map);
- }
-
- FileWrapper sdkPropFile = new FileWrapper(platformFolder, SdkConstants.FN_SDK_PROP);
- if (sdkPropFile.isFile()) { // obsolete platforms don't have this.
- map = ProjectProperties.parsePropertyFile(sdkPropFile, log);
- if (map != null) {
- platformProp.putAll(map);
- }
- }
-
- // look for some specific values in the map.
-
- // api level
- int apiNumber;
- String stringValue = platformProp.get(PROP_VERSION_SDK);
- if (stringValue == null) {
- log.warning(
- "Ignoring platform '%1$s': %2$s is missing from '%3$s'",
- platformFolder.getName(), PROP_VERSION_SDK,
- SdkConstants.FN_BUILD_PROP);
- return null;
- } else {
- try {
- apiNumber = Integer.parseInt(stringValue);
- } catch (NumberFormatException e) {
- // looks like apiNumber does not parse to a number.
- // Ignore this platform.
- log.warning(
- "Ignoring platform '%1$s': %2$s is not a valid number in %3$s.",
- platformFolder.getName(), PROP_VERSION_SDK,
- SdkConstants.FN_BUILD_PROP);
- return null;
- }
- }
-
- // Codename must be either null or a platform codename.
- // REL means it's a release version and therefore the codename should be null.
- AndroidVersion apiVersion =
- new AndroidVersion(apiNumber, platformProp.get(PROP_VERSION_CODENAME));
-
- // version string
- String apiName = platformProp.get(PkgProps.PLATFORM_VERSION);
- if (apiName == null) {
- apiName = platformProp.get(PROP_VERSION_RELEASE);
- }
- if (apiName == null) {
- log.warning(
- "Ignoring platform '%1$s': %2$s is missing from '%3$s'",
- platformFolder.getName(), PROP_VERSION_RELEASE,
- SdkConstants.FN_BUILD_PROP);
- return null;
- }
-
- // platform rev number & layoutlib version are extracted from the source.properties
- // saved by the SDK Manager when installing the package.
-
- int revision = 1;
- LayoutlibVersion layoutlibVersion = null;
- try {
- revision = Integer.parseInt(platformProp.get(PkgProps.PKG_REVISION));
- } catch (NumberFormatException e) {
- // do nothing, we'll keep the default value of 1.
- }
-
- try {
- String propApi = platformProp.get(PkgProps.LAYOUTLIB_API);
- String propRev = platformProp.get(PkgProps.LAYOUTLIB_REV);
- int llApi = propApi == null ? LayoutlibVersion.NOT_SPECIFIED :
- Integer.parseInt(propApi);
- int llRev = propRev == null ? LayoutlibVersion.NOT_SPECIFIED :
- Integer.parseInt(propRev);
- if (llApi > LayoutlibVersion.NOT_SPECIFIED &&
- llRev >= LayoutlibVersion.NOT_SPECIFIED) {
- layoutlibVersion = new LayoutlibVersion(llApi, llRev);
- }
- } catch (NumberFormatException e) {
- // do nothing, we'll ignore the layoutlib version if it's invalid
- }
-
- // api number and name look valid, perform a few more checks
- if (checkPlatformContent(platformFolder, log) == false) {
- return null;
- }
-
- ISystemImage[] systemImages =
- getPlatformSystemImages(sdkOsPath, platformFolder, apiVersion);
-
- // create the target.
- PlatformTarget target = new PlatformTarget(
- sdkOsPath,
- platformFolder.getAbsolutePath(),
- apiVersion,
- apiName,
- revision,
- layoutlibVersion,
- systemImages,
- platformProp);
-
- // need to parse the skins.
- String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
- target.setSkins(skins);
-
- return target;
- } else {
- log.warning("Ignoring platform '%1$s': %2$s is missing.", //$NON-NLS-1$
- platformFolder.getName(),
- SdkConstants.FN_BUILD_PROP);
- }
-
- return null;
- }
-
- /**
- * Get all the system images supported by an add-on target.
- * For an add-on, we first look for sub-folders in the addon/images directory.
- * If none are found but the directory exists and is not empty, assume it's a legacy
- * arm eabi system image.
- * <p/>
- * Note that it's OK for an add-on to have no system-images at all, since it can always
- * rely on the ones from its base platform.
- *
- * @param root Root of the add-on target being loaded.
- * @return an array of ISystemImage containing all the system images for the target.
- * The list can be empty.
- */
- private static ISystemImage[] getAddonSystemImages(File root) {
- Set<ISystemImage> found = new TreeSet<ISystemImage>();
-
- root = new File(root, SdkConstants.OS_IMAGES_FOLDER);
- File[] files = root.listFiles();
- boolean hasImgFiles = false;
-
- if (files != null) {
- // Look for sub-directories
- for (File file : files) {
- if (file.isDirectory()) {
- found.add(new SystemImage(
- file,
- LocationType.IN_PLATFORM_SUBFOLDER,
- file.getName()));
- } else if (!hasImgFiles && file.isFile()) {
- if (file.getName().endsWith(".img")) { //$NON-NLS-1$
- hasImgFiles = true;
- }
- }
- }
- }
-
- if (found.size() == 0 && hasImgFiles && root.isDirectory()) {
- // We found no sub-folder system images but it looks like the top directory
- // has some img files in it. It must be a legacy ARM EABI system image folder.
- found.add(new SystemImage(
- root,
- LocationType.IN_PLATFORM_LEGACY,
- SdkConstants.ABI_ARMEABI));
- }
-
- return found.toArray(new ISystemImage[found.size()]);
- }
-
- /**
- * Get all the system images supported by a platform target.
- * For a platform, we first look in the new sdk/system-images folders then we
- * look for sub-folders in the platform/images directory and/or the one legacy
- * folder.
- * If any given API appears twice or more, the first occurrence wins.
- *
- * @param sdkOsPath The path to the SDK.
- * @param root Root of the platform target being loaded.
- * @param version API level + codename of platform being loaded.
- * @return an array of ISystemImage containing all the system images for the target.
- * The list can be empty.
- */
- private static ISystemImage[] getPlatformSystemImages(
- String sdkOsPath,
- File root,
- AndroidVersion version) {
- Set<ISystemImage> found = new TreeSet<ISystemImage>();
- Set<String> abiFound = new HashSet<String>();
-
- // First look in the SDK/system-image/platform-n/abi folders.
- // We require/enforce the system image to have a valid properties file.
- // The actual directory names are irrelevant.
- // If we find multiple occurrences of the same platform/abi, the first one read wins.
-
- File[] firstLevelFiles = new File(sdkOsPath, SdkConstants.FD_SYSTEM_IMAGES).listFiles();
- if (firstLevelFiles != null) {
- for (File firstLevel : firstLevelFiles) {
- File[] secondLevelFiles = firstLevel.listFiles();
- if (secondLevelFiles != null) {
- for (File secondLevel : secondLevelFiles) {
- try {
- File propFile = new File(secondLevel, SdkConstants.FN_SOURCE_PROP);
- Properties props = new Properties();
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(propFile);
- props.load(fis);
- } finally {
- if (fis != null) {
- fis.close();
- }
- }
-
- AndroidVersion propsVersion = new AndroidVersion(props);
- if (!propsVersion.equals(version)) {
- continue;
- }
-
- String abi = props.getProperty(PkgProps.SYS_IMG_ABI);
- if (abi != null && !abiFound.contains(abi)) {
- found.add(new SystemImage(
- secondLevel,
- LocationType.IN_SYSTEM_IMAGE,
- abi));
- abiFound.add(abi);
- }
- } catch (Exception ignore) {
- }
- }
- }
- }
- }
-
- // Then look in either the platform/images/abi or the legacy folder
- root = new File(root, SdkConstants.OS_IMAGES_FOLDER);
- File[] files = root.listFiles();
- boolean useLegacy = true;
- boolean hasImgFiles = false;
-
- if (files != null) {
- // Look for sub-directories
- for (File file : files) {
- if (file.isDirectory()) {
- useLegacy = false;
- String abi = file.getName();
- if (!abiFound.contains(abi)) {
- found.add(new SystemImage(
- file,
- LocationType.IN_PLATFORM_SUBFOLDER,
- abi));
- abiFound.add(abi);
- }
- } else if (!hasImgFiles && file.isFile()) {
- if (file.getName().endsWith(".img")) { //$NON-NLS-1$
- hasImgFiles = true;
- }
- }
- }
- }
-
- if (useLegacy && hasImgFiles && root.isDirectory() &&
- !abiFound.contains(SdkConstants.ABI_ARMEABI)) {
- // We found no sub-folder system images but it looks like the top directory
- // has some img files in it. It must be a legacy ARM EABI system image folder.
- found.add(new SystemImage(
- root,
- LocationType.IN_PLATFORM_LEGACY,
- SdkConstants.ABI_ARMEABI));
- }
-
- return found.toArray(new ISystemImage[found.size()]);
- }
-
- /**
- * Loads the Add-on from the SDK.
- * Creates the "add-ons" folder if necessary.
- *
- * @param osSdkPath Location of the SDK
- * @param targets the list to fill with the add-ons.
- * @param dirInfos a map to keep information on directories to see if they change later.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- * @throws RuntimeException when the "add-ons" folder is missing and cannot be created.
- */
- private static void loadAddOns(
- String osSdkPath,
- ArrayList<IAndroidTarget> targets,
- Map<File, DirInfo> dirInfos, ILogger log) {
- File addonFolder = new File(osSdkPath, SdkConstants.FD_ADDONS);
-
- if (addonFolder.isDirectory()) {
- File[] addons = addonFolder.listFiles();
-
- IAndroidTarget[] targetList = targets.toArray(new IAndroidTarget[targets.size()]);
-
- if (addons != null) {
- for (File addon : addons) {
- // Add-ons have to be folders. Ignore files and no need to warn about them.
- AddOnTarget target = null;
- if (addon.isDirectory()) {
- target = loadAddon(addon, targetList, log);
- if (target != null) {
- targets.add(target);
- }
- // Remember we visited this file/directory,
- // even if we failed to load anything from it.
- dirInfos.put(addon, new DirInfo(addon));
- }
- }
- }
-
- return;
- }
-
- // Try to create it or complain if something else is in the way.
- if (!addonFolder.exists()) {
- if (!addonFolder.mkdir()) {
- throw new RuntimeException(
- String.format("Failed to create %1$s.",
- addonFolder.getAbsolutePath()));
- }
- } else {
- throw new RuntimeException(
- String.format("%1$s is not a folder.",
- addonFolder.getAbsolutePath()));
- }
- }
-
- /**
- * Loads a specific Add-on at a given location.
- * @param addonDir the location of the add-on directory.
- * @param targetList The list of Android target that were already loaded from the SDK.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- */
- private static AddOnTarget loadAddon(File addonDir,
- IAndroidTarget[] targetList,
- ILogger log) {
-
- // Parse the addon properties to ensure we can load it.
- Pair<Map<String, String>, String> infos = parseAddonProperties(addonDir, targetList, log);
-
- Map<String, String> propertyMap = infos.getFirst();
- String error = infos.getSecond();
-
- if (error != null) {
- log.warning("Ignoring add-on '%1$s': %2$s", addonDir.getName(), error);
- return null;
- }
-
- // Since error==null we're not supposed to encounter any issues loading this add-on.
- try {
- assert propertyMap != null;
-
- String api = propertyMap.get(ADDON_API);
- String name = propertyMap.get(ADDON_NAME);
- String vendor = propertyMap.get(ADDON_VENDOR);
-
- assert api != null;
- assert name != null;
- assert vendor != null;
-
- PlatformTarget baseTarget = null;
-
- // Look for a platform that has a matching api level or codename.
- for (IAndroidTarget target : targetList) {
- if (target.isPlatform() && target.getVersion().equals(api)) {
- baseTarget = (PlatformTarget)target;
- break;
- }
- }
-
- assert baseTarget != null;
-
- // get the optional description
- String description = propertyMap.get(ADDON_DESCRIPTION);
-
- // get the add-on revision
- int revisionValue = 1;
- String revision = propertyMap.get(ADDON_REVISION);
- if (revision == null) {
- revision = propertyMap.get(ADDON_REVISION_OLD);
- }
- if (revision != null) {
- revisionValue = Integer.parseInt(revision);
- }
-
- // get the optional libraries
- String librariesValue = propertyMap.get(ADDON_LIBRARIES);
- Map<String, String[]> libMap = null;
-
- if (librariesValue != null) {
- librariesValue = librariesValue.trim();
- if (librariesValue.length() > 0) {
- // split in the string into the libraries name
- String[] libraries = librariesValue.split(";"); //$NON-NLS-1$
- if (libraries.length > 0) {
- libMap = new HashMap<String, String[]>();
- for (String libName : libraries) {
- libName = libName.trim();
-
- // get the library data from the properties
- String libData = propertyMap.get(libName);
-
- if (libData != null) {
- // split the jar file from the description
- Matcher m = PATTERN_LIB_DATA.matcher(libData);
- if (m.matches()) {
- libMap.put(libName, new String[] {
- m.group(1), m.group(2) });
- } else {
- log.warning(
- "Ignoring library '%1$s', property value has wrong format\n\t%2$s",
- libName, libData);
- }
- } else {
- log.warning(
- "Ignoring library '%1$s', missing property value",
- libName, libData);
- }
- }
- }
- }
- }
-
- // get the abi list.
- ISystemImage[] systemImages = getAddonSystemImages(addonDir);
-
- // check whether the add-on provides its own rendering info/library.
- boolean hasRenderingLibrary = false;
- boolean hasRenderingResources = false;
-
- File dataFolder = new File(addonDir, SdkConstants.FD_DATA);
- if (dataFolder.isDirectory()) {
- hasRenderingLibrary = new File(dataFolder, SdkConstants.FN_LAYOUTLIB_JAR).isFile();
- hasRenderingResources = new File(dataFolder, SdkConstants.FD_RES).isDirectory() &&
- new File(dataFolder, SdkConstants.FD_FONTS).isDirectory();
- }
-
- AddOnTarget target = new AddOnTarget(addonDir.getAbsolutePath(), name, vendor,
- revisionValue, description, systemImages, libMap,
- hasRenderingLibrary, hasRenderingResources,baseTarget);
-
- // need to parse the skins.
- String[] skins = parseSkinFolder(target.getPath(IAndroidTarget.SKINS));
-
- // get the default skin, or take it from the base platform if needed.
- String defaultSkin = propertyMap.get(ADDON_DEFAULT_SKIN);
- if (defaultSkin == null) {
- if (skins.length == 1) {
- defaultSkin = skins[0];
- } else {
- defaultSkin = baseTarget.getDefaultSkin();
- }
- }
-
- // get the USB ID (if available)
- int usbVendorId = convertId(propertyMap.get(ADDON_USB_VENDOR));
- if (usbVendorId != IAndroidTarget.NO_USB_ID) {
- target.setUsbVendorId(usbVendorId);
- }
-
- target.setSkins(skins, defaultSkin);
-
- return target;
- }
- catch (Exception e) {
- log.warning("Ignoring add-on '%1$s': error %2$s.",
- addonDir.getName(), e.toString());
- }
-
- return null;
- }
-
- /**
- * Parses the add-on properties and decodes any error that occurs when loading an addon.
- *
- * @param addonDir the location of the addon directory.
- * @param targetList The list of Android target that were already loaded from the SDK.
- * @param log the ILogger object receiving warning/error from the parsing. Cannot be null.
- * @return A pair with the property map and an error string. Both can be null but not at the
- * same time. If a non-null error is present then the property map must be ignored. The error
- * should be translatable as it might show up in the SdkManager UI.
- */
- public static Pair<Map<String, String>, String> parseAddonProperties(
- File addonDir,
- IAndroidTarget[] targetList,
- ILogger log) {
- Map<String, String> propertyMap = null;
- String error = null;
-
- FileWrapper addOnManifest = new FileWrapper(addonDir, SdkConstants.FN_MANIFEST_INI);
-
- do {
- if (!addOnManifest.isFile()) {
- error = String.format("File not found: %1$s", SdkConstants.FN_MANIFEST_INI);
- break;
- }
-
- propertyMap = ProjectProperties.parsePropertyFile(addOnManifest, log);
- if (propertyMap == null) {
- error = String.format("Failed to parse properties from %1$s",
- SdkConstants.FN_MANIFEST_INI);
- break;
- }
-
- // look for some specific values in the map.
- // we require name, vendor, and api
- String name = propertyMap.get(ADDON_NAME);
- if (name == null) {
- error = addonManifestWarning(ADDON_NAME);
- break;
- }
-
- String vendor = propertyMap.get(ADDON_VENDOR);
- if (vendor == null) {
- error = addonManifestWarning(ADDON_VENDOR);
- break;
- }
-
- String api = propertyMap.get(ADDON_API);
- PlatformTarget baseTarget = null;
- if (api == null) {
- error = addonManifestWarning(ADDON_API);
- break;
- }
-
- // Look for a platform that has a matching api level or codename.
- for (IAndroidTarget target : targetList) {
- if (target.isPlatform() && target.getVersion().equals(api)) {
- baseTarget = (PlatformTarget)target;
- break;
- }
- }
-
- if (baseTarget == null) {
- error = String.format("Unable to find base platform with API level '%1$s'", api);
- break;
- }
-
- // get the add-on revision
- String revision = propertyMap.get(ADDON_REVISION);
- if (revision == null) {
- revision = propertyMap.get(ADDON_REVISION_OLD);
- }
- if (revision != null) {
- try {
- Integer.parseInt(revision);
- } catch (NumberFormatException e) {
- // looks like revision does not parse to a number.
- error = String.format("%1$s is not a valid number in %2$s.",
- ADDON_REVISION, SdkConstants.FN_BUILD_PROP);
- break;
- }
- }
-
- } while(false);
-
- return Pair.of(propertyMap, error);
- }
-
- /**
- * Converts a string representation of an hexadecimal ID into an int.
- * @param value the string to convert.
- * @return the int value, or {@link IAndroidTarget#NO_USB_ID} if the convertion failed.
- */
- private static int convertId(String value) {
- if (value != null && value.length() > 0) {
- if (PATTERN_USB_IDS.matcher(value).matches()) {
- String v = value.substring(2);
- try {
- return Integer.parseInt(v, 16);
- } catch (NumberFormatException e) {
- // this shouldn't happen since we check the pattern above, but this is safer.
- // the method will return 0 below.
- }
- }
- }
-
- return IAndroidTarget.NO_USB_ID;
- }
-
- /**
- * Prepares a warning about the addon being ignored due to a missing manifest value.
- * This string will show up in the SdkManager UI.
- *
- * @param valueName The missing manifest value, for display.
- */
- private static String addonManifestWarning(String valueName) {
- return String.format("'%1$s' is missing from %2$s.",
- valueName, SdkConstants.FN_MANIFEST_INI);
- }
-
- /**
- * Checks the given platform has all the required files, and returns true if they are all
- * present.
- * <p/>This checks the presence of the following files: android.jar, framework.aidl, aapt(.exe),
- * aidl(.exe), dx(.bat), and dx.jar
- *
- * @param platform The folder containing the platform.
- * @param log Logger. Cannot be null.
- */
- private static boolean checkPlatformContent(File platform, ILogger log) {
- for (String relativePath : sPlatformContentList) {
- File f = new File(platform, relativePath);
- if (!f.exists()) {
- log.warning(
- "Ignoring platform '%1$s': %2$s is missing.", //$NON-NLS-1$
- platform.getName(), relativePath);
- return false;
- }
- }
- return true;
- }
-
-
-
- /**
- * Parses the skin folder and builds the skin list.
- * @param osPath The path of the skin root folder.
- */
- private static String[] parseSkinFolder(String osPath) {
- File skinRootFolder = new File(osPath);
-
- if (skinRootFolder.isDirectory()) {
- ArrayList<String> skinList = new ArrayList<String>();
-
- File[] files = skinRootFolder.listFiles();
-
- for (File skinFolder : files) {
- if (skinFolder.isDirectory()) {
- // check for layout file
- File layout = new File(skinFolder, SdkConstants.FN_SKIN_LAYOUT);
-
- if (layout.isFile()) {
- // for now we don't parse the content of the layout and
- // simply add the directory to the list.
- skinList.add(skinFolder.getName());
- }
- }
- }
-
- return skinList.toArray(new String[skinList.size()]);
- }
-
- return new String[0];
- }
-
- /**
- * Initialize the sample folders for all known targets (platforms and addons).
- * <p/>
- * Samples used to be located at SDK/Target/samples. We then changed this to
- * have a separate SDK/samples/samples-API directory. This parses either directories
- * and sets the targets' sample path accordingly.
- *
- * @param log Logger. Cannot be null.
- */
- private void initializeSamplePaths(ILogger log) {
- File sampleFolder = new File(mOsSdkPath, SdkConstants.FD_SAMPLES);
- if (sampleFolder.isDirectory()) {
- File[] platforms = sampleFolder.listFiles();
-
- for (File platform : platforms) {
- if (platform.isDirectory()) {
- // load the source.properties file and get an AndroidVersion object from it.
- AndroidVersion version = getSamplesVersion(platform, log);
-
- if (version != null) {
- // locate the platform matching this version
- for (IAndroidTarget target : mTargets) {
- if (target.isPlatform() && target.getVersion().equals(version)) {
- ((PlatformTarget)target).setSamplesPath(platform.getAbsolutePath());
- break;
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * Returns the {@link AndroidVersion} of the sample in the given folder.
- *
- * @param folder The sample's folder.
- * @param log Logger for errors. Cannot be null.
- * @return An {@link AndroidVersion} or null on error.
- */
- private AndroidVersion getSamplesVersion(File folder, ILogger log) {
- File sourceProp = new File(folder, SdkConstants.FN_SOURCE_PROP);
- try {
- Properties p = new Properties();
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(sourceProp);
- p.load(fis);
- } finally {
- if (fis != null) {
- fis.close();
- }
- }
-
- return new AndroidVersion(p);
- } catch (FileNotFoundException e) {
- log.warning("Ignoring sample '%1$s': does not contain %2$s.", //$NON-NLS-1$
- folder.getName(), SdkConstants.FN_SOURCE_PROP);
- } catch (IOException e) {
- log.warning("Ignoring sample '%1$s': failed reading %2$s.", //$NON-NLS-1$
- folder.getName(), SdkConstants.FN_SOURCE_PROP);
- } catch (AndroidVersionException e) {
- log.warning("Ignoring sample '%1$s': no android version found in %2$s.", //$NON-NLS-1$
- folder.getName(), SdkConstants.FN_SOURCE_PROP);
- }
-
- return null;
- }
-
- // -------------
-
- public static class LayoutlibVersion implements Comparable<LayoutlibVersion> {
- private final int mApi;
- private final int mRevision;
-
- public static final int NOT_SPECIFIED = 0;
-
- public LayoutlibVersion(int api, int revision) {
- mApi = api;
- mRevision = revision;
- }
-
- public int getApi() {
- return mApi;
- }
-
- public int getRevision() {
- return mRevision;
- }
-
- @Override
- public int compareTo(LayoutlibVersion rhs) {
- boolean useRev = this.mRevision > NOT_SPECIFIED && rhs.mRevision > NOT_SPECIFIED;
- int lhsValue = (this.mApi << 16) + (useRev ? this.mRevision : 0);
- int rhsValue = (rhs.mApi << 16) + (useRev ? rhs.mRevision : 0);
- return lhsValue - rhsValue;
- }
- }
-
- // -------------
-
- private static class DirInfo {
- private final @NonNull File mDir;
- private final long mDirModifiedTS;
- private final long mPropsModifedTS;
- private final long mPropsChecksum;
-
- /**
- * Creates a new immutable {@link DirInfo}.
- *
- * @param dir The platform/addon directory of the target. It should be a directory.
- */
- public DirInfo(@NonNull File dir) {
- mDir = dir;
- mDirModifiedTS = dir.lastModified();
-
- // Capture some info about the source.properties file if it exists.
- // We use propsModifedTS == 0 to mean there is no props file.
- long propsChecksum = 0;
- long propsModifedTS = 0;
- File props = new File(dir, SdkConstants.FN_SOURCE_PROP);
- if (props.isFile()) {
- propsModifedTS = props.lastModified();
- propsChecksum = getFileChecksum(props);
- }
- mPropsModifedTS = propsModifedTS;
- mPropsChecksum = propsChecksum;
- }
-
- /**
- * Checks whether the directory/source.properties attributes have changed.
- *
- * @return True if the directory modified timestampd or
- * its source.property files have changed.
- */
- public boolean hasChanged() {
- // Does platform directory still exist?
- if (!mDir.isDirectory()) {
- return true;
- }
- // Has platform directory modified-timestamp changed?
- if (mDirModifiedTS != mDir.lastModified()) {
- return true;
- }
-
- File props = new File(mDir, SdkConstants.FN_SOURCE_PROP);
-
- // The directory did not have a props file if target was null or
- // if mPropsModifedTS is 0.
- boolean hadProps = mPropsModifedTS != 0;
-
- // Was there a props file and it vanished, or there wasn't and there's one now?
- if (hadProps != props.isFile()) {
- return true;
- }
-
- if (hadProps) {
- // Has source.props file modified-timestampd changed?
- if (mPropsModifedTS != props.lastModified()) {
- return true;
- }
- // Had the content of source.props changed?
- if (mPropsChecksum != getFileChecksum(props)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Computes an adler32 checksum (source.props are small files, so this
- * should be OK with an acceptable collision rate.)
- */
- private static long getFileChecksum(File file) {
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(file);
- Adler32 a = new Adler32();
- byte[] buf = new byte[1024];
- int n;
- while ((n = fis.read(buf)) > 0) {
- a.update(buf, 0, n);
- }
- return a.getValue();
- } catch (Exception ignore) {
- } finally {
- try {
- if (fis != null) {
- fis.close();
- }
- } catch(Exception ignore) {};
- }
- return 0;
- }
-
- /** Returns a visual representation of this object for debugging. */
- @Override
- public String toString() {
- String s = String.format("<DirInfo %1$s TS=%2$d", mDir, mDirModifiedTS); //$NON-NLS-1$
- if (mPropsModifedTS != 0) {
- s += String.format(" | Props TS=%1$d, Chksum=%2$s", //$NON-NLS-1$
- mPropsModifedTS, mPropsChecksum);
- }
- return s + ">"; //$NON-NLS-1$
- }
- }
-}