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.DocPackage;
\r
23 import com.android.sdklib.internal.repository.ExtraPackage;
\r
24 import com.android.sdklib.internal.repository.IMinApiLevelDependency;
\r
25 import com.android.sdklib.internal.repository.IMinToolsDependency;
\r
26 import com.android.sdklib.internal.repository.IPackageVersion;
\r
27 import com.android.sdklib.internal.repository.IPlatformDependency;
\r
28 import com.android.sdklib.internal.repository.MinToolsPackage;
\r
29 import com.android.sdklib.internal.repository.Package;
\r
30 import com.android.sdklib.internal.repository.PlatformPackage;
\r
31 import com.android.sdklib.internal.repository.RepoSource;
\r
32 import com.android.sdklib.internal.repository.RepoSources;
\r
33 import com.android.sdklib.internal.repository.SamplePackage;
\r
34 import com.android.sdklib.internal.repository.ToolPackage;
\r
35 import com.android.sdklib.internal.repository.Package.UpdateInfo;
\r
37 import java.util.ArrayList;
\r
38 import java.util.Collection;
\r
39 import java.util.HashMap;
\r
42 * The logic to compute which packages to install, based on the choices
\r
43 * made by the user. This adds dependent packages as needed.
\r
45 * When the user doesn't provide a selection, looks at local package to find
\r
46 * those that can be updated and compute dependencies too.
\r
48 class UpdaterLogic {
\r
51 * Compute which packages to install by taking the user selection
\r
52 * and adding dependent packages as needed.
\r
54 * When the user doesn't provide a selection, looks at local packages to find
\r
55 * those that can be updated and compute dependencies too.
\r
57 public ArrayList<ArchiveInfo> computeUpdates(
\r
58 Collection<Archive> selectedArchives,
\r
59 RepoSources sources,
\r
60 Package[] localPkgs) {
\r
62 ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
\r
63 ArrayList<Package> remotePkgs = new ArrayList<Package>();
\r
64 RepoSource[] remoteSources = sources.getSources();
\r
66 // Create ArchiveInfos out of local (installed) packages.
\r
67 ArchiveInfo[] localArchives = createLocalArchives(localPkgs);
\r
69 if (selectedArchives == null) {
\r
70 selectedArchives = findUpdates(localArchives, remotePkgs, remoteSources);
\r
73 for (Archive a : selectedArchives) {
\r
80 false /*automated*/);
\r
87 * Finds new packages that the user does not have in his/her local SDK
\r
88 * and adds them to the list of archives to install.
\r
90 public void addNewPlatforms(ArrayList<ArchiveInfo> archives,
\r
91 RepoSources sources,
\r
92 Package[] localPkgs) {
\r
94 // Create ArchiveInfos out of local (installed) packages.
\r
95 ArchiveInfo[] localArchives = createLocalArchives(localPkgs);
\r
97 // Find the highest platform installed
\r
98 float currentPlatformScore = 0;
\r
99 float currentSampleScore = 0;
\r
100 float currentAddonScore = 0;
\r
101 float currentDocScore = 0;
\r
102 HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();
\r
103 for (Package p : localPkgs) {
\r
104 int rev = p.getRevision();
\r
106 boolean isPreview = false;
\r
107 if (p instanceof IPackageVersion) {
\r
108 AndroidVersion vers = ((IPackageVersion) p).getVersion();
\r
109 api = vers.getApiLevel();
\r
110 isPreview = vers.isPreview();
\r
113 // The score is 10*api + (1 if preview) + rev/100
\r
114 // This allows previews to rank above a non-preview and
\r
115 // allows revisions to rank appropriately.
\r
116 float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
\r
118 if (p instanceof PlatformPackage) {
\r
119 currentPlatformScore = Math.max(currentPlatformScore, score);
\r
120 } else if (p instanceof SamplePackage) {
\r
121 currentSampleScore = Math.max(currentSampleScore, score);
\r
122 } else if (p instanceof AddonPackage) {
\r
123 currentAddonScore = Math.max(currentAddonScore, score);
\r
124 } else if (p instanceof ExtraPackage) {
\r
125 currentExtraScore.put(((ExtraPackage) p).getPath(), score);
\r
126 } else if (p instanceof DocPackage) {
\r
127 currentDocScore = Math.max(currentDocScore, score);
\r
131 RepoSource[] remoteSources = sources.getSources();
\r
132 ArrayList<Package> remotePkgs = new ArrayList<Package>();
\r
133 fetchRemotePackages(remotePkgs, remoteSources);
\r
135 Package suggestedDoc = null;
\r
137 for (Package p : remotePkgs) {
\r
138 int rev = p.getRevision();
\r
140 boolean isPreview = false;
\r
141 if (p instanceof IPackageVersion) {
\r
142 AndroidVersion vers = ((IPackageVersion) p).getVersion();
\r
143 api = vers.getApiLevel();
\r
144 isPreview = vers.isPreview();
\r
147 float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
\r
149 boolean shouldAdd = false;
\r
150 if (p instanceof PlatformPackage) {
\r
151 shouldAdd = score > currentPlatformScore;
\r
152 } else if (p instanceof SamplePackage) {
\r
153 shouldAdd = score > currentSampleScore;
\r
154 } else if (p instanceof AddonPackage) {
\r
155 shouldAdd = score > currentAddonScore;
\r
156 } else if (p instanceof ExtraPackage) {
\r
157 String key = ((ExtraPackage) p).getPath();
\r
158 shouldAdd = !currentExtraScore.containsKey(key) ||
\r
159 score > currentExtraScore.get(key).floatValue();
\r
160 } else if (p instanceof DocPackage) {
\r
161 // We don't want all the doc, only the most recent one
\r
162 if (score > currentDocScore) {
\r
164 currentDocScore = score;
\r
169 // We should suggest this package for installation.
\r
170 for (Archive a : p.getArchives()) {
\r
171 if (a.isCompatible()) {
\r
174 null /*selectedArchives*/,
\r
178 true /*automated*/);
\r
184 if (suggestedDoc != null) {
\r
185 // We should suggest this package for installation.
\r
186 for (Archive a : suggestedDoc.getArchives()) {
\r
187 if (a.isCompatible()) {
\r
190 null /*selectedArchives*/,
\r
194 true /*automated*/);
\r
201 * Create a array of {@link ArchiveInfo} based on all local (already installed)
\r
202 * packages. The array is always non-null but may be empty.
\r
204 * The local {@link ArchiveInfo} are guaranteed to have one non-null archive
\r
205 * that you can retrieve using {@link ArchiveInfo#getNewArchive()}.
\r
207 protected ArchiveInfo[] createLocalArchives(Package[] localPkgs) {
\r
209 if (localPkgs != null) {
\r
210 ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();
\r
211 for (Package p : localPkgs) {
\r
212 // Only accept packages that have one compatible archive.
\r
213 // Local package should have 1 and only 1 compatible archive anyway.
\r
214 for (Archive a : p.getArchives()) {
\r
215 if (a != null && a.isCompatible()) {
\r
216 // We create an "installed" archive info to wrap the local package.
\r
217 // Note that dependencies are not computed since right now we don't
\r
218 // deal with more than one level of dependencies and installed archives
\r
219 // are deemed implicitly accepted anyway.
\r
220 list.add(new LocalArchiveInfo(a));
\r
225 return list.toArray(new ArchiveInfo[list.size()]);
\r
228 return new ArchiveInfo[0];
\r
232 * Find suitable updates to all current local packages.
\r
234 private Collection<Archive> findUpdates(ArchiveInfo[] localArchives,
\r
235 ArrayList<Package> remotePkgs,
\r
236 RepoSource[] remoteSources) {
\r
237 ArrayList<Archive> updates = new ArrayList<Archive>();
\r
239 fetchRemotePackages(remotePkgs, remoteSources);
\r
241 for (ArchiveInfo ai : localArchives) {
\r
242 Archive na = ai.getNewArchive();
\r
246 Package localPkg = na.getParentPackage();
\r
248 for (Package remotePkg : remotePkgs) {
\r
249 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {
\r
250 // Found a suitable update. Only accept the remote package
\r
251 // if it provides at least one compatible archive.
\r
253 for (Archive a : remotePkg.getArchives()) {
\r
254 if (a.isCompatible()) {
\r
266 private ArchiveInfo insertArchive(Archive archive,
\r
267 ArrayList<ArchiveInfo> outArchives,
\r
268 Collection<Archive> selectedArchives,
\r
269 ArrayList<Package> remotePkgs,
\r
270 RepoSource[] remoteSources,
\r
271 ArchiveInfo[] localArchives,
\r
272 boolean automated) {
\r
273 Package p = archive.getParentPackage();
\r
275 // Is this an update?
\r
276 Archive updatedArchive = null;
\r
277 for (ArchiveInfo ai : localArchives) {
\r
278 Archive a = ai.getNewArchive();
\r
280 Package lp = a.getParentPackage();
\r
282 if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {
\r
283 updatedArchive = a;
\r
288 // Find dependencies
\r
289 ArchiveInfo[] deps = findDependency(p,
\r
296 // Make sure it's not a dup
\r
297 ArchiveInfo ai = null;
\r
299 for (ArchiveInfo ai2 : outArchives) {
\r
300 Archive a2 = ai2.getNewArchive();
\r
301 if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) {
\r
308 ai = new ArchiveInfo(
\r
309 archive, //newArchive
\r
310 updatedArchive, //replaced
\r
313 outArchives.add(ai);
\r
316 if (deps != null) {
\r
317 for (ArchiveInfo d : deps) {
\r
318 d.addDependencyFor(ai);
\r
326 * Resolves dependencies for a given package.
\r
328 * Returns null if no dependencies were found.
\r
329 * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have
\r
330 * at least size 1 and contain no null elements.
\r
332 private ArchiveInfo[] findDependency(Package pkg,
\r
333 ArrayList<ArchiveInfo> outArchives,
\r
334 Collection<Archive> selectedArchives,
\r
335 ArrayList<Package> remotePkgs,
\r
336 RepoSource[] remoteSources,
\r
337 ArchiveInfo[] localArchives) {
\r
339 // Current dependencies can be:
\r
340 // - addon: *always* depends on platform of same API level
\r
341 // - platform: *might* depends on tools of rev >= min-tools-rev
\r
342 // - extra: *might* depends on platform with api >= min-api-level
\r
344 ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();
\r
346 if (pkg instanceof IPlatformDependency) {
\r
347 ArchiveInfo ai = findPlatformDependency(
\r
348 (IPlatformDependency) pkg,
\r
360 if (pkg instanceof IMinToolsDependency) {
\r
362 ArchiveInfo ai = findToolsDependency(
\r
363 (IMinToolsDependency) pkg,
\r
375 if (pkg instanceof IMinApiLevelDependency) {
\r
377 ArchiveInfo ai = findMinApiLevelDependency(
\r
378 (IMinApiLevelDependency) pkg,
\r
390 if (list.size() > 0) {
\r
391 return list.toArray(new ArchiveInfo[list.size()]);
\r
398 * Resolves dependencies on tools.
\r
400 * A platform or an extra package can both have a min-tools-rev, in which case it
\r
401 * depends on having a tools package of the requested revision.
\r
402 * Finds the tools dependency. If found, add it to the list of things to install.
\r
403 * Returns the archive info dependency, if any.
\r
405 protected ArchiveInfo findToolsDependency(
\r
406 IMinToolsDependency pkg,
\r
407 ArrayList<ArchiveInfo> outArchives,
\r
408 Collection<Archive> selectedArchives,
\r
409 ArrayList<Package> remotePkgs,
\r
410 RepoSource[] remoteSources,
\r
411 ArchiveInfo[] localArchives) {
\r
412 // This is the requirement to match.
\r
413 int rev = pkg.getMinToolsRevision();
\r
415 if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
\r
416 // Well actually there's no requirement.
\r
420 // First look in locally installed packages.
\r
421 for (ArchiveInfo ai : localArchives) {
\r
422 Archive a = ai.getNewArchive();
\r
424 Package p = a.getParentPackage();
\r
425 if (p instanceof ToolPackage) {
\r
426 if (((ToolPackage) p).getRevision() >= rev) {
\r
427 // We found one already installed.
\r
434 // Look in archives already scheduled for install
\r
435 for (ArchiveInfo ai : outArchives) {
\r
436 Archive a = ai.getNewArchive();
\r
438 Package p = a.getParentPackage();
\r
439 if (p instanceof ToolPackage) {
\r
440 if (((ToolPackage) p).getRevision() >= rev) {
\r
441 // The dependency is already scheduled for install, nothing else to do.
\r
448 // Otherwise look in the selected archives.
\r
449 if (selectedArchives != null) {
\r
450 for (Archive a : selectedArchives) {
\r
451 Package p = a.getParentPackage();
\r
452 if (p instanceof ToolPackage) {
\r
453 if (((ToolPackage) p).getRevision() >= rev) {
\r
454 // It's not already in the list of things to install, so add it now
\r
455 return insertArchive(a,
\r
461 true /*automated*/);
\r
467 // Finally nothing matched, so let's look at all available remote packages
\r
468 fetchRemotePackages(remotePkgs, remoteSources);
\r
469 for (Package p : remotePkgs) {
\r
470 if (p instanceof ToolPackage) {
\r
471 if (((ToolPackage) p).getRevision() >= rev) {
\r
472 // It's not already in the list of things to install, so add the
\r
473 // first compatible archive we can find.
\r
474 for (Archive a : p.getArchives()) {
\r
475 if (a.isCompatible()) {
\r
476 return insertArchive(a,
\r
482 true /*automated*/);
\r
489 // We end up here if nothing matches. We don't have a good platform to match.
\r
490 // We need to indicate this extra depends on a missing platform archive
\r
491 // so that it can be impossible to install later on.
\r
492 return new MissingToolArchiveInfo(rev);
\r
496 * Resolves dependencies on platform for an addon.
\r
498 * An addon depends on having a platform with the same API level.
\r
500 * Finds the platform dependency. If found, add it to the list of things to install.
\r
501 * Returns the archive info dependency, if any.
\r
503 protected ArchiveInfo findPlatformDependency(
\r
504 IPlatformDependency pkg,
\r
505 ArrayList<ArchiveInfo> outArchives,
\r
506 Collection<Archive> selectedArchives,
\r
507 ArrayList<Package> remotePkgs,
\r
508 RepoSource[] remoteSources,
\r
509 ArchiveInfo[] localArchives) {
\r
510 // This is the requirement to match.
\r
511 AndroidVersion v = pkg.getVersion();
\r
513 // Find a platform that would satisfy the requirement.
\r
515 // First look in locally installed packages.
\r
516 for (ArchiveInfo ai : localArchives) {
\r
517 Archive a = ai.getNewArchive();
\r
519 Package p = a.getParentPackage();
\r
520 if (p instanceof PlatformPackage) {
\r
521 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
522 // We found one already installed.
\r
529 // Look in archives already scheduled for install
\r
530 for (ArchiveInfo ai : outArchives) {
\r
531 Archive a = ai.getNewArchive();
\r
533 Package p = a.getParentPackage();
\r
534 if (p instanceof PlatformPackage) {
\r
535 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
536 // The dependency is already scheduled for install, nothing else to do.
\r
543 // Otherwise look in the selected archives.
\r
544 if (selectedArchives != null) {
\r
545 for (Archive a : selectedArchives) {
\r
546 Package p = a.getParentPackage();
\r
547 if (p instanceof PlatformPackage) {
\r
548 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
549 // It's not already in the list of things to install, so add it now
\r
550 return insertArchive(a,
\r
556 true /*automated*/);
\r
562 // Finally nothing matched, so let's look at all available remote packages
\r
563 fetchRemotePackages(remotePkgs, remoteSources);
\r
564 for (Package p : remotePkgs) {
\r
565 if (p instanceof PlatformPackage) {
\r
566 if (v.equals(((PlatformPackage) p).getVersion())) {
\r
567 // It's not already in the list of things to install, so add the
\r
568 // first compatible archive we can find.
\r
569 for (Archive a : p.getArchives()) {
\r
570 if (a.isCompatible()) {
\r
571 return insertArchive(a,
\r
577 true /*automated*/);
\r
584 // We end up here if nothing matches. We don't have a good platform to match.
\r
585 // We need to indicate this addon depends on a missing platform archive
\r
586 // so that it can be impossible to install later on.
\r
587 return new MissingPlatformArchiveInfo(pkg.getVersion());
\r
591 * Resolves platform dependencies for extras.
\r
592 * An extra depends on having a platform with a minimun API level.
\r
594 * We try to return the highest API level available above the specified minimum.
\r
595 * Note that installed packages have priority so if one installed platform satisfies
\r
596 * the dependency, we'll use it even if there's a higher API platform available but
\r
597 * not installed yet.
\r
599 * Finds the platform dependency. If found, add it to the list of things to install.
\r
600 * Returns the archive info dependency, if any.
\r
602 protected ArchiveInfo findMinApiLevelDependency(
\r
603 IMinApiLevelDependency pkg,
\r
604 ArrayList<ArchiveInfo> outArchives,
\r
605 Collection<Archive> selectedArchives,
\r
606 ArrayList<Package> remotePkgs,
\r
607 RepoSource[] remoteSources,
\r
608 ArchiveInfo[] localArchives) {
\r
610 int api = pkg.getMinApiLevel();
\r
612 if (api == ExtraPackage.MIN_API_LEVEL_NOT_SPECIFIED) {
\r
616 // Find a platform that would satisfy the requirement.
\r
618 // First look in locally installed packages.
\r
619 for (ArchiveInfo ai : localArchives) {
\r
620 Archive a = ai.getNewArchive();
\r
622 Package p = a.getParentPackage();
\r
623 if (p instanceof PlatformPackage) {
\r
624 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {
\r
625 // We found one already installed.
\r
632 // Look in archives already scheduled for install
\r
634 ArchiveInfo foundAi = null;
\r
636 for (ArchiveInfo ai : outArchives) {
\r
637 Archive a = ai.getNewArchive();
\r
639 Package p = a.getParentPackage();
\r
640 if (p instanceof PlatformPackage) {
\r
641 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {
\r
642 if (api > foundApi) {
\r
651 if (foundAi != null) {
\r
652 // The dependency is already scheduled for install, nothing else to do.
\r
656 // Otherwise look in the selected archives *or* available remote packages
\r
657 // and takes the best out of the two sets.
\r
659 Archive foundArchive = null;
\r
660 if (selectedArchives != null) {
\r
661 for (Archive a : selectedArchives) {
\r
662 Package p = a.getParentPackage();
\r
663 if (p instanceof PlatformPackage) {
\r
664 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {
\r
665 if (api > foundApi) {
\r
674 // Finally nothing matched, so let's look at all available remote packages
\r
675 fetchRemotePackages(remotePkgs, remoteSources);
\r
676 for (Package p : remotePkgs) {
\r
677 if (p instanceof PlatformPackage) {
\r
678 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {
\r
679 if (api > foundApi) {
\r
680 // It's not already in the list of things to install, so add the
\r
681 // first compatible archive we can find.
\r
682 for (Archive a : p.getArchives()) {
\r
683 if (a.isCompatible()) {
\r
693 if (foundArchive != null) {
\r
694 // It's not already in the list of things to install, so add it now
\r
695 return insertArchive(foundArchive,
\r
701 true /*automated*/);
\r
704 // We end up here if nothing matches. We don't have a good platform to match.
\r
705 // We need to indicate this extra depends on a missing platform archive
\r
706 // so that it can be impossible to install later on.
\r
707 return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/));
\r
710 /** Fetch all remote packages only if really needed. */
\r
711 protected void fetchRemotePackages(ArrayList<Package> remotePkgs, RepoSource[] remoteSources) {
\r
712 if (remotePkgs.size() > 0) {
\r
716 for (RepoSource remoteSrc : remoteSources) {
\r
717 Package[] pkgs = remoteSrc.getPackages();
\r
718 if (pkgs != null) {
\r
719 nextPackage: for (Package pkg : pkgs) {
\r
720 for (Archive a : pkg.getArchives()) {
\r
721 // Only add a package if it contains at least one compatible archive
\r
722 if (a.isCompatible()) {
\r
723 remotePkgs.add(pkg);
\r
724 continue nextPackage;
\r
734 * A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed
\r
735 * "local" package/archive.
\r
737 * In this case, the "new Archive" is still expected to be non null and the
\r
738 * "replaced Archive" isnull. Installed archives are always accepted and never
\r
741 * Dependencies are not set.
\r
743 private static class LocalArchiveInfo extends ArchiveInfo {
\r
745 public LocalArchiveInfo(Archive localArchive) {
\r
746 super(localArchive, null /*replaced*/, null /*dependsOn*/);
\r
749 /** Installed archives are always accepted. */
\r
751 public boolean isAccepted() {
\r
755 /** Installed archives are never rejected. */
\r
757 public boolean isRejected() {
\r
763 * A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a
\r
764 * package/archive that we <em>really</em> need as a dependency but that we don't have.
\r
766 * This is currently used for addons and extras in case we can't find a matching base platform.
\r
768 * This kind of archive has specific properties: the new archive to install is null,
\r
769 * there are no dependencies and no archive is being replaced. The info can never be
\r
770 * accepted and is always rejected.
\r
772 private static class MissingPlatformArchiveInfo extends ArchiveInfo {
\r
774 private final AndroidVersion mVersion;
\r
777 * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the
\r
778 * given platform version is missing.
\r
780 public MissingPlatformArchiveInfo(AndroidVersion version) {
\r
781 super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);
\r
782 mVersion = version;
\r
785 /** Missing archives are never accepted. */
\r
787 public boolean isAccepted() {
\r
791 /** Missing archives are always rejected. */
\r
793 public boolean isRejected() {
\r
798 public String getShortDescription() {
\r
799 return String.format("Missing SDK Platform Android%1$s, API %2$d",
\r
800 mVersion.isPreview() ? " Preview" : "",
\r
801 mVersion.getApiLevel());
\r
806 * A {@link MissingToolArchiveInfo} is an {@link ArchiveInfo} that represents a
\r
807 * package/archive that we <em>really</em> need as a dependency but that we don't have.
\r
809 * This is currently used for extras in case we can't find a matching tool revision.
\r
811 * This kind of archive has specific properties: the new archive to install is null,
\r
812 * there are no dependencies and no archive is being replaced. The info can never be
\r
813 * accepted and is always rejected.
\r
815 private static class MissingToolArchiveInfo extends ArchiveInfo {
\r
817 private final int mRevision;
\r
820 * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the
\r
821 * given platform version is missing.
\r
823 public MissingToolArchiveInfo(int revision) {
\r
824 super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);
\r
825 mRevision = revision;
\r
828 /** Missing archives are never accepted. */
\r
830 public boolean isAccepted() {
\r
834 /** Missing archives are always rejected. */
\r
836 public boolean isRejected() {
\r
841 public String getShortDescription() {
\r
842 return String.format("Missing Android SDK Tools, revision %1$d", mRevision);
\r