OSDN Git Service

SDK Manager: fix displayed dependencies at installation.
[android-x86/sdk.git] / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / repository / UpdaterLogic.java
index 7ec0214..14f3da8 100755 (executable)
@@ -21,12 +21,16 @@ import com.android.sdklib.internal.repository.AddonPackage;
 import com.android.sdklib.internal.repository.Archive;\r
 import com.android.sdklib.internal.repository.DocPackage;\r
 import com.android.sdklib.internal.repository.ExtraPackage;\r
+import com.android.sdklib.internal.repository.IMinApiLevelDependency;\r
+import com.android.sdklib.internal.repository.IMinToolsDependency;\r
 import com.android.sdklib.internal.repository.IPackageVersion;\r
+import com.android.sdklib.internal.repository.IPlatformDependency;\r
 import com.android.sdklib.internal.repository.MinToolsPackage;\r
 import com.android.sdklib.internal.repository.Package;\r
 import com.android.sdklib.internal.repository.PlatformPackage;\r
 import com.android.sdklib.internal.repository.RepoSource;\r
 import com.android.sdklib.internal.repository.RepoSources;\r
+import com.android.sdklib.internal.repository.SamplePackage;\r
 import com.android.sdklib.internal.repository.ToolPackage;\r
 import com.android.sdklib.internal.repository.Package.UpdateInfo;\r
 \r
@@ -59,8 +63,11 @@ class UpdaterLogic {
         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
         RepoSource[] remoteSources = sources.getSources();\r
 \r
+        // Create ArchiveInfos out of local (installed) packages.\r
+        ArchiveInfo[] localArchives = createLocalArchives(localPkgs);\r
+\r
         if (selectedArchives == null) {\r
-            selectedArchives = findUpdates(localPkgs, remotePkgs, remoteSources);\r
+            selectedArchives = findUpdates(localArchives, remotePkgs, remoteSources);\r
         }\r
 \r
         for (Archive a : selectedArchives) {\r
@@ -69,7 +76,7 @@ class UpdaterLogic {
                     selectedArchives,\r
                     remotePkgs,\r
                     remoteSources,\r
-                    localPkgs,\r
+                    localArchives,\r
                     false /*automated*/);\r
         }\r
 \r
@@ -77,15 +84,19 @@ class UpdaterLogic {
     }\r
 \r
     /**\r
-     * Finds new platforms that the user does not have in his/her local SDK\r
+     * Finds new packages that the user does not have in his/her local SDK\r
      * and adds them to the list of archives to install.\r
      */\r
     public void addNewPlatforms(ArrayList<ArchiveInfo> archives,\r
             RepoSources sources,\r
             Package[] localPkgs) {\r
 \r
+        // Create ArchiveInfos out of local (installed) packages.\r
+        ArchiveInfo[] localArchives = createLocalArchives(localPkgs);\r
+\r
         // Find the highest platform installed\r
         float currentPlatformScore = 0;\r
+        float currentSampleScore = 0;\r
         float currentAddonScore = 0;\r
         float currentDocScore = 0;\r
         HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();\r
@@ -93,7 +104,7 @@ class UpdaterLogic {
             int rev = p.getRevision();\r
             int api = 0;\r
             boolean isPreview = false;\r
-            if (p instanceof  IPackageVersion) {\r
+            if (p instanceof IPackageVersion) {\r
                 AndroidVersion vers = ((IPackageVersion) p).getVersion();\r
                 api = vers.getApiLevel();\r
                 isPreview = vers.isPreview();\r
@@ -106,6 +117,8 @@ class UpdaterLogic {
 \r
             if (p instanceof PlatformPackage) {\r
                 currentPlatformScore = Math.max(currentPlatformScore, score);\r
+            } else if (p instanceof SamplePackage) {\r
+                currentSampleScore = Math.max(currentSampleScore, score);\r
             } else if (p instanceof AddonPackage) {\r
                 currentAddonScore = Math.max(currentAddonScore, score);\r
             } else if (p instanceof ExtraPackage) {\r
@@ -136,6 +149,8 @@ class UpdaterLogic {
             boolean shouldAdd = false;\r
             if (p instanceof PlatformPackage) {\r
                 shouldAdd = score > currentPlatformScore;\r
+            } else if (p instanceof SamplePackage) {\r
+                shouldAdd = score > currentSampleScore;\r
             } else if (p instanceof AddonPackage) {\r
                 shouldAdd = score > currentAddonScore;\r
             } else if (p instanceof ExtraPackage) {\r
@@ -159,7 +174,7 @@ class UpdaterLogic {
                                 null /*selectedArchives*/,\r
                                 remotePkgs,\r
                                 remoteSources,\r
-                                localPkgs,\r
+                                localArchives,\r
                                 true /*automated*/);\r
                     }\r
                 }\r
@@ -175,25 +190,61 @@ class UpdaterLogic {
                             null /*selectedArchives*/,\r
                             remotePkgs,\r
                             remoteSources,\r
-                            localPkgs,\r
+                            localArchives,\r
                             true /*automated*/);\r
                 }\r
             }\r
         }\r
+    }\r
+\r
+    /**\r
+     * Create a array of {@link ArchiveInfo} based on all local (already installed)\r
+     * packages. The array is always non-null but may be empty.\r
+     * <p/>\r
+     * The local {@link ArchiveInfo} are guaranteed to have one non-null archive\r
+     * that you can retrieve using {@link ArchiveInfo#getNewArchive()}.\r
+     */\r
+    protected ArchiveInfo[] createLocalArchives(Package[] localPkgs) {\r
+\r
+        if (localPkgs != null) {\r
+            ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();\r
+            for (Package p : localPkgs) {\r
+                // Only accept packages that have one compatible archive.\r
+                // Local package should have 1 and only 1 compatible archive anyway.\r
+                for (Archive a : p.getArchives()) {\r
+                    if (a != null && a.isCompatible()) {\r
+                        // We create an "installed" archive info to wrap the local package.\r
+                        // Note that dependencies are not computed since right now we don't\r
+                        // deal with more than one level of dependencies and installed archives\r
+                        // are deemed implicitly accepted anyway.\r
+                        list.add(new LocalArchiveInfo(a));\r
+                    }\r
+                }\r
+            }\r
+\r
+            return list.toArray(new ArchiveInfo[list.size()]);\r
+        }\r
 \r
+        return new ArchiveInfo[0];\r
     }\r
 \r
     /**\r
      * Find suitable updates to all current local packages.\r
      */\r
-    private Collection<Archive> findUpdates(Package[] localPkgs,\r
+    private Collection<Archive> findUpdates(ArchiveInfo[] localArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources) {\r
         ArrayList<Archive> updates = new ArrayList<Archive>();\r
 \r
         fetchRemotePackages(remotePkgs, remoteSources);\r
 \r
-        for (Package localPkg : localPkgs) {\r
+        for (ArchiveInfo ai : localArchives) {\r
+            Archive na = ai.getNewArchive();\r
+            if (na == null) {\r
+                continue;\r
+            }\r
+            Package localPkg = na.getParentPackage();\r
+\r
             for (Package remotePkg : remotePkgs) {\r
                 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {\r
                     // Found a suitable update. Only accept the remote package\r
@@ -217,16 +268,20 @@ class UpdaterLogic {
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources,\r
-            Package[] localPkgs,\r
+            ArchiveInfo[] localArchives,\r
             boolean automated) {\r
         Package p = archive.getParentPackage();\r
 \r
         // Is this an update?\r
         Archive updatedArchive = null;\r
-        for (Package lp : localPkgs) {\r
-            assert lp.getArchives().length == 1;\r
-            if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {\r
-                updatedArchive = lp.getArchives()[0];\r
+        for (ArchiveInfo ai : localArchives) {\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package lp = a.getParentPackage();\r
+\r
+                if (lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {\r
+                    updatedArchive = a;\r
+                }\r
             }\r
         }\r
 \r
@@ -236,13 +291,14 @@ class UpdaterLogic {
                 selectedArchives,\r
                 remotePkgs,\r
                 remoteSources,\r
-                localPkgs);\r
+                localArchives);\r
 \r
         // Make sure it's not a dup\r
         ArchiveInfo ai = null;\r
 \r
         for (ArchiveInfo ai2 : outArchives) {\r
-            if (ai2.getNewArchive().getParentPackage().sameItemAs(archive.getParentPackage())) {\r
+            Archive a2 = ai2.getNewArchive();\r
+            if (a2 != null && a2.getParentPackage().sameItemAs(archive.getParentPackage())) {\r
                 ai = ai2;\r
                 break;\r
             }\r
@@ -266,68 +322,75 @@ class UpdaterLogic {
         return ai;\r
     }\r
 \r
+    /**\r
+     * Resolves dependencies for a given package.\r
+     *\r
+     * Returns null if no dependencies were found.\r
+     * Otherwise return an array of {@link ArchiveInfo}, which is guaranteed to have\r
+     * at least size 1 and contain no null elements.\r
+     */\r
     private ArchiveInfo[] findDependency(Package pkg,\r
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources,\r
-            Package[] localPkgs) {\r
+            ArchiveInfo[] localArchives) {\r
 \r
         // Current dependencies can be:\r
         // - addon: *always* depends on platform of same API level\r
         // - platform: *might* depends on tools of rev >= min-tools-rev\r
         // - extra: *might* depends on platform with api >= min-api-level\r
 \r
-        if (pkg instanceof AddonPackage) {\r
-            AddonPackage addon = (AddonPackage) pkg;\r
+        ArrayList<ArchiveInfo> list = new ArrayList<ArchiveInfo>();\r
 \r
+        if (pkg instanceof IPlatformDependency) {\r
             ArchiveInfo ai = findPlatformDependency(\r
-                    addon,\r
+                    (IPlatformDependency) pkg,\r
                     outArchives,\r
                     selectedArchives,\r
                     remotePkgs,\r
                     remoteSources,\r
-                    localPkgs);\r
+                    localArchives);\r
 \r
             if (ai != null) {\r
-                return new ArchiveInfo[] { ai };\r
+                list.add(ai);\r
             }\r
+        }\r
 \r
-        } else if (pkg instanceof MinToolsPackage) {\r
-            MinToolsPackage platformOrExtra = (MinToolsPackage) pkg;\r
+        if (pkg instanceof IMinToolsDependency) {\r
 \r
-            int n = 0;\r
-            ArchiveInfo ai1 = findToolsDependency(\r
-                    platformOrExtra,\r
+            ArchiveInfo ai = findToolsDependency(\r
+                    (IMinToolsDependency) pkg,\r
                     outArchives,\r
                     selectedArchives,\r
                     remotePkgs,\r
                     remoteSources,\r
-                    localPkgs);\r
-\r
-            n += ai1 == null ? 0 : 1;\r
-\r
-            ArchiveInfo ai2 = null;\r
-            if (pkg instanceof ExtraPackage) {\r
-                ai2 = findExtraPlatformDependency(\r
-                        (ExtraPackage) pkg,\r
-                        outArchives,\r
-                        selectedArchives,\r
-                        remotePkgs,\r
-                        remoteSources,\r
-                        localPkgs);\r
+                    localArchives);\r
+\r
+            if (ai != null) {\r
+                list.add(ai);\r
             }\r
+        }\r
 \r
-            n += ai2 == null ? 0 : 1;\r
+        if (pkg instanceof IMinApiLevelDependency) {\r
+\r
+            ArchiveInfo ai = findMinApiLevelDependency(\r
+                    (IMinApiLevelDependency) pkg,\r
+                    outArchives,\r
+                    selectedArchives,\r
+                    remotePkgs,\r
+                    remoteSources,\r
+                    localArchives);\r
 \r
-            if (n > 0) {\r
-                ArchiveInfo[] ais = new ArchiveInfo[n];\r
-                ais[0] = ai1 != null ? ai1 : ai2;\r
-                if (n > 1) ais[1] = ai2;\r
-                return ais;\r
+            if (ai != null) {\r
+                list.add(ai);\r
             }\r
         }\r
 \r
+        if (list.size() > 0) {\r
+            return list.toArray(new ArchiveInfo[list.size()]);\r
+        }\r
+\r
         return null;\r
     }\r
 \r
@@ -339,14 +402,15 @@ class UpdaterLogic {
      * Finds the tools dependency. If found, add it to the list of things to install.\r
      * Returns the archive info dependency, if any.\r
      */\r
-    protected ArchiveInfo findToolsDependency(MinToolsPackage platformOrExtra,\r
+    protected ArchiveInfo findToolsDependency(\r
+            IMinToolsDependency pkg,\r
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources,\r
-            Package[] localPkgs) {\r
+            ArchiveInfo[] localArchives) {\r
         // This is the requirement to match.\r
-        int rev = platformOrExtra.getMinToolsRevision();\r
+        int rev = pkg.getMinToolsRevision();\r
 \r
         if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {\r
             // Well actually there's no requirement.\r
@@ -354,23 +418,29 @@ class UpdaterLogic {
         }\r
 \r
         // First look in locally installed packages.\r
-        for (Package p : localPkgs) {\r
-            if (p instanceof ToolPackage) {\r
-                if (((ToolPackage) p).getRevision() >= rev) {\r
-                    // We found one already installed. We don't report this dependency\r
-                    // as the UI only cares about resolving "newly added dependencies".\r
-                    return null;\r
+        for (ArchiveInfo ai : localArchives) {\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof ToolPackage) {\r
+                    if (((ToolPackage) p).getRevision() >= rev) {\r
+                        // We found one already installed.\r
+                        return null;\r
+                    }\r
                 }\r
             }\r
         }\r
 \r
         // Look in archives already scheduled for install\r
         for (ArchiveInfo ai : outArchives) {\r
-            Package p = ai.getNewArchive().getParentPackage();\r
-            if (p instanceof ToolPackage) {\r
-                if (((ToolPackage) p).getRevision() >= rev) {\r
-                    // The dependency is already scheduled for install, nothing else to do.\r
-                    return ai;\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof ToolPackage) {\r
+                    if (((ToolPackage) p).getRevision() >= rev) {\r
+                        // The dependency is already scheduled for install, nothing else to do.\r
+                        return ai;\r
+                    }\r
                 }\r
             }\r
         }\r
@@ -382,8 +452,12 @@ class UpdaterLogic {
                 if (p instanceof ToolPackage) {\r
                     if (((ToolPackage) p).getRevision() >= rev) {\r
                         // It's not already in the list of things to install, so add it now\r
-                        return insertArchive(a, outArchives,\r
-                                selectedArchives, remotePkgs, remoteSources, localPkgs,\r
+                        return insertArchive(a,\r
+                                outArchives,\r
+                                selectedArchives,\r
+                                remotePkgs,\r
+                                remoteSources,\r
+                                localArchives,\r
                                 true /*automated*/);\r
                     }\r
                 }\r
@@ -399,8 +473,12 @@ class UpdaterLogic {
                     // first compatible archive we can find.\r
                     for (Archive a : p.getArchives()) {\r
                         if (a.isCompatible()) {\r
-                            return insertArchive(a, outArchives,\r
-                                    selectedArchives, remotePkgs, remoteSources, localPkgs,\r
+                            return insertArchive(a,\r
+                                    outArchives,\r
+                                    selectedArchives,\r
+                                    remotePkgs,\r
+                                    remoteSources,\r
+                                    localArchives,\r
                                     true /*automated*/);\r
                         }\r
                     }\r
@@ -408,14 +486,14 @@ class UpdaterLogic {
             }\r
         }\r
 \r
-        // We end up here if nothing matches. We don't have a good tools to match.\r
-        // Seriously, that can't happens unless we totally screwed our repo manifest.\r
-        // We'll let this one go through anyway.\r
-        return null;\r
+        // We end up here if nothing matches. We don't have a good platform to match.\r
+        // We need to indicate this extra depends on a missing platform archive\r
+        // so that it can be impossible to install later on.\r
+        return new MissingToolArchiveInfo(rev);\r
     }\r
 \r
     /**\r
-     * Resolves dependencies on platform.\r
+     * Resolves dependencies on platform for an addon.\r
      *\r
      * An addon depends on having a platform with the same API level.\r
      *\r
@@ -423,35 +501,41 @@ class UpdaterLogic {
      * Returns the archive info dependency, if any.\r
      */\r
     protected ArchiveInfo findPlatformDependency(\r
-            AddonPackage addon,\r
+            IPlatformDependency pkg,\r
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources,\r
-            Package[] localPkgs) {\r
+            ArchiveInfo[] localArchives) {\r
         // This is the requirement to match.\r
-        AndroidVersion v = addon.getVersion();\r
+        AndroidVersion v = pkg.getVersion();\r
 \r
         // Find a platform that would satisfy the requirement.\r
 \r
         // First look in locally installed packages.\r
-        for (Package p : localPkgs) {\r
-            if (p instanceof PlatformPackage) {\r
-                if (v.equals(((PlatformPackage) p).getVersion())) {\r
-                    // We found one already installed. We don't report this dependency\r
-                    // as the UI only cares about resolving "newly added dependencies".\r
-                    return null;\r
+        for (ArchiveInfo ai : localArchives) {\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof PlatformPackage) {\r
+                    if (v.equals(((PlatformPackage) p).getVersion())) {\r
+                        // We found one already installed.\r
+                        return null;\r
+                    }\r
                 }\r
             }\r
         }\r
 \r
         // Look in archives already scheduled for install\r
         for (ArchiveInfo ai : outArchives) {\r
-            Package p = ai.getNewArchive().getParentPackage();\r
-            if (p instanceof PlatformPackage) {\r
-                if (v.equals(((PlatformPackage) p).getVersion())) {\r
-                    // The dependency is already scheduled for install, nothing else to do.\r
-                    return ai;\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof PlatformPackage) {\r
+                    if (v.equals(((PlatformPackage) p).getVersion())) {\r
+                        // The dependency is already scheduled for install, nothing else to do.\r
+                        return ai;\r
+                    }\r
                 }\r
             }\r
         }\r
@@ -463,8 +547,12 @@ class UpdaterLogic {
                 if (p instanceof PlatformPackage) {\r
                     if (v.equals(((PlatformPackage) p).getVersion())) {\r
                         // It's not already in the list of things to install, so add it now\r
-                        return insertArchive(a, outArchives,\r
-                                selectedArchives, remotePkgs, remoteSources, localPkgs,\r
+                        return insertArchive(a,\r
+                                outArchives,\r
+                                selectedArchives,\r
+                                remotePkgs,\r
+                                remoteSources,\r
+                                localArchives,\r
                                 true /*automated*/);\r
                     }\r
                 }\r
@@ -480,8 +568,12 @@ class UpdaterLogic {
                     // first compatible archive we can find.\r
                     for (Archive a : p.getArchives()) {\r
                         if (a.isCompatible()) {\r
-                            return insertArchive(a, outArchives,\r
-                                    selectedArchives, remotePkgs, remoteSources, localPkgs,\r
+                            return insertArchive(a,\r
+                                    outArchives,\r
+                                    selectedArchives,\r
+                                    remotePkgs,\r
+                                    remoteSources,\r
+                                    localArchives,\r
                                     true /*automated*/);\r
                         }\r
                     }\r
@@ -490,11 +582,9 @@ class UpdaterLogic {
         }\r
 \r
         // We end up here if nothing matches. We don't have a good platform to match.\r
-        // Seriously, that can't happens unless the repository contains a bogus addon\r
-        // entry that does not match any existing platform API level.\r
-        // It's conceivable that a 3rd part addon repo might have error, in which case\r
-        // we'll let this one go through anyway.\r
-        return null;\r
+        // We need to indicate this addon depends on a missing platform archive\r
+        // so that it can be impossible to install later on.\r
+        return new MissingPlatformArchiveInfo(pkg.getVersion());\r
     }\r
 \r
     /**\r
@@ -509,15 +599,15 @@ class UpdaterLogic {
      * Finds the platform dependency. If found, add it to the list of things to install.\r
      * Returns the archive info dependency, if any.\r
      */\r
-    protected ArchiveInfo findExtraPlatformDependency(\r
-            ExtraPackage extra,\r
+    protected ArchiveInfo findMinApiLevelDependency(\r
+            IMinApiLevelDependency pkg,\r
             ArrayList<ArchiveInfo> outArchives,\r
             Collection<Archive> selectedArchives,\r
             ArrayList<Package> remotePkgs,\r
             RepoSource[] remoteSources,\r
-            Package[] localPkgs) {\r
+            ArchiveInfo[] localArchives) {\r
 \r
-        int api = extra.getMinApiLevel();\r
+        int api = pkg.getMinApiLevel();\r
 \r
         if (api == ExtraPackage.MIN_API_LEVEL_NOT_SPECIFIED) {\r
             return null;\r
@@ -526,12 +616,15 @@ class UpdaterLogic {
         // Find a platform that would satisfy the requirement.\r
 \r
         // First look in locally installed packages.\r
-        for (Package p : localPkgs) {\r
-            if (p instanceof PlatformPackage) {\r
-                if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
-                    // We found one already installed. We don't report this dependency\r
-                    // as the UI only cares about resolving "newly added dependencies".\r
-                    return null;\r
+        for (ArchiveInfo ai : localArchives) {\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof PlatformPackage) {\r
+                    if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
+                        // We found one already installed.\r
+                        return null;\r
+                    }\r
                 }\r
             }\r
         }\r
@@ -541,12 +634,15 @@ class UpdaterLogic {
         ArchiveInfo foundAi = null;\r
 \r
         for (ArchiveInfo ai : outArchives) {\r
-            Package p = ai.getNewArchive().getParentPackage();\r
-            if (p instanceof PlatformPackage) {\r
-                if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
-                    if (api > foundApi) {\r
-                        foundApi = api;\r
-                        foundAi = ai;\r
+            Archive a = ai.getNewArchive();\r
+            if (a != null) {\r
+                Package p = a.getParentPackage();\r
+                if (p instanceof PlatformPackage) {\r
+                    if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
+                        if (api > foundApi) {\r
+                            foundApi = api;\r
+                            foundAi = ai;\r
+                        }\r
                     }\r
                 }\r
             }\r
@@ -596,17 +692,19 @@ class UpdaterLogic {
 \r
         if (foundArchive != null) {\r
             // It's not already in the list of things to install, so add it now\r
-            return insertArchive(foundArchive, outArchives,\r
-                    selectedArchives, remotePkgs, remoteSources, localPkgs,\r
+            return insertArchive(foundArchive,\r
+                    outArchives,\r
+                    selectedArchives,\r
+                    remotePkgs,\r
+                    remoteSources,\r
+                    localArchives,\r
                     true /*automated*/);\r
         }\r
 \r
         // We end up here if nothing matches. We don't have a good platform to match.\r
-        // Seriously, that can't happens unless the repository contains a bogus extra\r
-        // entry that does not match any existing platform API level.\r
-        // It's conceivable that a 3rd part addon repo might have error, in which case\r
-        // we'll let this one go through anyway.\r
-        return null;\r
+        // We need to indicate this extra depends on a missing platform archive\r
+        // so that it can be impossible to install later on.\r
+        return new MissingPlatformArchiveInfo(new AndroidVersion(api, null /*codename*/));\r
     }\r
 \r
     /** Fetch all remote packages only if really needed. */\r
@@ -630,4 +728,118 @@ class UpdaterLogic {
             }\r
         }\r
     }\r
+\r
+\r
+    /**\r
+     * A {@link LocalArchiveInfo} is an {@link ArchiveInfo} that wraps an already installed\r
+     * "local" package/archive.\r
+     * <p/>\r
+     * In this case, the "new Archive" is still expected to be non null and the\r
+     * "replaced Archive" isnull. Installed archives are always accepted and never\r
+     * rejected.\r
+     * <p/>\r
+     * Dependencies are not set.\r
+     */\r
+    private static class LocalArchiveInfo extends ArchiveInfo {\r
+\r
+        public LocalArchiveInfo(Archive localArchive) {\r
+            super(localArchive, null /*replaced*/, null /*dependsOn*/);\r
+        }\r
+\r
+        /** Installed archives are always accepted. */\r
+        @Override\r
+        public boolean isAccepted() {\r
+            return true;\r
+        }\r
+\r
+        /** Installed archives are never rejected. */\r
+        @Override\r
+        public boolean isRejected() {\r
+            return false;\r
+        }\r
+    }\r
+\r
+    /**\r
+     * A {@link MissingPlatformArchiveInfo} is an {@link ArchiveInfo} that represents a\r
+     * package/archive that we <em>really</em> need as a dependency but that we don't have.\r
+     * <p/>\r
+     * This is currently used for addons and extras in case we can't find a matching base platform.\r
+     * <p/>\r
+     * This kind of archive has specific properties: the new archive to install is null,\r
+     * there are no dependencies and no archive is being replaced. The info can never be\r
+     * accepted and is always rejected.\r
+     */\r
+    private static class MissingPlatformArchiveInfo extends ArchiveInfo {\r
+\r
+        private final AndroidVersion mVersion;\r
+\r
+        /**\r
+         * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the\r
+         * given platform version is missing.\r
+         */\r
+        public MissingPlatformArchiveInfo(AndroidVersion version) {\r
+            super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);\r
+            mVersion = version;\r
+        }\r
+\r
+        /** Missing archives are never accepted. */\r
+        @Override\r
+        public boolean isAccepted() {\r
+            return false;\r
+        }\r
+\r
+        /** Missing archives are always rejected. */\r
+        @Override\r
+        public boolean isRejected() {\r
+            return true;\r
+        }\r
+\r
+        @Override\r
+        public String getShortDescription() {\r
+            return String.format("Missing SDK Platform Android%1$s, API %2$d",\r
+                    mVersion.isPreview() ? " Preview" : "",\r
+                    mVersion.getApiLevel());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * A {@link MissingToolArchiveInfo} is an {@link ArchiveInfo} that represents a\r
+     * package/archive that we <em>really</em> need as a dependency but that we don't have.\r
+     * <p/>\r
+     * This is currently used for extras in case we can't find a matching tool revision.\r
+     * <p/>\r
+     * This kind of archive has specific properties: the new archive to install is null,\r
+     * there are no dependencies and no archive is being replaced. The info can never be\r
+     * accepted and is always rejected.\r
+     */\r
+    private static class MissingToolArchiveInfo extends ArchiveInfo {\r
+\r
+        private final int mRevision;\r
+\r
+        /**\r
+         * Constructs a {@link MissingPlatformArchiveInfo} that will indicate the\r
+         * given platform version is missing.\r
+         */\r
+        public MissingToolArchiveInfo(int revision) {\r
+            super(null /*newArchive*/, null /*replaced*/, null /*dependsOn*/);\r
+            mRevision = revision;\r
+        }\r
+\r
+        /** Missing archives are never accepted. */\r
+        @Override\r
+        public boolean isAccepted() {\r
+            return false;\r
+        }\r
+\r
+        /** Missing archives are always rejected. */\r
+        @Override\r
+        public boolean isRejected() {\r
+            return true;\r
+        }\r
+\r
+        @Override\r
+        public String getShortDescription() {\r
+            return String.format("Missing Android SDK Tools, revision %1$d", mRevision);\r
+        }\r
+    }\r
 }\r