diff options
Diffstat (limited to 'sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java')
-rw-r--r-- | sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java | 1368 |
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$ - } - } -} |