2 * Copyright (C) 2009 The Android Open Source Project
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
17 package com.android.sdkuilib.internal.repository;
\r
19 import com.android.sdklib.AndroidVersion;
\r
20 import com.android.sdklib.internal.repository.AddonPackage;
\r
21 import com.android.sdklib.internal.repository.Archive;
\r
22 import com.android.sdklib.internal.repository.MinToolsPackage;
\r
23 import com.android.sdklib.internal.repository.Package;
\r
24 import com.android.sdklib.internal.repository.PlatformPackage;
\r
25 import com.android.sdklib.internal.repository.RepoSource;
\r
26 import com.android.sdklib.internal.repository.RepoSources;
\r
27 import com.android.sdklib.internal.repository.ToolPackage;
\r
28 import com.android.sdklib.internal.repository.Package.UpdateInfo;
\r
30 import java.util.ArrayList;
\r
31 import java.util.Collection;
\r
34 * The logic to compute which packages to install, based on the choices
\r
35 * made by the user. This adds dependent packages as needed.
\r
37 * When the user doesn't provide a selection, looks at local package to find
\r
38 * those that can be updated and compute dependencies too.
\r
40 class UpdaterLogic {
\r
42 private RepoSources mSources;
\r
45 * Compute which packages to install by taking the user selection
\r
46 * and adding dependent packages as needed.
\r
48 * When the user doesn't provide a selection, looks at local package to find
\r
49 * those that can be updated and compute dependencies too.
\r
51 public ArrayList<ArchiveInfo> computeUpdates(
\r
52 Collection<Archive> selectedArchives,
\r
53 RepoSources sources,
\r
54 Package[] localPkgs) {
\r
57 ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
\r
58 ArrayList<Package> remotePkgs = new ArrayList<Package>();
\r
60 if (selectedArchives == null) {
\r
61 selectedArchives = findUpdates(localPkgs, remotePkgs);
\r
64 for (Archive a : selectedArchives) {
\r
65 insertArchive(a, archives, selectedArchives, remotePkgs, localPkgs, false);
\r
73 * Find suitable updates to all current local packages.
\r
75 private Collection<Archive> findUpdates(Package[] localPkgs, ArrayList<Package> remotePkgs) {
\r
76 ArrayList<Archive> updates = new ArrayList<Archive>();
\r
78 fetchRemotePackages(remotePkgs);
\r
80 for (Package localPkg : localPkgs) {
\r
81 for (Package remotePkg : remotePkgs) {
\r
82 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
\r
83 // Found a suitable update. Only accept the remote package
\r
84 // if it provides at least one compatible archive.
\r
86 for (Archive a : remotePkg.getArchives()) {
\r
87 if (a.isCompatible()) {
\r
99 private ArchiveInfo insertArchive(Archive archive,
\r
100 ArrayList<ArchiveInfo> outArchives,
\r
101 Collection<Archive> selectedArchives,
\r
102 ArrayList<Package> remotePkgs,
\r
103 Package[] localPkgs,
\r
104 boolean automated) {
\r
105 Package p = archive.getParentPackage();
\r
107 // Is this an update?
\r
108 Archive updatedArchive = null;
\r
109 for (Package lp : localPkgs) {
\r
110 assert lp.getArchives().length == 1;
\r
111 if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {
\r
112 updatedArchive = lp.getArchives()[0];
\r
116 // find dependencies
\r
117 ArchiveInfo dep = findDependency(p, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
119 ArchiveInfo ai = new ArchiveInfo(
\r
120 archive, //newArchive
\r
121 updatedArchive, //replaced
\r
125 outArchives.add(ai);
\r
127 dep.addDependencyFor(ai);
\r
133 private ArchiveInfo findDependency(Package pkg,
\r
134 ArrayList<ArchiveInfo> outArchives,
\r
135 Collection<Archive> selectedArchives,
\r
136 ArrayList<Package> remotePkgs,
\r
137 Package[] localPkgs) {
\r
139 // Current dependencies can be:
\r
140 // - addon: *always* depends on platform of same API level
\r
141 // - platform: *might* depends on tools of rev >= min-tools-rev
\r
143 if (pkg instanceof AddonPackage) {
\r
144 AddonPackage addon = (AddonPackage) pkg;
\r
146 return findPlatformDependency(
\r
147 addon, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
149 } else if (pkg instanceof MinToolsPackage) {
\r
150 MinToolsPackage platformOrExtra = (MinToolsPackage) pkg;
\r
152 return findToolsDependency(
\r
153 platformOrExtra, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
160 * Resolves dependencies on tools.
\r
162 * A platform or an extra package can both have a min-tools-rev, in which case it
\r
163 * depends on having a tools package of the requested revision.
\r
164 * Finds the tools dependency. If found, add it to the list of things to install.
\r
165 * Returns the archive info dependency, if any.
\r
167 protected ArchiveInfo findToolsDependency(MinToolsPackage platformOrExtra,
\r
168 ArrayList<ArchiveInfo> outArchives,
\r
169 Collection<Archive> selectedArchives,
\r
170 ArrayList<Package> remotePkgs,
\r
171 Package[] localPkgs) {
\r
172 // This is the requirement to match.
\r
173 int rev = platformOrExtra.getMinToolsRevision();
\r
175 if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
\r
176 // Well actually there's no requirement.
\r
180 // First look in local packages.
\r
181 for (Package p : localPkgs) {
\r
182 if (p instanceof ToolPackage) {
\r
183 if (((ToolPackage) p).getRevision() >= rev) {
\r
184 // We found one already installed. We don't report this dependency
\r
185 // as the UI only cares about resolving "newly added dependencies".
\r
191 // Look in archives already scheduled for install
\r
192 for (ArchiveInfo ai : outArchives) {
\r
193 Package p = ai.getNewArchive().getParentPackage();
\r
194 if (p instanceof PlatformPackage) {
\r
195 if (((ToolPackage) p).getRevision() >= rev) {
\r
196 // The dependency is already scheduled for install, nothing else to do.
\r
202 // Otherwise look in the selected archives.
\r
203 for (Archive a : selectedArchives) {
\r
204 Package p = a.getParentPackage();
\r
205 if (p instanceof ToolPackage) {
\r
206 if (((ToolPackage) p).getRevision() >= rev) {
\r
207 // It's not already in the list of things to install, so add it now
\r
208 return insertArchive(a, outArchives,
\r
209 selectedArchives, remotePkgs, localPkgs,
\r
215 // Finally nothing matched, so let's look at all available remote packages
\r
216 fetchRemotePackages(remotePkgs);
\r
217 for (Package p : remotePkgs) {
\r
218 if (p instanceof ToolPackage) {
\r
219 if (((ToolPackage) p).getRevision() >= rev) {
\r
220 // It's not already in the list of things to install, so add the
\r
221 // first compatible archive we can find.
\r
222 for (Archive a : p.getArchives()) {
\r
223 if (a.isCompatible()) {
\r
224 return insertArchive(a, outArchives,
\r
225 selectedArchives, remotePkgs, localPkgs,
\r
233 // We end up here if nothing matches. We don't have a good tools to match.
\r
234 // Seriously, that can't happens unless we totally screwed our repo manifest.
\r
235 // We'll let this one go through anyway.
\r
240 * Resolves dependencies on platform.
\r
242 * An addon depends on having a platform with the same API version.
\r
243 * Finds the platform dependency. If found, add it to the list of things to install.
\r
244 * Returns the archive info dependency, if any.
\r
246 protected ArchiveInfo findPlatformDependency(AddonPackage addon,
\r
247 ArrayList<ArchiveInfo> outArchives,
\r
248 Collection<Archive> selectedArchives,
\r
249 ArrayList<Package> remotePkgs,
\r
250 Package[] localPkgs) {
\r
251 // This is the requirement to match.
\r
252 AndroidVersion v = addon.getVersion();
\r
254 // Find a platform that would satisfy the requirement.
\r
256 // First look in local packages.
\r
257 for (Package p : localPkgs) {
\r
258 if (p instanceof PlatformPackage) {
\r
259 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
260 // We found one already installed. We don't report this dependency
\r
261 // as the UI only cares about resolving "newly added dependencies".
\r
267 // Look in archives already scheduled for install
\r
268 for (ArchiveInfo ai : outArchives) {
\r
269 Package p = ai.getNewArchive().getParentPackage();
\r
270 if (p instanceof PlatformPackage) {
\r
271 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
272 // The dependency is already scheduled for install, nothing else to do.
\r
278 // Otherwise look in the selected archives.
\r
279 for (Archive a : selectedArchives) {
\r
280 Package p = a.getParentPackage();
\r
281 if (p instanceof PlatformPackage) {
\r
282 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
283 // It's not already in the list of things to install, so add it now
\r
284 return insertArchive(a, outArchives,
\r
285 selectedArchives, remotePkgs, localPkgs,
\r
291 // Finally nothing matched, so let's look at all available remote packages
\r
292 fetchRemotePackages(remotePkgs);
\r
293 for (Package p : remotePkgs) {
\r
294 if (p instanceof PlatformPackage) {
\r
295 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
296 // It's not already in the list of things to install, so add the
\r
297 // first compatible archive we can find.
\r
298 for (Archive a : p.getArchives()) {
\r
299 if (a.isCompatible()) {
\r
300 return insertArchive(a, outArchives,
\r
301 selectedArchives, remotePkgs, localPkgs,
\r
309 // We end up here if nothing matches. We don't have a good platform to match.
\r
310 // Seriously, that can't happens unless the repository contains a bogus addon
\r
311 // entry that does not match any existing platform API level.
\r
312 // It's conceivable that a 3rd part addon repo might have error, in which case
\r
313 // we'll let this one go through anyway.
\r
317 /** Fetch all remote packages only if really needed. */
\r
318 protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
\r
319 if (remotePkgs.size() > 0) {
\r
323 // Get all the available packages from all loaded sources
\r
324 RepoSource[] remoteSources = mSources.getSources();
\r
326 for (RepoSource remoteSrc : remoteSources) {
\r
327 Package[] pkgs = remoteSrc.getPackages();
\r
328 if (pkgs != null) {
\r
329 nextPackage: for (Package pkg : pkgs) {
\r
330 for (Archive a : pkg.getArchives()) {
\r
331 // Only add a package if it contains at least one compatible archive
\r
332 if (a.isCompatible()) {
\r
333 remotePkgs.add(pkg);
\r
334 continue nextPackage;
\r