OSDN Git Service

am 626101ea: Merge "SDK Updater: support min-api-level in Extra packages." into eclair
[android-x86/sdk.git] / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / repository / UpdaterLogic.java
1 /*\r
2  * Copyright (C) 2009 The Android Open Source Project\r
3  *\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
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\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
15  */\r
16 \r
17 package com.android.sdkuilib.internal.repository;\r
18 \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.IPackageVersion;\r
25 import com.android.sdklib.internal.repository.MinToolsPackage;\r
26 import com.android.sdklib.internal.repository.Package;\r
27 import com.android.sdklib.internal.repository.PlatformPackage;\r
28 import com.android.sdklib.internal.repository.RepoSource;\r
29 import com.android.sdklib.internal.repository.RepoSources;\r
30 import com.android.sdklib.internal.repository.ToolPackage;\r
31 import com.android.sdklib.internal.repository.Package.UpdateInfo;\r
32 \r
33 import java.util.ArrayList;\r
34 import java.util.Collection;\r
35 import java.util.HashMap;\r
36 \r
37 /**\r
38  * The logic to compute which packages to install, based on the choices\r
39  * made by the user. This adds dependent packages as needed.\r
40  * <p/>\r
41  * When the user doesn't provide a selection, looks at local package to find\r
42  * those that can be updated and compute dependencies too.\r
43  */\r
44 class UpdaterLogic {\r
45 \r
46     /**\r
47      * Compute which packages to install by taking the user selection\r
48      * and adding dependent packages as needed.\r
49      *\r
50      * When the user doesn't provide a selection, looks at local packages to find\r
51      * those that can be updated and compute dependencies too.\r
52      */\r
53     public ArrayList<ArchiveInfo> computeUpdates(\r
54             Collection<Archive> selectedArchives,\r
55             RepoSources sources,\r
56             Package[] localPkgs) {\r
57 \r
58         ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();\r
59         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
60         RepoSource[] remoteSources = sources.getSources();\r
61 \r
62         if (selectedArchives == null) {\r
63             selectedArchives = findUpdates(localPkgs, remotePkgs, remoteSources);\r
64         }\r
65 \r
66         for (Archive a : selectedArchives) {\r
67             insertArchive(a,\r
68                     archives,\r
69                     selectedArchives,\r
70                     remotePkgs,\r
71                     remoteSources,\r
72                     localPkgs,\r
73                     false /*automated*/);\r
74         }\r
75 \r
76         return archives;\r
77     }\r
78 \r
79     /**\r
80      * Finds new platforms that the user does not have in his/her local SDK\r
81      * and adds them to the list of archives to install.\r
82      */\r
83     public void addNewPlatforms(ArrayList<ArchiveInfo> archives,\r
84             RepoSources sources,\r
85             Package[] localPkgs) {\r
86 \r
87         // Find the highest platform installed\r
88         float currentPlatformScore = 0;\r
89         float currentAddonScore = 0;\r
90         float currentDocScore = 0;\r
91         HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();\r
92         for (Package p : localPkgs) {\r
93             int rev = p.getRevision();\r
94             int api = 0;\r
95             boolean isPreview = false;\r
96             if (p instanceof  IPackageVersion) {\r
97                 AndroidVersion vers = ((IPackageVersion) p).getVersion();\r
98                 api = vers.getApiLevel();\r
99                 isPreview = vers.isPreview();\r
100             }\r
101 \r
102             // The score is 10*api + (1 if preview) + rev/100\r
103             // This allows previews to rank above a non-preview and\r
104             // allows revisions to rank appropriately.\r
105             float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;\r
106 \r
107             if (p instanceof PlatformPackage) {\r
108                 currentPlatformScore = Math.max(currentPlatformScore, score);\r
109             } else if (p instanceof AddonPackage) {\r
110                 currentAddonScore = Math.max(currentAddonScore, score);\r
111             } else if (p instanceof ExtraPackage) {\r
112                 currentExtraScore.put(((ExtraPackage) p).getPath(), score);\r
113             } else if (p instanceof DocPackage) {\r
114                 currentDocScore = Math.max(currentDocScore, score);\r
115             }\r
116         }\r
117 \r
118         RepoSource[] remoteSources = sources.getSources();\r
119         ArrayList<Package> remotePkgs = new ArrayList<Package>();\r
120         fetchRemotePackages(remotePkgs, remoteSources);\r
121 \r
122         Package suggestedDoc = null;\r
123 \r
124         for (Package p : remotePkgs) {\r
125             int rev = p.getRevision();\r
126             int api = 0;\r
127             boolean isPreview = false;\r
128             if (p instanceof  IPackageVersion) {\r
129                 AndroidVersion vers = ((IPackageVersion) p).getVersion();\r
130                 api = vers.getApiLevel();\r
131                 isPreview = vers.isPreview();\r
132             }\r
133 \r
134             float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;\r
135 \r
136             boolean shouldAdd = false;\r
137             if (p instanceof PlatformPackage) {\r
138                 shouldAdd = score > currentPlatformScore;\r
139             } else if (p instanceof AddonPackage) {\r
140                 shouldAdd = score > currentAddonScore;\r
141             } else if (p instanceof ExtraPackage) {\r
142                 String key = ((ExtraPackage) p).getPath();\r
143                 shouldAdd = !currentExtraScore.containsKey(key) ||\r
144                     score > currentExtraScore.get(key).floatValue();\r
145             } else if (p instanceof DocPackage) {\r
146                 // We don't want all the doc, only the most recent one\r
147                 if (score > currentDocScore) {\r
148                     suggestedDoc = p;\r
149                     currentDocScore = score;\r
150                 }\r
151             }\r
152 \r
153             if (shouldAdd) {\r
154                 // We should suggest this package for installation.\r
155                 for (Archive a : p.getArchives()) {\r
156                     if (a.isCompatible()) {\r
157                         insertArchive(a,\r
158                                 archives,\r
159                                 null /*selectedArchives*/,\r
160                                 remotePkgs,\r
161                                 remoteSources,\r
162                                 localPkgs,\r
163                                 true /*automated*/);\r
164                     }\r
165                 }\r
166             }\r
167         }\r
168 \r
169         if (suggestedDoc != null) {\r
170             // We should suggest this package for installation.\r
171             for (Archive a : suggestedDoc.getArchives()) {\r
172                 if (a.isCompatible()) {\r
173                     insertArchive(a,\r
174                             archives,\r
175                             null /*selectedArchives*/,\r
176                             remotePkgs,\r
177                             remoteSources,\r
178                             localPkgs,\r
179                             true /*automated*/);\r
180                 }\r
181             }\r
182         }\r
183 \r
184     }\r
185 \r
186     /**\r
187      * Find suitable updates to all current local packages.\r
188      */\r
189     private Collection<Archive> findUpdates(Package[] localPkgs,\r
190             ArrayList<Package> remotePkgs,\r
191             RepoSource[] remoteSources) {\r
192         ArrayList<Archive> updates = new ArrayList<Archive>();\r
193 \r
194         fetchRemotePackages(remotePkgs, remoteSources);\r
195 \r
196         for (Package localPkg : localPkgs) {\r
197             for (Package remotePkg : remotePkgs) {\r
198                 if (localPkg.canBeUpdatedBy(remotePkg) == UpdateInfo.UPDATE) {\r
199                     // Found a suitable update. Only accept the remote package\r
200                     // if it provides at least one compatible archive.\r
201 \r
202                     for (Archive a : remotePkg.getArchives()) {\r
203                         if (a.isCompatible()) {\r
204                             updates.add(a);\r
205                             break;\r
206                         }\r
207                     }\r
208                 }\r
209             }\r
210         }\r
211 \r
212         return updates;\r
213     }\r
214 \r
215     private ArchiveInfo insertArchive(Archive archive,\r
216             ArrayList<ArchiveInfo> outArchives,\r
217             Collection<Archive> selectedArchives,\r
218             ArrayList<Package> remotePkgs,\r
219             RepoSource[] remoteSources,\r
220             Package[] localPkgs,\r
221             boolean automated) {\r
222         Package p = archive.getParentPackage();\r
223 \r
224         // Is this an update?\r
225         Archive updatedArchive = null;\r
226         for (Package lp : localPkgs) {\r
227             assert lp.getArchives().length == 1;\r
228             if (lp.getArchives().length > 0 && lp.canBeUpdatedBy(p) == UpdateInfo.UPDATE) {\r
229                 updatedArchive = lp.getArchives()[0];\r
230             }\r
231         }\r
232 \r
233         // Find dependencies\r
234         ArchiveInfo[] deps = findDependency(p,\r
235                 outArchives,\r
236                 selectedArchives,\r
237                 remotePkgs,\r
238                 remoteSources,\r
239                 localPkgs);\r
240 \r
241         // Make sure it's not a dup\r
242         ArchiveInfo ai = null;\r
243 \r
244         for (ArchiveInfo ai2 : outArchives) {\r
245             if (ai2.getNewArchive().getParentPackage().sameItemAs(archive.getParentPackage())) {\r
246                 ai = ai2;\r
247                 break;\r
248             }\r
249         }\r
250 \r
251         if (ai == null) {\r
252             ai = new ArchiveInfo(\r
253                 archive,        //newArchive\r
254                 updatedArchive, //replaced\r
255                 deps            //dependsOn\r
256                 );\r
257             outArchives.add(ai);\r
258         }\r
259 \r
260         if (deps != null) {\r
261             for (ArchiveInfo d : deps) {\r
262                 d.addDependencyFor(ai);\r
263             }\r
264         }\r
265 \r
266         return ai;\r
267     }\r
268 \r
269     private ArchiveInfo[] findDependency(Package pkg,\r
270             ArrayList<ArchiveInfo> outArchives,\r
271             Collection<Archive> selectedArchives,\r
272             ArrayList<Package> remotePkgs,\r
273             RepoSource[] remoteSources,\r
274             Package[] localPkgs) {\r
275 \r
276         // Current dependencies can be:\r
277         // - addon: *always* depends on platform of same API level\r
278         // - platform: *might* depends on tools of rev >= min-tools-rev\r
279         // - extra: *might* depends on platform with api >= min-api-level\r
280 \r
281         if (pkg instanceof AddonPackage) {\r
282             AddonPackage addon = (AddonPackage) pkg;\r
283 \r
284             ArchiveInfo ai = findPlatformDependency(\r
285                     addon,\r
286                     outArchives,\r
287                     selectedArchives,\r
288                     remotePkgs,\r
289                     remoteSources,\r
290                     localPkgs);\r
291 \r
292             if (ai != null) {\r
293                 return new ArchiveInfo[] { ai };\r
294             }\r
295 \r
296         } else if (pkg instanceof MinToolsPackage) {\r
297             MinToolsPackage platformOrExtra = (MinToolsPackage) pkg;\r
298 \r
299             int n = 0;\r
300             ArchiveInfo ai1 = findToolsDependency(\r
301                     platformOrExtra,\r
302                     outArchives,\r
303                     selectedArchives,\r
304                     remotePkgs,\r
305                     remoteSources,\r
306                     localPkgs);\r
307 \r
308             n += ai1 == null ? 0 : 1;\r
309 \r
310             ArchiveInfo ai2 = null;\r
311             if (pkg instanceof ExtraPackage) {\r
312                 ai2 = findExtraPlatformDependency(\r
313                         (ExtraPackage) pkg,\r
314                         outArchives,\r
315                         selectedArchives,\r
316                         remotePkgs,\r
317                         remoteSources,\r
318                         localPkgs);\r
319             }\r
320 \r
321             n += ai2 == null ? 0 : 1;\r
322 \r
323             if (n > 0) {\r
324                 ArchiveInfo[] ais = new ArchiveInfo[n];\r
325                 ais[0] = ai1 != null ? ai1 : ai2;\r
326                 if (n > 1) ais[1] = ai2;\r
327                 return ais;\r
328             }\r
329         }\r
330 \r
331         return null;\r
332     }\r
333 \r
334     /**\r
335      * Resolves dependencies on tools.\r
336      *\r
337      * A platform or an extra package can both have a min-tools-rev, in which case it\r
338      * depends on having a tools package of the requested revision.\r
339      * Finds the tools dependency. If found, add it to the list of things to install.\r
340      * Returns the archive info dependency, if any.\r
341      */\r
342     protected ArchiveInfo findToolsDependency(MinToolsPackage platformOrExtra,\r
343             ArrayList<ArchiveInfo> outArchives,\r
344             Collection<Archive> selectedArchives,\r
345             ArrayList<Package> remotePkgs,\r
346             RepoSource[] remoteSources,\r
347             Package[] localPkgs) {\r
348         // This is the requirement to match.\r
349         int rev = platformOrExtra.getMinToolsRevision();\r
350 \r
351         if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {\r
352             // Well actually there's no requirement.\r
353             return null;\r
354         }\r
355 \r
356         // First look in locally installed packages.\r
357         for (Package p : localPkgs) {\r
358             if (p instanceof ToolPackage) {\r
359                 if (((ToolPackage) p).getRevision() >= rev) {\r
360                     // We found one already installed. We don't report this dependency\r
361                     // as the UI only cares about resolving "newly added dependencies".\r
362                     return null;\r
363                 }\r
364             }\r
365         }\r
366 \r
367         // Look in archives already scheduled for install\r
368         for (ArchiveInfo ai : outArchives) {\r
369             Package p = ai.getNewArchive().getParentPackage();\r
370             if (p instanceof ToolPackage) {\r
371                 if (((ToolPackage) p).getRevision() >= rev) {\r
372                     // The dependency is already scheduled for install, nothing else to do.\r
373                     return ai;\r
374                 }\r
375             }\r
376         }\r
377 \r
378         // Otherwise look in the selected archives.\r
379         if (selectedArchives != null) {\r
380             for (Archive a : selectedArchives) {\r
381                 Package p = a.getParentPackage();\r
382                 if (p instanceof ToolPackage) {\r
383                     if (((ToolPackage) p).getRevision() >= rev) {\r
384                         // It's not already in the list of things to install, so add it now\r
385                         return insertArchive(a, outArchives,\r
386                                 selectedArchives, remotePkgs, remoteSources, localPkgs,\r
387                                 true /*automated*/);\r
388                     }\r
389                 }\r
390             }\r
391         }\r
392 \r
393         // Finally nothing matched, so let's look at all available remote packages\r
394         fetchRemotePackages(remotePkgs, remoteSources);\r
395         for (Package p : remotePkgs) {\r
396             if (p instanceof ToolPackage) {\r
397                 if (((ToolPackage) p).getRevision() >= rev) {\r
398                     // It's not already in the list of things to install, so add the\r
399                     // first compatible archive we can find.\r
400                     for (Archive a : p.getArchives()) {\r
401                         if (a.isCompatible()) {\r
402                             return insertArchive(a, outArchives,\r
403                                     selectedArchives, remotePkgs, remoteSources, localPkgs,\r
404                                     true /*automated*/);\r
405                         }\r
406                     }\r
407                 }\r
408             }\r
409         }\r
410 \r
411         // We end up here if nothing matches. We don't have a good tools to match.\r
412         // Seriously, that can't happens unless we totally screwed our repo manifest.\r
413         // We'll let this one go through anyway.\r
414         return null;\r
415     }\r
416 \r
417     /**\r
418      * Resolves dependencies on platform.\r
419      *\r
420      * An addon depends on having a platform with the same API level.\r
421      *\r
422      * Finds the platform dependency. If found, add it to the list of things to install.\r
423      * Returns the archive info dependency, if any.\r
424      */\r
425     protected ArchiveInfo findPlatformDependency(\r
426             AddonPackage addon,\r
427             ArrayList<ArchiveInfo> outArchives,\r
428             Collection<Archive> selectedArchives,\r
429             ArrayList<Package> remotePkgs,\r
430             RepoSource[] remoteSources,\r
431             Package[] localPkgs) {\r
432         // This is the requirement to match.\r
433         AndroidVersion v = addon.getVersion();\r
434 \r
435         // Find a platform that would satisfy the requirement.\r
436 \r
437         // First look in locally installed packages.\r
438         for (Package p : localPkgs) {\r
439             if (p instanceof PlatformPackage) {\r
440                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
441                     // We found one already installed. We don't report this dependency\r
442                     // as the UI only cares about resolving "newly added dependencies".\r
443                     return null;\r
444                 }\r
445             }\r
446         }\r
447 \r
448         // Look in archives already scheduled for install\r
449         for (ArchiveInfo ai : outArchives) {\r
450             Package p = ai.getNewArchive().getParentPackage();\r
451             if (p instanceof PlatformPackage) {\r
452                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
453                     // The dependency is already scheduled for install, nothing else to do.\r
454                     return ai;\r
455                 }\r
456             }\r
457         }\r
458 \r
459         // Otherwise look in the selected archives.\r
460         if (selectedArchives != null) {\r
461             for (Archive a : selectedArchives) {\r
462                 Package p = a.getParentPackage();\r
463                 if (p instanceof PlatformPackage) {\r
464                     if (v.equals(((PlatformPackage) p).getVersion())) {\r
465                         // It's not already in the list of things to install, so add it now\r
466                         return insertArchive(a, outArchives,\r
467                                 selectedArchives, remotePkgs, remoteSources, localPkgs,\r
468                                 true /*automated*/);\r
469                     }\r
470                 }\r
471             }\r
472         }\r
473 \r
474         // Finally nothing matched, so let's look at all available remote packages\r
475         fetchRemotePackages(remotePkgs, remoteSources);\r
476         for (Package p : remotePkgs) {\r
477             if (p instanceof PlatformPackage) {\r
478                 if (v.equals(((PlatformPackage) p).getVersion())) {\r
479                     // It's not already in the list of things to install, so add the\r
480                     // first compatible archive we can find.\r
481                     for (Archive a : p.getArchives()) {\r
482                         if (a.isCompatible()) {\r
483                             return insertArchive(a, outArchives,\r
484                                     selectedArchives, remotePkgs, remoteSources, localPkgs,\r
485                                     true /*automated*/);\r
486                         }\r
487                     }\r
488                 }\r
489             }\r
490         }\r
491 \r
492         // We end up here if nothing matches. We don't have a good platform to match.\r
493         // Seriously, that can't happens unless the repository contains a bogus addon\r
494         // entry that does not match any existing platform API level.\r
495         // It's conceivable that a 3rd part addon repo might have error, in which case\r
496         // we'll let this one go through anyway.\r
497         return null;\r
498     }\r
499 \r
500     /**\r
501      * Resolves platform dependencies for extras.\r
502      * An extra depends on having a platform with a minimun API level.\r
503      *\r
504      * We try to return the highest API level available above the specified minimum.\r
505      * Note that installed packages have priority so if one installed platform satisfies\r
506      * the dependency, we'll use it even if there's a higher API platform available but\r
507      * not installed yet.\r
508      *\r
509      * Finds the platform dependency. If found, add it to the list of things to install.\r
510      * Returns the archive info dependency, if any.\r
511      */\r
512     protected ArchiveInfo findExtraPlatformDependency(\r
513             ExtraPackage extra,\r
514             ArrayList<ArchiveInfo> outArchives,\r
515             Collection<Archive> selectedArchives,\r
516             ArrayList<Package> remotePkgs,\r
517             RepoSource[] remoteSources,\r
518             Package[] localPkgs) {\r
519 \r
520         int api = extra.getMinApiLevel();\r
521 \r
522         if (api == ExtraPackage.MIN_API_LEVEL_NOT_SPECIFIED) {\r
523             return null;\r
524         }\r
525 \r
526         // Find a platform that would satisfy the requirement.\r
527 \r
528         // First look in locally installed packages.\r
529         for (Package p : localPkgs) {\r
530             if (p instanceof PlatformPackage) {\r
531                 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
532                     // We found one already installed. We don't report this dependency\r
533                     // as the UI only cares about resolving "newly added dependencies".\r
534                     return null;\r
535                 }\r
536             }\r
537         }\r
538 \r
539         // Look in archives already scheduled for install\r
540         int foundApi = 0;\r
541         ArchiveInfo foundAi = null;\r
542 \r
543         for (ArchiveInfo ai : outArchives) {\r
544             Package p = ai.getNewArchive().getParentPackage();\r
545             if (p instanceof PlatformPackage) {\r
546                 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
547                     if (api > foundApi) {\r
548                         foundApi = api;\r
549                         foundAi = ai;\r
550                     }\r
551                 }\r
552             }\r
553         }\r
554 \r
555         if (foundAi != null) {\r
556             // The dependency is already scheduled for install, nothing else to do.\r
557             return foundAi;\r
558         }\r
559 \r
560         // Otherwise look in the selected archives *or* available remote packages\r
561         // and takes the best out of the two sets.\r
562         foundApi = 0;\r
563         Archive foundArchive = null;\r
564         if (selectedArchives != null) {\r
565             for (Archive a : selectedArchives) {\r
566                 Package p = a.getParentPackage();\r
567                 if (p instanceof PlatformPackage) {\r
568                     if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
569                         if (api > foundApi) {\r
570                             foundApi = api;\r
571                             foundArchive = a;\r
572                         }\r
573                     }\r
574                 }\r
575             }\r
576         }\r
577 \r
578         // Finally nothing matched, so let's look at all available remote packages\r
579         fetchRemotePackages(remotePkgs, remoteSources);\r
580         for (Package p : remotePkgs) {\r
581             if (p instanceof PlatformPackage) {\r
582                 if (((PlatformPackage) p).getVersion().isGreaterOrEqualThan(api)) {\r
583                     if (api > foundApi) {\r
584                         // It's not already in the list of things to install, so add the\r
585                         // first compatible archive we can find.\r
586                         for (Archive a : p.getArchives()) {\r
587                             if (a.isCompatible()) {\r
588                                 foundApi = api;\r
589                                 foundArchive = a;\r
590                             }\r
591                         }\r
592                     }\r
593                 }\r
594             }\r
595         }\r
596 \r
597         if (foundArchive != null) {\r
598             // It's not already in the list of things to install, so add it now\r
599             return insertArchive(foundArchive, outArchives,\r
600                     selectedArchives, remotePkgs, remoteSources, localPkgs,\r
601                     true /*automated*/);\r
602         }\r
603 \r
604         // We end up here if nothing matches. We don't have a good platform to match.\r
605         // Seriously, that can't happens unless the repository contains a bogus extra\r
606         // entry that does not match any existing platform API level.\r
607         // It's conceivable that a 3rd part addon repo might have error, in which case\r
608         // we'll let this one go through anyway.\r
609         return null;\r
610     }\r
611 \r
612     /** Fetch all remote packages only if really needed. */\r
613     protected void fetchRemotePackages(ArrayList<Package> remotePkgs, RepoSource[] remoteSources) {\r
614         if (remotePkgs.size() > 0) {\r
615             return;\r
616         }\r
617 \r
618         for (RepoSource remoteSrc : remoteSources) {\r
619             Package[] pkgs = remoteSrc.getPackages();\r
620             if (pkgs != null) {\r
621                 nextPackage: for (Package pkg : pkgs) {\r
622                     for (Archive a : pkg.getArchives()) {\r
623                         // Only add a package if it contains at least one compatible archive\r
624                         if (a.isCompatible()) {\r
625                             remotePkgs.add(pkg);\r
626                             continue nextPackage;\r
627                         }\r
628                     }\r
629                 }\r
630             }\r
631         }\r
632     }\r
633 }\r