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.Package;
\r
23 import com.android.sdklib.internal.repository.PlatformPackage;
\r
24 import com.android.sdklib.internal.repository.RepoSource;
\r
25 import com.android.sdklib.internal.repository.RepoSources;
\r
26 import com.android.sdklib.internal.repository.ToolPackage;
\r
27 import com.android.sdklib.internal.repository.Package.UpdateInfo;
\r
29 import java.util.ArrayList;
\r
30 import java.util.Collection;
\r
32 class UpdaterLogic {
\r
34 private RepoSources mSources;
\r
36 public ArrayList<ArchiveInfo> computeUpdates(
\r
37 Collection<Archive> selectedArchives,
\r
38 RepoSources sources,
\r
39 Package[] localPkgs) {
\r
42 ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
\r
43 ArrayList<Package> remotePkgs = new ArrayList<Package>();
\r
45 if (selectedArchives == null) {
\r
46 selectedArchives = findUpdates(localPkgs, remotePkgs);
\r
49 for (Archive a : selectedArchives) {
\r
50 insertArchive(a, archives, selectedArchives, remotePkgs, localPkgs, false);
\r
58 * Find suitable updates to all current local packages.
\r
60 private Collection<Archive> findUpdates(Package[] localPkgs, ArrayList<Package> remotePkgs) {
\r
61 ArrayList<Archive> updates = new ArrayList<Archive>();
\r
63 fetchRemotePackages(remotePkgs);
\r
65 for (Package localPkg : localPkgs) {
\r
66 for (Package remotePkg : remotePkgs) {
\r
67 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
\r
68 // Found a suitable update. Only accept the remote package
\r
69 // if it provides at least one compatible archive.
\r
71 for (Archive a : remotePkg.getArchives()) {
\r
72 if (a.isCompatible()) {
\r
84 private ArchiveInfo insertArchive(Archive archive,
\r
85 ArrayList<ArchiveInfo> outArchives,
\r
86 Collection<Archive> selectedArchives,
\r
87 ArrayList<Package> remotePkgs,
\r
88 Package[] localPkgs,
\r
89 boolean automated) {
\r
90 Package p = archive.getParentPackage();
\r
92 // Is this an update?
\r
93 Archive updatedArchive = null;
\r
94 for (Package lp : localPkgs) {
\r
95 assert lp.getArchives().length == 1;
\r
96 if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {
\r
97 updatedArchive = lp.getArchives()[0];
\r
101 // find dependencies
\r
102 ArchiveInfo dep = findDependency(p, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
104 ArchiveInfo ai = new ArchiveInfo(
\r
105 archive, //newArchive
\r
106 updatedArchive, //replaced
\r
110 outArchives.add(ai);
\r
112 dep.addDependencyFor(ai);
\r
118 private ArchiveInfo findDependency(Package pkg,
\r
119 ArrayList<ArchiveInfo> outArchives,
\r
120 Collection<Archive> selectedArchives,
\r
121 ArrayList<Package> remotePkgs,
\r
122 Package[] localPkgs) {
\r
124 // Current dependencies can be:
\r
125 // - addon: *always* depends on platform of same API level
\r
126 // - platform: *might* depends on tools of rev >= min-tools-rev
\r
128 if (pkg instanceof AddonPackage) {
\r
129 AddonPackage addon = (AddonPackage) pkg;
\r
131 return findAddonDependency(
\r
132 addon, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
134 } else if (pkg instanceof PlatformPackage) {
\r
135 PlatformPackage platform = (PlatformPackage) pkg;
\r
137 return findPlatformDependency(
\r
138 platform, outArchives, selectedArchives, remotePkgs, localPkgs);
\r
145 * A platform can have a min-tools-rev, in which case it depends on having a tools package
\r
146 * of the requested revision.
\r
147 * Finds the tools dependency. If found, add it to the list of things to install.
\r
148 * Returns the archive info dependency, if any.
\r
150 protected ArchiveInfo findPlatformDependency(PlatformPackage platform,
\r
151 ArrayList<ArchiveInfo> outArchives,
\r
152 Collection<Archive> selectedArchives,
\r
153 ArrayList<Package> remotePkgs,
\r
154 Package[] localPkgs) {
\r
155 // This is the requirement to match.
\r
156 int rev = platform.getMinToolsRevision();
\r
158 if (rev == PlatformPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
\r
159 // Well actually there's no requirement.
\r
163 // First look in local packages.
\r
164 for (Package p : localPkgs) {
\r
165 if (p instanceof ToolPackage) {
\r
166 if (((ToolPackage) p).getRevision() >= rev) {
\r
167 // We found one already installed. We don't report this dependency
\r
168 // as the UI only cares about resolving "newly added dependencies".
\r
174 // Look in archives already scheduled for install
\r
175 for (ArchiveInfo ai : outArchives) {
\r
176 Package p = ai.getNewArchive().getParentPackage();
\r
177 if (p instanceof PlatformPackage) {
\r
178 if (((ToolPackage) p).getRevision() >= rev) {
\r
179 // The dependency is already scheduled for install, nothing else to do.
\r
185 // Otherwise look in the selected archives.
\r
186 for (Archive a : selectedArchives) {
\r
187 Package p = a.getParentPackage();
\r
188 if (p instanceof ToolPackage) {
\r
189 if (((ToolPackage) p).getRevision() >= rev) {
\r
190 // It's not already in the list of things to install, so add it now
\r
191 return insertArchive(a, outArchives,
\r
192 selectedArchives, remotePkgs, localPkgs,
\r
198 // Finally nothing matched, so let's look at all available remote packages
\r
199 fetchRemotePackages(remotePkgs);
\r
200 for (Package p : remotePkgs) {
\r
201 if (p instanceof ToolPackage) {
\r
202 if (((ToolPackage) p).getRevision() >= rev) {
\r
203 // It's not already in the list of things to install, so add the
\r
204 // first compatible archive we can find.
\r
205 for (Archive a : p.getArchives()) {
\r
206 if (a.isCompatible()) {
\r
207 return insertArchive(a, outArchives,
\r
208 selectedArchives, remotePkgs, localPkgs,
\r
216 // We end up here if nothing matches. We don't have a good tools to match.
\r
217 // Seriously, that can't happens unless we totally screwed our repo manifest.
\r
218 // We'll let this one go through anyway.
\r
223 * An addon depends on having a platform with the same API version.
\r
224 * Finds the platform dependency. If found, add it to the list of things to install.
\r
225 * Returns the archive info dependency, if any.
\r
227 protected ArchiveInfo findAddonDependency(AddonPackage addon,
\r
228 ArrayList<ArchiveInfo> outArchives,
\r
229 Collection<Archive> selectedArchives,
\r
230 ArrayList<Package> remotePkgs,
\r
231 Package[] localPkgs) {
\r
232 // This is the requirement to match.
\r
233 AndroidVersion v = addon.getVersion();
\r
235 // Find a platform that would satisfy the requirement.
\r
237 // First look in local packages.
\r
238 for (Package p : localPkgs) {
\r
239 if (p instanceof PlatformPackage) {
\r
240 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
241 // We found one already installed. We don't report this dependency
\r
242 // as the UI only cares about resolving "newly added dependencies".
\r
248 // Look in archives already scheduled for install
\r
249 for (ArchiveInfo ai : outArchives) {
\r
250 Package p = ai.getNewArchive().getParentPackage();
\r
251 if (p instanceof PlatformPackage) {
\r
252 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
253 // The dependency is already scheduled for install, nothing else to do.
\r
259 // Otherwise look in the selected archives.
\r
260 for (Archive a : selectedArchives) {
\r
261 Package p = a.getParentPackage();
\r
262 if (p instanceof PlatformPackage) {
\r
263 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
264 // It's not already in the list of things to install, so add it now
\r
265 return insertArchive(a, outArchives,
\r
266 selectedArchives, remotePkgs, localPkgs,
\r
272 // Finally nothing matched, so let's look at all available remote packages
\r
273 fetchRemotePackages(remotePkgs);
\r
274 for (Package p : remotePkgs) {
\r
275 if (p instanceof PlatformPackage) {
\r
276 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
277 // It's not already in the list of things to install, so add the
\r
278 // first compatible archive we can find.
\r
279 for (Archive a : p.getArchives()) {
\r
280 if (a.isCompatible()) {
\r
281 return insertArchive(a, outArchives,
\r
282 selectedArchives, remotePkgs, localPkgs,
\r
290 // We end up here if nothing matches. We don't have a good platform to match.
\r
291 // Seriously, that can't happens unless the repository contains a bogus addon
\r
292 // entry that does not match any existing platform API level.
\r
293 // It's conceivable that a 3rd part addon repo might have error, in which case
\r
294 // we'll let this one go through anyway.
\r
298 /** Fetch all remote packages only if really needed. */
\r
299 protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
\r
300 if (remotePkgs.size() > 0) {
\r
304 // Get all the available packages from all loaded sources
\r
305 RepoSource[] remoteSources = mSources.getSources();
\r
307 for (RepoSource remoteSrc : remoteSources) {
\r
308 Package[] pkgs = remoteSrc.getPackages();
\r
309 if (pkgs != null) {
\r
310 nextPackage: for (Package pkg : pkgs) {
\r
311 for (Archive a : pkg.getArchives()) {
\r
312 // Only add a package if it contains at least one compatible archive
\r
313 if (a.isCompatible()) {
\r
314 remotePkgs.add(pkg);
\r
315 continue nextPackage;
\r