• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.sdkuilib.internal.repository;
18 
19 import com.android.prefs.AndroidLocation.AndroidLocationException;
20 import com.android.sdklib.ISdkLog;
21 import com.android.sdklib.SdkConstants;
22 import com.android.sdklib.SdkManager;
23 import com.android.sdklib.internal.avd.AvdManager;
24 import com.android.sdklib.internal.repository.AddonPackage;
25 import com.android.sdklib.internal.repository.Archive;
26 import com.android.sdklib.internal.repository.DocPackage;
27 import com.android.sdklib.internal.repository.ExtraPackage;
28 import com.android.sdklib.internal.repository.ITask;
29 import com.android.sdklib.internal.repository.ITaskFactory;
30 import com.android.sdklib.internal.repository.ITaskMonitor;
31 import com.android.sdklib.internal.repository.LocalSdkParser;
32 import com.android.sdklib.internal.repository.Package;
33 import com.android.sdklib.internal.repository.PlatformPackage;
34 import com.android.sdklib.internal.repository.RepoSource;
35 import com.android.sdklib.internal.repository.RepoSources;
36 import com.android.sdklib.internal.repository.SamplePackage;
37 import com.android.sdklib.internal.repository.ToolPackage;
38 import com.android.sdklib.repository.SdkRepository;
39 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
40 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;
41 
42 import org.eclipse.jface.dialogs.MessageDialog;
43 import org.eclipse.swt.widgets.Display;
44 import org.eclipse.swt.widgets.Shell;
45 
46 import java.io.ByteArrayOutputStream;
47 import java.io.PrintStream;
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.HashMap;
51 import java.util.HashSet;
52 import java.util.Iterator;
53 
54 /**
55  * Data shared between {@link UpdaterWindowImpl} and its pages.
56  */
57 class UpdaterData {
58     private String mOsSdkRoot;
59 
60     private final ISdkLog mSdkLog;
61     private ITaskFactory mTaskFactory;
62     private boolean mUserCanChangeSdkRoot;
63 
64     private SdkManager mSdkManager;
65     private AvdManager mAvdManager;
66 
67     private final LocalSdkParser mLocalSdkParser = new LocalSdkParser();
68     private final RepoSources mSources = new RepoSources();
69 
70     private final LocalSdkAdapter mLocalSdkAdapter = new LocalSdkAdapter(this);
71     private final RepoSourcesAdapter mSourcesAdapter = new RepoSourcesAdapter(this);
72 
73     private ImageFactory mImageFactory;
74 
75     private final SettingsController mSettingsController;
76 
77     private final ArrayList<ISdkListener> mListeners = new ArrayList<ISdkListener>();
78 
79     private Shell mWindowShell;
80 
81     private AndroidLocationException mAvdManagerInitError;
82 
83     /**
84      * Creates a new updater data.
85      *
86      * @param sdkLog Logger. Cannot be null.
87      * @param osSdkRoot The OS path to the SDK root.
88      */
UpdaterData(String osSdkRoot, ISdkLog sdkLog)89     public UpdaterData(String osSdkRoot, ISdkLog sdkLog) {
90         mOsSdkRoot = osSdkRoot;
91         mSdkLog = sdkLog;
92 
93         mSettingsController = new SettingsController(this);
94 
95         initSdk();
96     }
97 
98     // ----- getters, setters ----
99 
getOsSdkRoot()100     public String getOsSdkRoot() {
101         return mOsSdkRoot;
102     }
103 
setTaskFactory(ITaskFactory taskFactory)104     public void setTaskFactory(ITaskFactory taskFactory) {
105         mTaskFactory = taskFactory;
106     }
107 
getTaskFactory()108     public ITaskFactory getTaskFactory() {
109         return mTaskFactory;
110     }
111 
setUserCanChangeSdkRoot(boolean userCanChangeSdkRoot)112     public void setUserCanChangeSdkRoot(boolean userCanChangeSdkRoot) {
113         mUserCanChangeSdkRoot = userCanChangeSdkRoot;
114     }
115 
canUserChangeSdkRoot()116     public boolean canUserChangeSdkRoot() {
117         return mUserCanChangeSdkRoot;
118     }
119 
getSources()120     public RepoSources getSources() {
121         return mSources;
122     }
123 
getSourcesAdapter()124     public RepoSourcesAdapter getSourcesAdapter() {
125         return mSourcesAdapter;
126     }
127 
getLocalSdkParser()128     public LocalSdkParser getLocalSdkParser() {
129         return mLocalSdkParser;
130     }
131 
getLocalSdkAdapter()132     public LocalSdkAdapter getLocalSdkAdapter() {
133         return mLocalSdkAdapter;
134     }
135 
getSdkLog()136     public ISdkLog getSdkLog() {
137         return mSdkLog;
138     }
139 
setImageFactory(ImageFactory imageFactory)140     public void setImageFactory(ImageFactory imageFactory) {
141         mImageFactory = imageFactory;
142     }
143 
getImageFactory()144     public ImageFactory getImageFactory() {
145         return mImageFactory;
146     }
147 
getSdkManager()148     public SdkManager getSdkManager() {
149         return mSdkManager;
150     }
151 
getAvdManager()152     public AvdManager getAvdManager() {
153         return mAvdManager;
154     }
155 
getSettingsController()156     public SettingsController getSettingsController() {
157         return mSettingsController;
158     }
159 
160     /** Adds a listener ({@link ISdkListener}) that is notified when the SDK is reloaded. */
addListeners(ISdkListener listener)161     public void addListeners(ISdkListener listener) {
162         if (mListeners.contains(listener) == false) {
163             mListeners.add(listener);
164         }
165     }
166 
167     /** Removes a listener ({@link ISdkListener}) that is notified when the SDK is reloaded. */
removeListener(ISdkListener listener)168     public void removeListener(ISdkListener listener) {
169         mListeners.remove(listener);
170     }
171 
setWindowShell(Shell windowShell)172     public void setWindowShell(Shell windowShell) {
173         mWindowShell = windowShell;
174     }
175 
getWindowShell()176     public Shell getWindowShell() {
177         return mWindowShell;
178     }
179 
180     /**
181      * Check if any error occurred during initialization.
182      * If it did, display an error message.
183      *
184      * @return True if an error occurred, false if we should continue.
185      */
checkIfInitFailed()186     public boolean checkIfInitFailed() {
187         if (mAvdManagerInitError != null) {
188             String example;
189             if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_WINDOWS) {
190                 example = "%USERPROFILE%";     //$NON-NLS-1$
191             } else {
192                 example = "~";                 //$NON-NLS-1$
193             }
194 
195             String error = String.format(
196                 "The AVD manager normally uses the user's profile directory to store " +
197                 "AVD files. However it failed to find the default profile directory. " +
198                 "\n" +
199                 "To fix this, please set the environment variable ANDROID_SDK_HOME to " +
200                 "a valid path such as \"%s\".",
201                 example);
202 
203             // We may not have any UI. Only display a dialog if there's a window shell available.
204             if (mWindowShell != null) {
205                 MessageDialog.openError(mWindowShell,
206                     "Android Virtual Devices Manager",
207                     error);
208             } else {
209                 mSdkLog.error(null /* Throwable */, "%s", error);  //$NON-NLS-1$
210             }
211 
212             return true;
213         }
214         return false;
215     }
216 
217     // -----
218 
219     /**
220      * Initializes the {@link SdkManager} and the {@link AvdManager}.
221      */
initSdk()222     private void initSdk() {
223         mSdkManager = SdkManager.createManager(mOsSdkRoot, mSdkLog);
224         try {
225             mAvdManager = null; // remove the old one if needed.
226             mAvdManager = new AvdManager(mSdkManager, mSdkLog);
227         } catch (AndroidLocationException e) {
228             mSdkLog.error(e, "Unable to read AVDs: " + e.getMessage());  //$NON-NLS-1$
229 
230             // Note: we used to continue here, but the thing is that
231             // mAvdManager==null so nothing is really going to work as
232             // expected. Let's just display an error later in checkIfInitFailed()
233             // and abort right there. This step is just too early in the SWT
234             // setup process to display a message box yet.
235 
236             mAvdManagerInitError = e;
237         }
238 
239         // notify listeners.
240         notifyListeners(false /*init*/);
241     }
242 
243     /**
244      * Reloads the SDK content (targets).
245      * <p/>
246      * This also reloads the AVDs in case their status changed.
247      * <p/>
248      * This does not notify the listeners ({@link ISdkListener}).
249      */
reloadSdk()250     public void reloadSdk() {
251         // reload SDK
252         mSdkManager.reloadSdk(mSdkLog);
253 
254         // reload AVDs
255         if (mAvdManager != null) {
256             try {
257                 mAvdManager.reloadAvds(mSdkLog);
258             } catch (AndroidLocationException e) {
259                 // FIXME
260             }
261         }
262 
263         // notify adapters?
264         mLocalSdkParser.clearPackages();
265         // TODO
266 
267         // notify listeners
268         notifyListeners(false /*init*/);
269     }
270 
271     /**
272      * Reloads the AVDs.
273      * <p/>
274      * This does not notify the listeners.
275      */
reloadAvds()276     public void reloadAvds() {
277         // reload AVDs
278         if (mAvdManager != null) {
279             try {
280                 mAvdManager.reloadAvds(mSdkLog);
281             } catch (AndroidLocationException e) {
282                 mSdkLog.error(e, null);
283             }
284         }
285     }
286 
287     /**
288      * Sets up the default sources: <br/>
289      * - the default google SDK repository, <br/>
290      * - the extra repo URLs from the environment, <br/>
291      * - the user sources from prefs <br/>
292      * - and finally the extra user repo URLs from the environment.
293      */
setupDefaultSources()294     public void setupDefaultSources() {
295         RepoSources sources = getSources();
296         sources.add(new RepoSource(SdkRepository.URL_GOOGLE_SDK_REPO_SITE, false /*userSource*/));
297 
298         // SDK_UPDATER_URLS is a semicolon-separated list of URLs that can be used to
299         // seed the SDK Updater list for full repositories.
300         String str = System.getenv("SDK_UPDATER_URLS");
301         if (str != null) {
302             String[] urls = str.split(";");
303             for (String url : urls) {
304                 if (url != null && url.length() > 0) {
305                     RepoSource s = new RepoSource(url, false /*userSource*/);
306                     if (!sources.hasSource(s)) {
307                         sources.add(s);
308                     }
309                 }
310             }
311         }
312 
313         // Load user sources
314         sources.loadUserSources(getSdkLog());
315 
316         // SDK_UPDATER_USER_URLS is a semicolon-separated list of URLs that can be used to
317         // seed the SDK Updater list for user-only repositories. User sources can only provide
318         // add-ons and extra packages.
319         str = System.getenv("SDK_UPDATER_USER_URLS");
320         if (str != null) {
321             String[] urls = str.split(";");
322             for (String url : urls) {
323                 if (url != null && url.length() > 0) {
324                     RepoSource s = new RepoSource(url, true /*userSource*/);
325                     if (!sources.hasSource(s)) {
326                         sources.add(s);
327                     }
328                 }
329             }
330         }
331     }
332 
333     /**
334      * Returns the list of installed packages, parsing them if this has not yet been done.
335      */
getInstalledPackage()336     public Package[] getInstalledPackage() {
337         LocalSdkParser parser = getLocalSdkParser();
338 
339         Package[] packages = parser.getPackages();
340 
341         if (packages == null) {
342             // load on demand the first time
343             packages = parser.parseSdk(getOsSdkRoot(), getSdkManager(), getSdkLog());
344         }
345 
346         return packages;
347     }
348 
349     /**
350      * Notify the listeners ({@link ISdkListener}) that the SDK was reloaded.
351      * <p/>
352      * This can be called from any thread.
353      *
354      * @param init whether the SDK loaded for the first time.
355      */
notifyListeners(final boolean init)356     public void notifyListeners(final boolean init) {
357         if (mWindowShell != null && mListeners.size() > 0) {
358             mWindowShell.getDisplay().syncExec(new Runnable() {
359                 public void run() {
360                     for (ISdkListener listener : mListeners) {
361                         try {
362                             listener.onSdkChange(init);
363                         } catch (Throwable t) {
364                             mSdkLog.error(t, null);
365                         }
366                     }
367                 }
368             });
369         }
370     }
371 
372     /**
373      * Install the list of given {@link Archive}s. This is invoked by the user selecting some
374      * packages in the remote page and then clicking "install selected".
375      *
376      * @param result The archives to install. Incompatible ones will be skipped.
377      */
installArchives(final ArrayList<ArchiveInfo> result)378     public void installArchives(final ArrayList<ArchiveInfo> result) {
379         if (mTaskFactory == null) {
380             throw new IllegalArgumentException("Task Factory is null");
381         }
382 
383         final boolean forceHttp = getSettingsController().getForceHttp();
384 
385         mTaskFactory.start("Installing Archives", new ITask() {
386             public void run(ITaskMonitor monitor) {
387 
388                 final int progressPerArchive = 2 * Archive.NUM_MONITOR_INC;
389                 monitor.setProgressMax(result.size() * progressPerArchive);
390                 monitor.setDescription("Preparing to install archives");
391 
392                 boolean installedAddon = false;
393                 boolean installedTools = false;
394 
395                 // Mark all current local archives as already installed.
396                 HashSet<Archive> installedArchives = new HashSet<Archive>();
397                 for (Package p : getInstalledPackage()) {
398                     for (Archive a : p.getArchives()) {
399                         installedArchives.add(a);
400                     }
401                 }
402 
403                 int numInstalled = 0;
404                 nextArchive: for (ArchiveInfo ai : result) {
405                     Archive archive = ai.getNewArchive();
406                     if (archive == null) {
407                         // This is not supposed to happen.
408                         continue nextArchive;
409                     }
410 
411                     int nextProgress = monitor.getProgress() + progressPerArchive;
412                     try {
413                         if (monitor.isCancelRequested()) {
414                             break;
415                         }
416 
417                         ArchiveInfo[] adeps = ai.getDependsOn();
418                         if (adeps != null) {
419                             for (ArchiveInfo adep : adeps) {
420                                 Archive na = adep.getNewArchive();
421                                 if (na == null) {
422                                     // This archive depends on a missing archive.
423                                     // We shouldn't get here.
424                                     // Skip it.
425                                     monitor.setResult("Skipping '%1$s'; it depends on a missing package.",
426                                             archive.getParentPackage().getShortDescription());
427                                     continue nextArchive;
428                                 } else if (!installedArchives.contains(na)) {
429                                     // This archive depends on another one that was not installed.
430                                     // We shouldn't get here.
431                                     // Skip it.
432                                     monitor.setResult("Skipping '%1$s'; it depends on '%2$s' which was not installed.",
433                                             archive.getParentPackage().getShortDescription(),
434                                             adep.getShortDescription());
435                                     continue nextArchive;
436                                 }
437                             }
438                         }
439 
440                         if (archive.install(mOsSdkRoot, forceHttp, mSdkManager, monitor)) {
441                             // We installed this archive.
442                             installedArchives.add(archive);
443                             numInstalled++;
444 
445                             // If this package was replacing an existing one, the old one
446                             // is no longer installed.
447                             installedArchives.remove(ai.getReplaced());
448 
449                             // Check if we successfully installed a tool or add-on package.
450                             if (archive.getParentPackage() instanceof AddonPackage) {
451                                 installedAddon = true;
452                             } else if (archive.getParentPackage() instanceof ToolPackage) {
453                                 installedTools = true;
454                             }
455                         }
456 
457                     } catch (Throwable t) {
458                         // Display anything unexpected in the monitor.
459                         String msg = t.getMessage();
460                         if (msg != null) {
461                             msg = String.format("Unexpected Error installing '%1$s': %2$s: %3$s",
462                                     archive.getParentPackage().getShortDescription(),
463                                     t.getClass().getCanonicalName(), msg);
464                         } else {
465                             // no error info? get the stack call to display it
466                             // At least that'll give us a better bug report.
467                             ByteArrayOutputStream baos = new ByteArrayOutputStream();
468                             t.printStackTrace(new PrintStream(baos));
469 
470                             msg = String.format("Unexpected Error installing '%1$s'\n%2$s",
471                                     archive.getParentPackage().getShortDescription(),
472                                     baos.toString());
473                         }
474 
475                         monitor.setResult(msg);
476                         mSdkLog.error(t, msg);
477                     } finally {
478 
479                         // Always move the progress bar to the desired position.
480                         // This allows internal methods to not have to care in case
481                         // they abort early
482                         monitor.incProgress(nextProgress - monitor.getProgress());
483                     }
484                 }
485 
486                 if (installedAddon) {
487                     // Update the USB vendor ids for adb
488                     try {
489                         mSdkManager.updateAdb();
490                         monitor.setResult("Updated ADB to support the USB devices declared in the SDK add-ons.");
491                     } catch (Exception e) {
492                         mSdkLog.error(e, "Update ADB failed");
493                         monitor.setResult("failed to update adb to support the USB devices declared in the SDK add-ons.");
494                     }
495                 }
496 
497                 if (installedAddon || installedTools) {
498                     // We need to restart ADB. Actually since we don't know if it's even
499                     // running, maybe we should just kill it and not start it.
500                     // Note: it turns out even under Windows we don't need to kill adb
501                     // before updating the tools folder, as adb.exe is (surprisingly) not
502                     // locked.
503 
504                     askForAdbRestart(monitor);
505                 }
506 
507                 if (installedTools) {
508                     notifyToolsNeedsToBeRestarted();
509                 }
510 
511                 if (numInstalled == 0) {
512                     monitor.setDescription("Done. Nothing was installed.");
513                 } else {
514                     monitor.setDescription("Done. %1$d %2$s installed.",
515                             numInstalled,
516                             numInstalled == 1 ? "package" : "packages");
517 
518                     //notify listeners something was installed, so that they can refresh
519                     reloadSdk();
520                 }
521             }
522         });
523     }
524 
525     /**
526      * Attempts to restart ADB.
527      * <p/>
528      * If the "ask before restart" setting is set (the default), prompt the user whether
529      * now is a good time to restart ADB.
530      *
531      * @param monitor
532      */
askForAdbRestart(ITaskMonitor monitor)533     private void askForAdbRestart(ITaskMonitor monitor) {
534         final boolean[] canRestart = new boolean[] { true };
535 
536         if (getWindowShell() != null && getSettingsController().getAskBeforeAdbRestart()) {
537             // need to ask for permission first
538             Display display = getWindowShell().getDisplay();
539 
540             display.syncExec(new Runnable() {
541                 public void run() {
542                     canRestart[0] = MessageDialog.openQuestion(getWindowShell(),
543                             "ADB Restart",
544                             "A package that depends on ADB has been updated. It is recommended " +
545                             "to restart ADB. Is it OK to do it now? If not, you can restart it " +
546                             "manually later.");
547                 }
548             });
549         }
550 
551         if (canRestart[0]) {
552             AdbWrapper adb = new AdbWrapper(getOsSdkRoot(), monitor);
553             adb.stopAdb();
554             adb.startAdb();
555         }
556     }
557 
notifyToolsNeedsToBeRestarted()558     private void notifyToolsNeedsToBeRestarted() {
559         if (getWindowShell() == null) {
560             // We don't need to print anything if this is a standalone console update.
561             return;
562         }
563 
564         Display display = getWindowShell().getDisplay();
565 
566         display.syncExec(new Runnable() {
567             public void run() {
568                 MessageDialog.openInformation(getWindowShell(),
569                         "Android Tools Updated",
570                         "The Android SDK and AVD Manager that you are currently using has been updated. " +
571                         "It is recommended that you now close the manager window and re-open it. " +
572                         "If you started this window from Eclipse, please check if the Android " +
573                         "plug-in needs to be updated.");
574             }
575         });
576     }
577 
578 
579     /**
580      * Tries to update all the *existing* local packages.
581      * This version *requires* to be run with a GUI.
582      * <p/>
583      * There are two modes of operation:
584      * <ul>
585      * <li>If selectedArchives is null, refreshes all sources, compares the available remote
586      * packages with the current local ones and suggest updates to be done to the user (including
587      * new platforms that the users doesn't have yet).
588      * <li>If selectedArchives is not null, this represents a list of archives/packages that
589      * the user wants to install or update, so just process these.
590      * </ul>
591      *
592      * @param selectedArchives The list of remote archives to consider for the update.
593      *  This can be null, in which case a list of remote archive is fetched from all
594      *  available sources.
595      */
updateOrInstallAll_WithGUI(Collection<Archive> selectedArchives)596     public void updateOrInstallAll_WithGUI(Collection<Archive> selectedArchives) {
597         if (selectedArchives == null) {
598             refreshSources(true);
599         }
600 
601         UpdaterLogic ul = new UpdaterLogic();
602         ArrayList<ArchiveInfo> archives = ul.computeUpdates(
603                 selectedArchives,
604                 getSources(),
605                 getLocalSdkParser().getPackages());
606 
607         if (selectedArchives == null) {
608             ul.addNewPlatforms(archives, getSources(), getLocalSdkParser().getPackages());
609         }
610 
611         // TODO if selectedArchives is null and archives.len==0, find if there are
612         // any new platform we can suggest to install instead.
613 
614         UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, archives);
615         dialog.open();
616 
617         ArrayList<ArchiveInfo> result = dialog.getResult();
618         if (result != null && result.size() > 0) {
619             installArchives(result);
620         }
621     }
622 
623     /**
624      * Tries to update all the *existing* local packages.
625      * This version is intended to run without a GUI and
626      * only outputs to the current {@link ISdkLog}.
627      *
628      * @param pkgFilter A list of {@link SdkRepository#NODES} to limit the type of packages
629      *   we can update. A null or empty list means to update everything possible.
630      * @param includeObsoletes True to also list and install obsolete packages.
631      * @param dryMode True to check what would be updated/installed but do not actually
632      *   download or install anything.
633      */
updateOrInstallAll_NoGUI( Collection<String> pkgFilter, boolean includeObsoletes, boolean dryMode)634     public void updateOrInstallAll_NoGUI(
635             Collection<String> pkgFilter,
636             boolean includeObsoletes,
637             boolean dryMode) {
638 
639         refreshSources(true);
640 
641         UpdaterLogic ul = new UpdaterLogic();
642         ArrayList<ArchiveInfo> archives = ul.computeUpdates(
643                 null /*selectedArchives*/,
644                 getSources(),
645                 getLocalSdkParser().getPackages());
646 
647         ul.addNewPlatforms(archives, getSources(), getLocalSdkParser().getPackages());
648 
649         // Filter the selected archives to only keep the ones matching the filter
650         if (pkgFilter != null && pkgFilter.size() > 0 && archives != null && archives.size() > 0) {
651             // Map filter types to an SdkRepository Package type.
652             HashMap<String, Class<? extends Package>> pkgMap =
653                 new HashMap<String, Class<? extends Package>>();
654             pkgMap.put(SdkRepository.NODE_PLATFORM, PlatformPackage.class);
655             pkgMap.put(SdkRepository.NODE_ADD_ON,   AddonPackage.class);
656             pkgMap.put(SdkRepository.NODE_TOOL,     ToolPackage.class);
657             pkgMap.put(SdkRepository.NODE_DOC,      DocPackage.class);
658             pkgMap.put(SdkRepository.NODE_SAMPLE,   SamplePackage.class);
659             pkgMap.put(SdkRepository.NODE_EXTRA,    ExtraPackage.class);
660 
661             if (SdkRepository.NODES.length != pkgMap.size()) {
662                 // Sanity check in case we forget to update this package map.
663                 // We don't cancel the operation though.
664                 mSdkLog.error(null,
665                     "Filter Mismatch!\nThe package filter list has changed. Please report this.");
666             }
667 
668             // Now make a set of the types that are allowed by the filter.
669             HashSet<Class<? extends Package>> allowedPkgSet =
670                 new HashSet<Class<? extends Package>>();
671             for (String type : pkgFilter) {
672                 if (pkgMap.containsKey(type)) {
673                     allowedPkgSet.add(pkgMap.get(type));
674                 } else {
675                     // This should not happen unless there's a mismatch in the package map.
676                     mSdkLog.error(null, "Ignoring unknown package filter '%1$s'", type);
677                 }
678             }
679 
680             // we don't need the map anymore
681             pkgMap = null;
682 
683             Iterator<ArchiveInfo> it = archives.iterator();
684             while (it.hasNext()) {
685                 boolean keep = false;
686                 ArchiveInfo ai = it.next();
687                 Archive a = ai.getNewArchive();
688                 if (a != null) {
689                     Package p = a.getParentPackage();
690                     if (p != null && allowedPkgSet.contains(p.getClass())) {
691                         keep = true;
692                     }
693                 }
694 
695                 if (!keep) {
696                     it.remove();
697                 }
698             }
699 
700             if (archives.size() == 0) {
701                 mSdkLog.warning("The package filter removed all packages. There is nothing to install.\n" +
702                         "Please consider trying updating again without a package filter.");
703                 return;
704             }
705         }
706 
707         if (!includeObsoletes && archives != null && archives.size() > 0) {
708             // Filter obsolete packages out
709             Iterator<ArchiveInfo> it = archives.iterator();
710             while (it.hasNext()) {
711                 boolean keep = false;
712                 ArchiveInfo ai = it.next();
713                 Archive a = ai.getNewArchive();
714                 if (a != null) {
715                     Package p = a.getParentPackage();
716                     if (p != null && !p.isObsolete()) {
717                         keep = true;
718                     }
719                 }
720 
721                 if (!keep) {
722                     it.remove();
723                 }
724             }
725 
726             if (archives.size() == 0) {
727                 mSdkLog.warning("All candidate packages were obsolete. Nothing to install.");
728                 return;
729             }
730         }
731 
732         // TODO if selectedArchives is null and archives.len==0, find if there are
733         // any new platform we can suggest to install instead.
734 
735         if (archives != null && archives.size() > 0) {
736             if (dryMode) {
737                 mSdkLog.printf("Packages selected for install:\n");
738                 for (ArchiveInfo ai : archives) {
739                     Archive a = ai.getNewArchive();
740                     if (a != null) {
741                         Package p = a.getParentPackage();
742                         if (p != null) {
743                             mSdkLog.printf("- %1$s\n", p.getShortDescription());
744                         }
745                     }
746                 }
747                 mSdkLog.printf("\nDry mode is on so nothing will actually be installed.\n");
748             } else {
749                 installArchives(archives);
750             }
751         } else {
752             mSdkLog.printf("There is nothing to install or update.\n");
753         }
754     }
755 
756     /**
757      * Refresh all sources. This is invoked either internally (reusing an existing monitor)
758      * or as a UI callback on the remote page "Refresh" button (in which case the monitor is
759      * null and a new task should be created.)
760      *
761      * @param forceFetching When true, load sources that haven't been loaded yet.
762      *                      When false, only refresh sources that have been loaded yet.
763      */
refreshSources(final boolean forceFetching)764     public void refreshSources(final boolean forceFetching) {
765         assert mTaskFactory != null;
766 
767         final boolean forceHttp = getSettingsController().getForceHttp();
768 
769         mTaskFactory.start("Refresh Sources", new ITask() {
770             public void run(ITaskMonitor monitor) {
771                 RepoSource[] sources = mSources.getSources();
772                 monitor.setProgressMax(sources.length);
773                 for (RepoSource source : sources) {
774                     if (forceFetching ||
775                             source.getPackages() != null ||
776                             source.getFetchError() != null) {
777                         source.load(monitor.createSubMonitor(1), forceHttp);
778                     }
779                     monitor.incProgress(1);
780                 }
781             }
782         });
783     }
784 }
785