| /* |
| * Copyright (C) 2009 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.internal.repository.updater; |
| |
| import com.android.sdklib.AndroidVersion; |
| import com.android.sdklib.internal.repository.ITask; |
| import com.android.sdklib.internal.repository.ITaskMonitor; |
| import com.android.sdklib.internal.repository.archives.Archive; |
| import com.android.sdklib.internal.repository.packages.AddonPackage; |
| import com.android.sdklib.internal.repository.packages.BuildToolPackage; |
| import com.android.sdklib.internal.repository.packages.DocPackage; |
| import com.android.sdklib.internal.repository.packages.ExtraPackage; |
| import com.android.sdklib.internal.repository.packages.IAndroidVersionProvider; |
| import com.android.sdklib.internal.repository.packages.IExactApiLevelDependency; |
| import com.android.sdklib.internal.repository.packages.IMinApiLevelDependency; |
| import com.android.sdklib.internal.repository.packages.IMinPlatformToolsDependency; |
| import com.android.sdklib.internal.repository.packages.IMinToolsDependency; |
| import com.android.sdklib.internal.repository.packages.IPlatformDependency; |
| import com.android.sdklib.internal.repository.packages.MinToolsPackage; |
| import com.android.sdklib.internal.repository.packages.Package; |
| import com.android.sdklib.internal.repository.packages.Package.UpdateInfo; |
| import com.android.sdklib.internal.repository.packages.PlatformPackage; |
| import com.android.sdklib.internal.repository.packages.PlatformToolPackage; |
| import com.android.sdklib.internal.repository.packages.SamplePackage; |
| import com.android.sdklib.internal.repository.packages.SystemImagePackage; |
| import com.android.sdklib.internal.repository.packages.ToolPackage; |
| import com.android.sdklib.internal.repository.sources.SdkSource; |
| import com.android.sdklib.internal.repository.sources.SdkSources; |
| import com.android.sdklib.repository.FullRevision; |
| import com.android.sdklib.repository.descriptors.IdDisplay; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * The logic to compute which packages to install, based on the choices |
| * made by the user. This adds required packages as needed. |
| * <p/> |
| * When the user doesn't provide a selection, looks at local package to find |
| * those that can be updated and compute dependencies too. |
| * |
| * @deprecated |
| * com.android.sdklib.internal.repository has moved into Studio as |
| * com.android.tools.idea.sdk.remote.internal. |
| */ |
| @Deprecated |
| public class SdkUpdaterLogic { |
| |
| private final IUpdaterData mUpdaterData; |
| |
| public SdkUpdaterLogic(IUpdaterData updaterData) { |
| mUpdaterData = updaterData; |
| } |
| |
| /** |
| * Retrieves an unfiltered list of all remote archives. |
| * The archives are guaranteed to be compatible with the current platform. |
| */ |
| public List<ArchiveInfo> getAllRemoteArchives( |
| SdkSources sources, |
| Package[] localPkgs, |
| boolean includeAll) { |
| |
| List<Package> remotePkgs = new ArrayList<Package>(); |
| SdkSource[] remoteSources = sources.getAllSources(); |
| fetchRemotePackages(remotePkgs, remoteSources); |
| |
| ArrayList<Archive> archives = new ArrayList<Archive>(); |
| for (Package remotePkg : remotePkgs) { |
| // Only look for non-obsolete updates unless requested to include them |
| if (includeAll || !remotePkg.isObsolete()) { |
| // Found a suitable update. Only accept the remote package |
| // if it provides at least one compatible archive |
| |
| addArchives: |
| for (Archive a : remotePkg.getArchives()) { |
| if (a.isCompatible()) { |
| |
| // If we're trying to add a package for revision N, |
| // make sure we don't also have a package for revision N-1. |
| for (int i = archives.size() - 1; i >= 0; i--) { |
| Package pkgFound = archives.get(i).getParentPackage(); |
| if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { |
| // This package can update one we selected earlier. |
| // Remove the one that can be updated by this new one. |
| archives.remove(i); |
| } else if (remotePkg.canBeUpdatedBy(pkgFound) == UpdateInfo.UPDATE) { |
| // There is a package in the list that is already better |
| // than the one we want to add, so don't add it. |
| break addArchives; |
| } |
| } |
| |
| archives.add(a); |
| break; |
| } |
| } |
| } |
| } |
| |
| ArrayList<ArchiveInfo> result = new ArrayList<ArchiveInfo>(); |
| |
| ArchiveInfo[] localArchives = createLocalArchives(localPkgs); |
| |
| for (Archive a : archives) { |
| insertArchive(a, |
| result, |
| archives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| false /*automated*/); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Compute which packages to install by taking the user selection |
| * and adding required packages as needed. |
| * |
| * When the user doesn't provide a selection, looks at local packages to find |
| * those that can be updated and compute dependencies too. |
| */ |
| public List<ArchiveInfo> computeUpdates( |
| Collection<Archive> selectedArchives, |
| SdkSources sources, |
| Package[] localPkgs, |
| boolean includeAll) { |
| |
| List<ArchiveInfo> archives = new ArrayList<ArchiveInfo>(); |
| List<Package> remotePkgs = new ArrayList<Package>(); |
| SdkSource[] remoteSources = sources.getAllSources(); |
| |
| // Create ArchiveInfos out of local (installed) packages. |
| ArchiveInfo[] localArchives = createLocalArchives(localPkgs); |
| |
| // If we do not have a specific list of archives to install (that is the user |
| // selected "update all" rather than request specific packages), then we try to |
| // find updates based on the *existing* packages. |
| if (selectedArchives == null) { |
| selectedArchives = findUpdates( |
| localArchives, |
| remotePkgs, |
| remoteSources, |
| includeAll); |
| } |
| |
| // Once we have a list of packages to install, we try to solve all their |
| // dependencies by automatically adding them to the list of things to install. |
| // This works on the list provided either by the user directly or the list |
| // computed from potential updates. |
| for (Archive a : selectedArchives) { |
| insertArchive(a, |
| archives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| false /*automated*/); |
| } |
| |
| // Finally we need to look at *existing* packages which are not being updated |
| // and check if they have any missing dependencies and suggest how to fix |
| // these dependencies. |
| fixMissingLocalDependencies( |
| archives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| return archives; |
| } |
| |
| private double getRevisionRank(FullRevision rev) { |
| int p = rev.isPreview() ? 999 : 999 - rev.getPreview(); |
| return rev.getMajor() + |
| rev.getMinor() / 1000.d + |
| rev.getMicro() / 1000000.d + |
| p / 1000000000.d; |
| } |
| |
| /** |
| * Finds new packages that the user does not have in his/her local SDK |
| * and adds them to the list of archives to install. |
| * <p/> |
| * The default is to only find "new" platforms, that is anything more |
| * recent than the highest platform currently installed. |
| * A side effect is that for an empty SDK install this will list *all* |
| * platforms available (since there's no "highest" installed platform.) |
| * <p/> |
| * This also adds "silent" dependencies. For example the user probably |
| * needs to have at least one version of the build-tools package although |
| * there is nothing that directly depends on it. So if the user doesn't have |
| * any installed or selected version, selected the most recent one as a |
| * candidate for install. |
| * |
| * @param archives The in-out list of archives to install. Typically the |
| * list is not empty at first as it should contain any archives that is |
| * already scheduled for install. This method will add to the list. |
| * @param sources The list of all sources, to fetch them as necessary. |
| * @param localPkgs The list of all currently installed packages. |
| * @param includeAll When true, this will list all platforms. |
| * (included these lower than the highest installed one) as well as |
| * all obsolete packages of these platforms. |
| */ |
| public void addNewPlatforms( |
| Collection<ArchiveInfo> archives, |
| SdkSources sources, |
| Package[] localPkgs, |
| boolean includeAll) { |
| |
| // Create ArchiveInfos out of local (installed) packages. |
| ArchiveInfo[] localArchives = createLocalArchives(localPkgs); |
| |
| // Find the highest platform installed |
| double currentBuildToolScore = 0; |
| double currentPlatformScore = 0; |
| double currentSampleScore = 0; |
| double currentAddonScore = 0; |
| double currentDocScore = 0; |
| HashMap<String, Double> currentExtraScore = new HashMap<String, Double>(); |
| if (!includeAll) { |
| if (localPkgs != null) { |
| for (Package p : localPkgs) { |
| double rev = getRevisionRank(p.getRevision()); |
| int api = 0; |
| boolean isPreview = false; |
| if (p instanceof IAndroidVersionProvider) { |
| AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion(); |
| api = vers.getApiLevel(); |
| isPreview = vers.isPreview(); |
| } |
| |
| // The score is 1000*api + (999 if preview) + rev |
| // This allows previews to rank above a non-preview and |
| // allows revisions to rank appropriately. |
| double score = api * 1000 + (isPreview ? 999 : 0) + rev; |
| |
| if (p instanceof BuildToolPackage) { |
| currentBuildToolScore = Math.max(currentBuildToolScore, score); |
| } else if (p instanceof PlatformPackage) { |
| currentPlatformScore = Math.max(currentPlatformScore, score); |
| } else if (p instanceof SamplePackage) { |
| currentSampleScore = Math.max(currentSampleScore, score); |
| } else if (p instanceof AddonPackage) { |
| currentAddonScore = Math.max(currentAddonScore, score); |
| } else if (p instanceof ExtraPackage) { |
| currentExtraScore.put(((ExtraPackage) p).getPath(), score); |
| } else if (p instanceof DocPackage) { |
| currentDocScore = Math.max(currentDocScore, score); |
| } |
| } |
| } |
| } |
| |
| SdkSource[] remoteSources = sources.getAllSources(); |
| ArrayList<Package> remotePkgs = new ArrayList<Package>(); |
| fetchRemotePackages(remotePkgs, remoteSources); |
| |
| Package suggestedDoc = null; |
| Package suggestedBuildTool = null; |
| |
| for (Package p : remotePkgs) { |
| // Skip obsolete packages unless requested to include them. |
| if (p.isObsolete() && !includeAll) { |
| continue; |
| } |
| |
| double rev = getRevisionRank(p.getRevision()); |
| int api = 0; |
| boolean isPreview = false; |
| if (p instanceof IAndroidVersionProvider) { |
| AndroidVersion vers = ((IAndroidVersionProvider) p).getAndroidVersion(); |
| api = vers.getApiLevel(); |
| isPreview = vers.isPreview(); |
| } |
| |
| double score = api * 1000 + (isPreview ? 999 : 0) + rev; |
| |
| boolean shouldAdd = false; |
| if (p instanceof BuildToolPackage) { |
| // We don't want all the build-tool packages, only the most recent one |
| // if not is currently installed. |
| if (currentBuildToolScore == 0 && score > currentBuildToolScore) { |
| suggestedBuildTool = p; |
| currentBuildToolScore = score; |
| } |
| } else if (p instanceof PlatformPackage) { |
| shouldAdd = score > currentPlatformScore; |
| } else if (p instanceof SamplePackage) { |
| shouldAdd = score > currentSampleScore; |
| } else if (p instanceof AddonPackage) { |
| shouldAdd = score > currentAddonScore; |
| } else if (p instanceof ExtraPackage) { |
| String key = ((ExtraPackage) p).getPath(); |
| shouldAdd = !currentExtraScore.containsKey(key) || |
| score > currentExtraScore.get(key).doubleValue(); |
| } else if (p instanceof DocPackage) { |
| // We don't want all the doc, only the most recent one |
| if (score > currentDocScore) { |
| suggestedDoc = p; |
| currentDocScore = score; |
| } |
| } |
| |
| if (shouldAdd) { |
| // We should suggest this package for installation. |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| insertArchive(a, |
| archives, |
| null /*selectedArchives*/, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| |
| if (p instanceof PlatformPackage && (score >= currentPlatformScore)) { |
| // We just added a new platform *or* we are visiting the highest currently |
| // installed platform. In either case we want to make sure it either has |
| // its own system image or that we provide one by default. |
| PlatformPackage pp = (PlatformPackage) p; |
| if (pp.getIncludedAbi() == null) { |
| for (Package p2 : remotePkgs) { |
| if (!(p2 instanceof SystemImagePackage) || |
| ((SystemImagePackage)p2).isPlatform() || |
| (p2.isObsolete() && !includeAll)) { |
| continue; |
| } |
| SystemImagePackage sip = (SystemImagePackage) p2; |
| if (sip.getAndroidVersion().equals(pp.getAndroidVersion())) { |
| for (Archive a : sip.getArchives()) { |
| if (a.isCompatible()) { |
| insertArchive(a, |
| archives, |
| null /*selectedArchives*/, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (suggestedDoc != null) { |
| // We should suggest this package for installation. |
| for (Archive a : suggestedDoc.getArchives()) { |
| if (a.isCompatible()) { |
| insertArchive(a, |
| archives, |
| null /*selectedArchives*/, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| |
| if (suggestedBuildTool != null) { |
| // We should suggest this package for installation. |
| for (Archive a : suggestedBuildTool.getArchives()) { |
| if (a.isCompatible()) { |
| insertArchive(a, |
| archives, |
| null /*selectedArchives*/, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create a array of {@link ArchiveInfo} based on all local (already installed) |
| * packages. The array is always non-null but may be empty. |
| * <p/> |
| * The local {@link ArchiveInfo} are guaranteed to have one non-null archive |
| * that you can retrieve using {@link ArchiveInfo#getNewArchive()}. |
| */ |
| public ArchiveInfo[] createLocalArchives(Package[] localPkgs) { |
| |
| if (localPkgs != null) { |
| ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>(); |
| for (Package p : localPkgs) { |
| // Only accept packages that have one compatible archive. |
| // Local package should have 1 and only 1 compatible archive anyway. |
| for (Archive a : p.getArchives()) { |
| if (a != null && a.isCompatible()) { |
| // We create an "installed" archive info to wrap the local package. |
| // Note that dependencies are not computed since right now we don't |
| // deal with more than one level of dependencies and installed archives |
| // are deemed implicitly accepted anyway. |
| list.add(new LocalArchiveInfo(a)); |
| } |
| } |
| } |
| |
| return list.toArray(new ArchiveInfo[list.size()]); |
| } |
| |
| return new ArchiveInfo[0]; |
| } |
| |
| /** |
| * Find suitable updates to all current local packages. |
| * <p/> |
| * Returns a list of potential updates for *existing* packages. This does NOT solve |
| * dependencies for the new packages. |
| * <p/> |
| * Always returns a non-null collection, which can be empty. |
| */ |
| private Collection<Archive> findUpdates( |
| ArchiveInfo[] localArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| boolean includeAll) { |
| ArrayList<Archive> updates = new ArrayList<Archive>(); |
| |
| fetchRemotePackages(remotePkgs, remoteSources); |
| |
| for (ArchiveInfo ai : localArchives) { |
| Archive na = ai.getNewArchive(); |
| if (na == null) { |
| continue; |
| } |
| Package localPkg = na.getParentPackage(); |
| |
| for (Package remotePkg : remotePkgs) { |
| // Only look for non-obsolete updates unless requested to include them |
| if ((includeAll || !remotePkg.isObsolete()) && |
| localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { |
| // Found a suitable update. Only accept the remote package |
| // if it provides at least one compatible archive |
| |
| addArchives: |
| for (Archive a : remotePkg.getArchives()) { |
| if (a.isCompatible()) { |
| |
| // If we're trying to add a package for revision N, |
| // make sure we don't also have a package for revision N-1. |
| for (int i = updates.size() - 1; i >= 0; i--) { |
| Package pkgFound = updates.get(i).getParentPackage(); |
| if (pkgFound.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) { |
| // This package can update one we selected earlier. |
| // Remove the one that can be updated by this new one. |
| updates.remove(i); |
| } else if (remotePkg.canBeUpdatedBy(pkgFound) == |
| UpdateInfo.UPDATE) { |
| // There is a package in the list that is already better |
| // than the one we want to add, so don't add it. |
| break addArchives; |
| } |
| } |
| |
| updates.add(a); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| return updates; |
| } |
| |
| /** |
| * Check all local archives which are NOT being updated and see if they |
| * miss any dependency. If they do, try to fix that dependency by selecting |
| * an appropriate package. |
| */ |
| private void fixMissingLocalDependencies( |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| |
| nextLocalArchive: for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| Package p = a == null ? null : a.getParentPackage(); |
| if (p == null) { |
| continue; |
| } |
| |
| // Is this local archive being updated? |
| for (ArchiveInfo ai2 : outArchives) { |
| if (ai2.getReplaced() == a) { |
| // this new archive will replace the current local one, |
| // so we don't have to care about fixing dependencies (since the |
| // new archive should already have had its dependencies resolved) |
| continue nextLocalArchive; |
| } |
| } |
| |
| // find dependencies for the local archive and add them as needed |
| // to the outArchives collection. |
| ArchiveInfo[] deps = findDependency(p, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (deps != null) { |
| // The already installed archive has a missing dependency, which we |
| // just selected for install. Make sure we remember the dependency |
| // so that we can enforce it later in the UI. |
| for (ArchiveInfo aid : deps) { |
| aid.addDependencyFor(ai); |
| } |
| } |
| } |
| } |
| |
| private ArchiveInfo insertArchive(Archive archive, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives, |
| boolean automated) { |
| Package p = archive.getParentPackage(); |
| |
| // Is this an update? |
| Archive updatedArchive = null; |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package lp = a.getParentPackage(); |
| |
| if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) { |
| updatedArchive = a; |
| } |
| } |
| } |
| |
| // Find dependencies and adds them as needed to outArchives |
| ArchiveInfo[] deps = findDependency(p, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| // Make sure it's not a dup |
| ArchiveInfo ai = null; |
| |
| for (ArchiveInfo ai2 : outArchives) { |
| Archive a2 = ai2.getNewArchive(); |
| if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) { |
| ai = ai2; |
| break; |
| } |
| } |
| |
| if (ai == null) { |
| ai = new ArchiveInfo( |
| archive, //newArchive |
| updatedArchive, //replaced |
| deps //dependsOn |
| ); |
| outArchives.add(ai); |
| } |
| |
| if (deps != null) { |
| for (ArchiveInfo d : deps) { |
| d.addDependencyFor(ai); |
| } |
| } |
| |
| return ai; |
| } |
| |
| /** |
| * Resolves dependencies for a given package. |
| * |
| * Returns null if no dependencies were found. |
| * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have |
| * at least size 1 and contain no null elements. |
| */ |
| private ArchiveInfo[] findDependency(Package pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| |
| // Current dependencies can be: |
| // - addon: *always* depends on platform of same API level |
| // - platform: *might* depends on tools of rev >= min-tools-rev |
| // - extra: *might* depends on platform with api >= min-api-level |
| |
| Set<ArchiveInfo> aiFound = new HashSet<ArchiveInfo>(); |
| |
| if (pkg instanceof IPlatformDependency) { |
| ArchiveInfo ai = findPlatformDependency( |
| (IPlatformDependency) pkg, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (ai != null) { |
| aiFound.add(ai); |
| } |
| } |
| |
| if (pkg instanceof IMinToolsDependency) { |
| |
| ArchiveInfo ai = findToolsDependency( |
| (IMinToolsDependency) pkg, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (ai != null) { |
| aiFound.add(ai); |
| } |
| } |
| |
| if (pkg instanceof IMinPlatformToolsDependency) { |
| |
| ArchiveInfo ai = findPlatformToolsDependency( |
| (IMinPlatformToolsDependency) pkg, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (ai != null) { |
| aiFound.add(ai); |
| } |
| } |
| |
| if (pkg instanceof IMinApiLevelDependency) { |
| |
| ArchiveInfo ai = findMinApiLevelDependency( |
| (IMinApiLevelDependency) pkg, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (ai != null) { |
| aiFound.add(ai); |
| } |
| } |
| |
| if (pkg instanceof IExactApiLevelDependency) { |
| |
| ArchiveInfo ai = findExactApiLevelDependency( |
| (IExactApiLevelDependency) pkg, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives); |
| |
| if (ai != null) { |
| aiFound.add(ai); |
| } |
| } |
| |
| if (!aiFound.isEmpty()) { |
| ArchiveInfo[] result = aiFound.toArray(new ArchiveInfo[aiFound.size()]); |
| Arrays.sort(result); |
| return result; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Resolves dependencies on tools. |
| * |
| * A platform or an extra package can both have a min-tools-rev, in which case it |
| * depends on having a tools package of the requested revision. |
| * Finds the tools dependency. If found, add it to the list of things to install. |
| * Returns the archive info dependency, if any. |
| */ |
| public ArchiveInfo findToolsDependency( |
| IMinToolsDependency pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| // This is the requirement to match. |
| FullRevision rev = pkg.getMinToolsRevision(); |
| |
| if (rev.equals(MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED)) { |
| // Well actually there's no requirement. |
| return null; |
| } |
| |
| // First look in locally installed packages. |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof ToolPackage) { |
| if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { |
| // We found one already installed. |
| return null; |
| } |
| } |
| } |
| } |
| |
| // Look in archives already scheduled for install |
| for (ArchiveInfo ai : outArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof ToolPackage) { |
| if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { |
| // The dependency is already scheduled for install, nothing else to do. |
| return ai; |
| } |
| } |
| } |
| } |
| |
| // Otherwise look in the selected archives. |
| if (selectedArchives != null) { |
| for (Archive a : selectedArchives) { |
| Package p = a.getParentPackage(); |
| if (p instanceof ToolPackage) { |
| if (((ToolPackage) p).getRevision().compareTo(rev) >= 0) { |
| // It's not already in the list of things to install, so add it now |
| return insertArchive(a, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| |
| // Finally nothing matched, so let's look at all available remote packages |
| fetchRemotePackages(remotePkgs, remoteSources); |
| FullRevision localRev = rev; |
| Archive localArch = null; |
| for (Package p : remotePkgs) { |
| if (p instanceof ToolPackage) { |
| FullRevision r = ((ToolPackage) p).getRevision(); |
| if (r.compareTo(localRev) >= 0) { |
| // It's not already in the list of things to install, so add the |
| // first compatible archive we can find. |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| localRev = r; |
| localArch = a; |
| break; |
| } |
| } |
| } |
| } |
| } |
| if (localArch != null) { |
| return insertArchive(localArch, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| |
| } |
| |
| // We end up here if nothing matches. We don't have a good platform to match. |
| // We need to indicate this extra depends on a missing platform archive |
| // so that it can be impossible to install later on. |
| return new MissingArchiveInfo(MissingArchiveInfo.TITLE_TOOL, rev); |
| } |
| |
| /** |
| * Resolves dependencies on platform-tools. |
| * |
| * A tool package can have a min-platform-tools-rev, in which case it depends on |
| * having a platform-tool package of the requested revision. |
| * Finds the platform-tool dependency. If found, add it to the list of things to install. |
| * Returns the archive info dependency, if any. |
| */ |
| public ArchiveInfo findPlatformToolsDependency( |
| IMinPlatformToolsDependency pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| // This is the requirement to match. |
| FullRevision rev = pkg.getMinPlatformToolsRevision(); |
| boolean findMax = false; |
| int compareThreshold = 0; |
| ArchiveInfo aiMax = null; |
| Archive aMax = null; |
| |
| if (rev.equals(IMinPlatformToolsDependency.MIN_PLATFORM_TOOLS_REV_INVALID)) { |
| // The requirement is invalid, which is not supposed to happen since this |
| // property is mandatory. However in a typical upgrade scenario we can end |
| // up with the previous updater managing a new package and not dealing |
| // correctly with the new unknown property. |
| // So instead we parse all the existing and remote packages and try to find |
| // the max available revision and we'll use it. |
| findMax = true; |
| // When findMax is false, we want r.compareTo(rev) >= 0. |
| // When findMax is true, we want r.compareTo(rev) > 0 (so >= 1). |
| compareThreshold = 1; |
| } |
| |
| // First look in locally installed packages. |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformToolPackage) { |
| FullRevision r = ((PlatformToolPackage) p).getRevision(); |
| if (findMax && r.compareTo(rev) > compareThreshold) { |
| rev = r; |
| aiMax = ai; |
| } else if (!findMax && r.compareTo(rev) >= compareThreshold) { |
| // We found one already installed. |
| return null; |
| } |
| } |
| } |
| } |
| |
| // Because of previews, we can have more than 1 choice, so get the local max. |
| FullRevision localRev = rev; |
| ArchiveInfo localAiMax = null; |
| // Look in archives already scheduled for install |
| for (ArchiveInfo ai : outArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformToolPackage) { |
| FullRevision r = ((PlatformToolPackage) p).getRevision(); |
| // If computing dependencies for a non-preview package, don't offer preview dependencies |
| if (r.isPreview() && !rev.isPreview()) { |
| continue; |
| } |
| if (r.compareTo(localRev) >= compareThreshold) { |
| localRev = r; |
| localAiMax = ai; |
| } |
| } |
| } |
| } |
| if (localAiMax != null) { |
| if (findMax) { |
| rev = localRev; |
| aiMax = localAiMax; |
| } else { |
| // The dependency is already scheduled for install, nothing else to do. |
| return localAiMax; |
| } |
| } |
| |
| |
| // Otherwise look in the selected archives. |
| localRev = rev; |
| Archive localAMax = null; |
| if (selectedArchives != null) { |
| for (Archive a : selectedArchives) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformToolPackage) { |
| FullRevision r = ((PlatformToolPackage) p).getRevision(); |
| // If computing dependencies for a non-preview package, don't offer preview dependencies |
| if (r.isPreview() && !rev.isPreview()) { |
| continue; |
| } |
| |
| if (r.compareTo(localRev) >= compareThreshold) { |
| localRev = r; |
| localAiMax = null; |
| localAMax = a; |
| } |
| } |
| } |
| if (localAMax != null) { |
| if (findMax) { |
| rev = localRev; |
| aiMax = null; |
| aMax = localAMax; |
| } else { |
| // It's not already in the list of things to install, so add it now |
| return insertArchive(localAMax, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| |
| // Finally nothing matched, so let's look at all available remote packages |
| fetchRemotePackages(remotePkgs, remoteSources); |
| localRev = rev; |
| localAMax = null; |
| for (Package p : remotePkgs) { |
| if (p instanceof PlatformToolPackage) { |
| FullRevision r = ((PlatformToolPackage) p).getRevision(); |
| // If computing dependencies for a non-preview package, don't offer preview dependencies |
| if (r.isPreview() && !rev.isPreview()) { |
| continue; |
| } |
| |
| if (r.compareTo(rev) >= 0) { |
| // Make sure there's at least one valid archive here |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| if (r.compareTo(localRev) >= compareThreshold) { |
| localRev = r; |
| localAiMax = null; |
| localAMax = a; |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| if (localAMax != null) { |
| if (findMax) { |
| rev = localRev; |
| aiMax = null; |
| aMax = localAMax; |
| } else { |
| // It's not already in the list of things to install, so add the |
| // first compatible archive we can find. |
| return insertArchive(localAMax, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| |
| if (findMax) { |
| if (aMax != null) { |
| return insertArchive(aMax, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } else if (aiMax != null) { |
| return aiMax; |
| } |
| } |
| |
| // We end up here if nothing matches. We don't have a good platform to match. |
| // We need to indicate this package depends on a missing platform archive |
| // so that it can be impossible to install later on. |
| return new MissingArchiveInfo(MissingArchiveInfo.TITLE_PLATFORM_TOOL, rev); |
| } |
| |
| /** |
| * Resolves dependencies on platform for an add-on. |
| * Resolves dependencies on a system-image on its base platform or add-on. |
| * |
| * An add-on depends on having a platform with the same API level. |
| * |
| * Finds the platform dependency. If found, add it to the list of things to install. |
| * Returns the archive info dependency, if any. |
| */ |
| public ArchiveInfo findPlatformDependency( |
| IPlatformDependency pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| // This is the requirement to match. |
| AndroidVersion v = pkg.getAndroidVersion(); |
| |
| |
| // The dependency package must be a PlatformPackage or an AddonPackage. |
| // For an add-on, we also need to have the same vendor and name-id. |
| Class<? extends Package> expectedClass = PlatformPackage.class; |
| IdDisplay addonVendor = null; |
| IdDisplay addonTag = null; |
| if (pkg instanceof SystemImagePackage && !((SystemImagePackage) pkg).isPlatform()) { |
| expectedClass = AddonPackage.class; |
| addonVendor = ((SystemImagePackage) pkg).getAddonVendor(); |
| addonTag = ((SystemImagePackage) pkg).getTag(); |
| } |
| |
| // Find a platform or addon that would satisfy the requirement. |
| |
| // First look in locally installed packages. |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (expectedClass.isInstance(p)) { |
| if (v.equals(((IAndroidVersionProvider) p).getAndroidVersion())) { |
| if (addonVendor != null && addonTag != null && p instanceof AddonPackage) { |
| if (!((AddonPackage) p).getVendorId().equals(addonVendor.getId()) || |
| !((AddonPackage) p).getNameId().equals(addonTag.getId())) { |
| continue; |
| } |
| } |
| // We found one already installed. |
| return null; |
| } |
| } |
| } |
| } |
| |
| // Look in archives already scheduled for install |
| for (ArchiveInfo ai : outArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (expectedClass.isInstance(p)) { |
| if (v.equals(((IAndroidVersionProvider) p).getAndroidVersion())) { |
| if (addonVendor != null && addonTag != null && p instanceof AddonPackage) { |
| if (!((AddonPackage) p).getVendorId().equals(addonVendor.getId()) || |
| !((AddonPackage) p).getNameId().equals(addonTag.getId())) { |
| continue; |
| } |
| } |
| // The dependency is already scheduled for install, nothing else to do. |
| return ai; |
| } |
| } |
| } |
| } |
| |
| // Otherwise look in the selected archives. |
| if (selectedArchives != null) { |
| for (Archive a : selectedArchives) { |
| Package p = a.getParentPackage(); |
| if (expectedClass.isInstance(p)) { |
| if (v.equals(((IAndroidVersionProvider) p).getAndroidVersion())) { |
| if (addonVendor != null && addonTag != null && p instanceof AddonPackage) { |
| if (!((AddonPackage) p).getVendorId().equals(addonVendor.getId()) || |
| !((AddonPackage) p).getNameId().equals(addonTag.getId())) { |
| continue; |
| } |
| } |
| // It's not already in the list of things to install, so add it now |
| return insertArchive(a, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| |
| // Finally nothing matched, so let's look at all available remote packages |
| fetchRemotePackages(remotePkgs, remoteSources); |
| for (Package p : remotePkgs) { |
| if (expectedClass.isInstance(p)) { |
| if (v.equals(((IAndroidVersionProvider) p).getAndroidVersion())) { |
| if (addonVendor != null && addonTag != null && p instanceof AddonPackage) { |
| if (!((AddonPackage) p).getVendorId().equals(addonVendor.getId()) || |
| !((AddonPackage) p).getNameId().equals(addonTag.getId())) { |
| continue; |
| } |
| } |
| // It's not already in the list of things to install, so add the |
| // first compatible archive we can find. |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| return insertArchive(a, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| } |
| |
| // We end up here if nothing matches. We don't have a good platform to match. |
| // We need to indicate this addon depends on a missing platform archive |
| // so that it can be impossible to install later on. |
| return new MissingPlatformArchiveInfo(pkg.getAndroidVersion()); |
| } |
| |
| /** |
| * Resolves platform dependencies for extras. |
| * An extra depends on having a platform with a minimun API level. |
| * |
| * We try to return the highest API level available above the specified minimum. |
| * Note that installed packages have priority so if one installed platform satisfies |
| * the dependency, we'll use it even if there's a higher API platform available but |
| * not installed yet. |
| * |
| * Finds the platform dependency. If found, add it to the list of things to install. |
| * Returns the archive info dependency, if any. |
| */ |
| protected ArchiveInfo findMinApiLevelDependency( |
| IMinApiLevelDependency pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| |
| int api = pkg.getMinApiLevel(); |
| |
| if (api == IMinApiLevelDependency.MIN_API_LEVEL_NOT_SPECIFIED) { |
| return null; |
| } |
| |
| // Find a platform that would satisfy the requirement. |
| |
| // First look in locally installed packages. |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { |
| // We found one already installed. |
| return null; |
| } |
| } |
| } |
| } |
| |
| // Look in archives already scheduled for install |
| int foundApi = 0; |
| ArchiveInfo foundAi = null; |
| |
| for (ArchiveInfo ai : outArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { |
| if (api > foundApi) { |
| foundApi = api; |
| foundAi = ai; |
| } |
| } |
| } |
| } |
| } |
| |
| if (foundAi != null) { |
| // The dependency is already scheduled for install, nothing else to do. |
| return foundAi; |
| } |
| |
| // Otherwise look in the selected archives *or* available remote packages |
| // and takes the best out of the two sets. |
| foundApi = 0; |
| Archive foundArchive = null; |
| if (selectedArchives != null) { |
| for (Archive a : selectedArchives) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { |
| if (api > foundApi) { |
| foundApi = api; |
| foundArchive = a; |
| } |
| } |
| } |
| } |
| } |
| |
| // Finally nothing matched, so let's look at all available remote packages |
| fetchRemotePackages(remotePkgs, remoteSources); |
| for (Package p : remotePkgs) { |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().isGreaterOrEqualThan(api)) { |
| if (api > foundApi) { |
| // It's not already in the list of things to install, so add the |
| // first compatible archive we can find. |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| foundApi = api; |
| foundArchive = a; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (foundArchive != null) { |
| // It's not already in the list of things to install, so add it now |
| return insertArchive(foundArchive, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| |
| // We end up here if nothing matches. We don't have a good platform to match. |
| // We need to indicate this extra depends on a missing platform archive |
| // so that it can be impossible to install later on. |
| return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/)); |
| } |
| |
| /** |
| * Resolves platform dependencies for add-ons. |
| * An add-ons depends on having a platform with an exact specific API level. |
| * |
| * Finds the platform dependency. If found, add it to the list of things to install. |
| * Returns the archive info dependency, if any. |
| */ |
| public ArchiveInfo findExactApiLevelDependency( |
| IExactApiLevelDependency pkg, |
| Collection<ArchiveInfo> outArchives, |
| Collection<Archive> selectedArchives, |
| Collection<Package> remotePkgs, |
| SdkSource[] remoteSources, |
| ArchiveInfo[] localArchives) { |
| |
| int api = pkg.getExactApiLevel(); |
| |
| if (api == IExactApiLevelDependency.API_LEVEL_INVALID) { |
| return null; |
| } |
| |
| // Find a platform that would satisfy the requirement. |
| |
| // First look in locally installed packages. |
| for (ArchiveInfo ai : localArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().equals(api)) { |
| // We found one already installed. |
| return null; |
| } |
| } |
| } |
| } |
| |
| // Look in archives already scheduled for install |
| |
| for (ArchiveInfo ai : outArchives) { |
| Archive a = ai.getNewArchive(); |
| if (a != null) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().equals(api)) { |
| return ai; |
| } |
| } |
| } |
| } |
| |
| // Otherwise look in the selected archives. |
| if (selectedArchives != null) { |
| for (Archive a : selectedArchives) { |
| Package p = a.getParentPackage(); |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().equals(api)) { |
| // It's not already in the list of things to install, so add it now |
| return insertArchive(a, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| |
| // Finally nothing matched, so let's look at all available remote packages |
| fetchRemotePackages(remotePkgs, remoteSources); |
| for (Package p : remotePkgs) { |
| if (p instanceof PlatformPackage) { |
| if (((PlatformPackage) p).getAndroidVersion().equals(api)) { |
| // It's not already in the list of things to install, so add the |
| // first compatible archive we can find. |
| for (Archive a : p.getArchives()) { |
| if (a.isCompatible()) { |
| return insertArchive(a, |
| outArchives, |
| selectedArchives, |
| remotePkgs, |
| remoteSources, |
| localArchives, |
| true /*automated*/); |
| } |
| } |
| } |
| } |
| } |
| |
| // We end up here if nothing matches. We don't have a good platform to match. |
| // We need to indicate this extra depends on a missing platform archive |
| // so that it can be impossible to install later on. |
| return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/)); |
| } |
| |
| /** |
| * Fetch all remote packages only if really needed. |
| * <p/> |
| * This method takes a list of sources. Each source is only fetched once -- that is each |
| * source keeps the list of packages that we fetched from the remote XML file. If the list |
| * is null, it means this source has never been fetched so we'll do it once here. Otherwise |
| * we rely on the cached list of packages from this source. |
| * <p/> |
| * This method also takes a remote package list as input, which it will fill out. |
| * If a source has already been fetched, we'll add its packages to the remote package list |
| * if they are not already present. Otherwise, the source will be fetched and the packages |
| * added to the list. |
| * |
| * @param remotePkgs An in-out list of packages available from remote sources. |
| * This list must not be null. |
| * It can be empty or already contain some packages. |
| * @param remoteSources A list of available remote sources to fetch from. |
| */ |
| protected void fetchRemotePackages( |
| final Collection<Package> remotePkgs, |
| final SdkSource[] remoteSources) { |
| if (!remotePkgs.isEmpty()) { |
| return; |
| } |
| |
| // First check if there's any remote source we need to fetch. |
| // This will bring the task window, so we rather not display it unless |
| // necessary. |
| boolean needsFetch = false; |
| for (final SdkSource remoteSrc : remoteSources) { |
| Package[] pkgs = remoteSrc.getPackages(); |
| if (pkgs == null) { |
| // This source has never been fetched. We'll do it below. |
| needsFetch = true; |
| } else { |
| // This source has already been fetched and we know its package list. |
| // We still need to make sure all of its packages are present in the |
| // remotePkgs list. |
| |
| nextPackage: for (Package pkg : pkgs) { |
| for (Archive a : pkg.getArchives()) { |
| // Only add a package if it contains at least one compatible archive |
| // and is not already in the remote package list. |
| if (a.isCompatible()) { |
| if (!remotePkgs.contains(pkg)) { |
| remotePkgs.add(pkg); |
| continue nextPackage; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (!needsFetch) { |
| return; |
| } |
| |
| final boolean forceHttp = mUpdaterData.getSettingsController().getSettings().getForceHttp(); |
| |
| mUpdaterData.getTaskFactory().start("Refresh Sources", new ITask() { |
| @Override |
| public void run(ITaskMonitor monitor) { |
| for (SdkSource remoteSrc : remoteSources) { |
| Package[] pkgs = remoteSrc.getPackages(); |
| |
| if (pkgs == null) { |
| remoteSrc.load(mUpdaterData.getDownloadCache(), monitor, forceHttp); |
| pkgs = remoteSrc.getPackages(); |
| } |
| |
| if (pkgs != null) { |
| nextPackage: for (Package pkg : pkgs) { |
| for (Archive a : pkg.getArchives()) { |
| // Only add a package if it contains at least one compatible archive |
| // and is not already in the remote package list. |
| if (a.isCompatible()) { |
| if (!remotePkgs.contains(pkg)) { |
| remotePkgs.add(pkg); |
| continue nextPackage; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| |
| /** |
| * A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed |
| * "local" package/archive. |
| * <p/> |
| * In this case, the "new Archive" is still expected to be non null and the |
| * "replaced Archive" is null. Installed archives are always accepted and never |
| * rejected. |
| * <p/> |
| * Dependencies are not set. |
| */ |
| private static class LocalArchiveInfo extends ArchiveInfo { |
| |
| public LocalArchiveInfo(Archive localArchive) { |
| super(localArchive, null /*replaced*/, null /*dependsOn*/); |
| } |
| |
| /** Installed archives are always accepted. */ |
| @Override |
| public boolean isAccepted() { |
| return true; |
| } |
| |
| /** Installed archives are never rejected. */ |
| @Override |
| public boolean isRejected() { |
| return false; |
| } |
| } |
| |
| /** |
| * A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a |
| * package/archive that we <em>really</em> need as a dependency but that we don't have. |
| * <p/> |
| * This is currently used for addons and extras in case we can't find a matching base platform. |
| * <p/> |
| * This kind of archive has specific properties: the new archive to install is null, |
| * there are no dependencies and no archive is being replaced. The info can never be |
| * accepted and is always rejected. |
| */ |
| private static class MissingPlatformArchiveInfo extends ArchiveInfo { |
| |
| private final AndroidVersion mVersion; |
| |
| /** |
| * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the |
| * given platform version is missing. |
| */ |
| public MissingPlatformArchiveInfo(AndroidVersion version) { |
| super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); |
| mVersion = version; |
| } |
| |
| /** Missing archives are never accepted. */ |
| @Override |
| public boolean isAccepted() { |
| return false; |
| } |
| |
| /** Missing archives are always rejected. */ |
| @Override |
| public boolean isRejected() { |
| return true; |
| } |
| |
| @Override |
| public String getShortDescription() { |
| return String.format("Missing SDK Platform Android%1$s, API %2$d", |
| mVersion.isPreview() ? " Preview" : "", |
| mVersion.getApiLevel()); |
| } |
| } |
| |
| /** |
| * A {@link MissingArchiveInfo} is an {@link ArchiveInfo} that represents a |
| * package/archive that we <em>really</em> need as a dependency but that we don't have. |
| * <p/> |
| * This is currently used for extras in case we can't find a matching tool revision |
| * or when a platform-tool is missing. |
| * <p/> |
| * This kind of archive has specific properties: the new archive to install is null, |
| * there are no dependencies and no archive is being replaced. The info can never be |
| * accepted and is always rejected. |
| */ |
| private static class MissingArchiveInfo extends ArchiveInfo { |
| |
| private final FullRevision mRevision; |
| private final String mTitle; |
| |
| public static final String TITLE_TOOL = "Tools"; |
| public static final String TITLE_PLATFORM_TOOL = "Platform-tools"; |
| |
| /** |
| * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the |
| * given platform version is missing. |
| * |
| * @param title Typically "Tools" or "Platform-tools". |
| * @param revision The required revision. |
| */ |
| public MissingArchiveInfo(String title, FullRevision revision) { |
| super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/); |
| mTitle = title; |
| mRevision = revision; |
| } |
| |
| /** Missing archives are never accepted. */ |
| @Override |
| public boolean isAccepted() { |
| return false; |
| } |
| |
| /** Missing archives are always rejected. */ |
| @Override |
| public boolean isRejected() { |
| return true; |
| } |
| |
| @Override |
| public String getShortDescription() { |
| return String.format("Missing Android SDK %1$s, revision %2$s", |
| mTitle, |
| mRevision.toShortString()); |
| } |
| } |
| } |