Merge "SDK Manager fix: override in-memory sources when checking for updates." into tools_r20
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java
index 2f154a6..e77c7d4 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DownloadCache.java
@@ -79,9 +79,11 @@
     private static final String KEY_URL = "URL";                        //$NON-NLS-1$
 
     /** Prefix of binary files stored in the {@link SdkConstants#FD_CACHE} directory. */
-    private final static String BIN_FILE_PREFIX = "sdkbin-";            //$NON-NLS-1$
+    private final static String BIN_FILE_PREFIX = "sdkbin";             //$NON-NLS-1$
     /** Prefix of meta info files stored in the {@link SdkConstants#FD_CACHE} directory. */
-    private final static String INFO_FILE_PREFIX = "sdkinf-";           //$NON-NLS-1$
+    private final static String INFO_FILE_PREFIX = "sdkinf";            //$NON-NLS-1$
+    /* Revision suffixed to the prefix. */
+    private final static String REV_FILE_PREFIX = "-1_";                //$NON-NLS-1$
 
     /**
      * Minimum time before we consider a cached entry is potentially stale.
@@ -226,6 +228,31 @@
     }
 
     /**
+     * Removes all obsolete cached files from the cache directory
+     * that do not match the latest revision.
+     */
+    public void clearOldCache() {
+        String prefix1 = BIN_FILE_PREFIX + REV_FILE_PREFIX;
+        String prefix2 = INFO_FILE_PREFIX + REV_FILE_PREFIX;
+        if (mCacheRoot != null) {
+            File[] files = mCacheRoot.listFiles();
+            if (files != null) {
+                for (File f : files) {
+                    if (f.isFile()) {
+                        String name = f.getName();
+                        if (name.startsWith(BIN_FILE_PREFIX) ||
+                                name.startsWith(INFO_FILE_PREFIX)) {
+                            if (!name.startsWith(prefix1) && !name.startsWith(prefix2)) {
+                                f.delete();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Returns the directory to be used as a cache.
      * Creates it if necessary.
      * Makes it possible to disable or override the cache location in unit tests.
@@ -735,12 +762,13 @@
         leaf = leaf.replaceAll("__+", "_");
 
         leaf = hash + '-' + leaf;
-        int n = 64 - BIN_FILE_PREFIX.length();
+        String prefix = BIN_FILE_PREFIX + REV_FILE_PREFIX;
+        int n = 64 - prefix.length();
         if (leaf.length() > n) {
             leaf = leaf.substring(0, n);
         }
 
-        return BIN_FILE_PREFIX + leaf;
+        return prefix + leaf;
     }
 
     private String getInfoFilename(String cacheFilename) {
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java
index 983c94d..822240c 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackageLoader.java
@@ -70,7 +70,7 @@
 
     /**
      * Interface for the callback called by
-     * {@link PackageLoader#loadPackages(ISourceLoadedCallback)}.
+     * {@link PackageLoader#loadPackages(boolean, ISourceLoadedCallback)}.
      * <p/>
      * After processing each source, the package loader calls {@link #onUpdateSource}
      * with the list of packages found in that source.
@@ -184,8 +184,18 @@
      * The caller is responsible to accumulate the packages given to the callback
      * after each source is finished loaded. In return the callback tells the loader
      * whether to continue loading sources.
+     * <p/>
+     * Normally this method doesn't access the remote source if it's already
+     * been loaded in the in-memory source (e.g. don't fetch twice).
+     *
+     * @param overrideExisting Set this to true  when the caller wants to
+     *          check for updates and discard any existing source already
+     *          loaded in memory. It should be false for normal use.
+     * @param sourceLoadedCallback The callback to invoke for each loaded source.
      */
-    public void loadPackages(final ISourceLoadedCallback sourceLoadedCallback) {
+    public void loadPackages(
+            final boolean overrideExisting,
+            final ISourceLoadedCallback sourceLoadedCallback) {
         try {
             if (mUpdaterData == null) {
                 return;
@@ -218,7 +228,7 @@
                             subMonitor.setProgressMax(sources.length);
                             for (SdkSource source : sources) {
                                 Package[] pkgs = source.getPackages();
-                                if (pkgs == null) {
+                                if (pkgs == null || overrideExisting) {
                                     source.load(getDownloadCache(),
                                             subMonitor.createSubMonitor(1),
                                             forceHttp);
@@ -248,7 +258,8 @@
     }
 
     /**
-     * Load packages, source by source using {@link #loadPackages(ISourceLoadedCallback)},
+     * Load packages, source by source using
+     * {@link #loadPackages(boolean, ISourceLoadedCallback)},
      * and executes the given {@link IAutoInstallTask} on the current package list.
      * That is for each package known, the install task is queried to find if
      * the package is the one to be installed or updated.
@@ -280,7 +291,7 @@
             final int installFlags,
             final IAutoInstallTask installTask) {
 
-        loadPackages(new ISourceLoadedCallback() {
+        loadPackages(false /*overrideExisting*/, new ISourceLoadedCallback() {
             List<Archive> mArchivesToInstall = new ArrayList<Archive>();
             Map<Package, File> mInstallPaths = new HashMap<Package, File>();
 
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
index 0b6d725..5ea9bb0 100755
--- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
+++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/sdkman2/PackagesPage.java
@@ -174,12 +174,12 @@
         // First a package loader is created that only checks
         // the local cache xml files. It populates the package
         // list based on what the client got last, essentially.
-        loadPackages(true /*useLocalCache*/);
+        loadPackages(true /*useLocalCache*/, false /*overrideExisting*/);
 
         // Next a regular package loader is created that will
         // respect the expiration and refresh parameters of the
         // download cache.
-        loadPackages(false /*useLocalCache*/);
+        loadPackages(false /*useLocalCache*/, true /*overrideExisting*/);
     }
 
     @SuppressWarnings("unused")
@@ -591,7 +591,7 @@
      * cache and refreshing strategy as needed.
      */
     private void loadPackages() {
-        loadPackages(false /*useLocalCache*/);
+        loadPackages(false /*useLocalCache*/, false /*overrideExisting*/);
     }
 
     /**
@@ -603,7 +603,7 @@
      *  manifests. This is used once the very first time the sdk manager window opens
      *  and is typically followed by a regular load with refresh.
      */
-    private void loadPackages(final boolean useLocalCache) {
+    private void loadPackages(final boolean useLocalCache, final boolean overrideExisting) {
         if (mUpdaterData == null) {
             return;
         }
@@ -635,7 +635,7 @@
         assert packageLoader != null;
 
         mDiffLogic.updateStart();
-        packageLoader.loadPackages(new ISourceLoadedCallback() {
+        packageLoader.loadPackages(overrideExisting, new ISourceLoadedCallback() {
             @Override
             public boolean onUpdateSource(SdkSource source, Package[] newPackages) {
                 // This runs in a thread and must not access UI directly.