Merge "Add lint check for "old" proguard configurations with generic Android config"
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 2584933..f6fe09f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -20,7 +20,9 @@
 import com.android.ide.common.log.ILogger;
 import com.android.ide.common.resources.ResourceFile;
 import com.android.ide.common.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution;
 import com.android.ide.eclipse.adt.internal.VersionCheck;
+import com.android.ide.eclipse.adt.internal.actions.SdkManagerAction;
 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
 import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
@@ -42,6 +44,7 @@
 import com.android.sdklib.IAndroidTarget;
 import com.android.sdklib.SdkConstants;
 
+import org.eclipse.core.commands.Command;
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarkerDelta;
 import org.eclipse.core.resources.IProject;
@@ -59,8 +62,10 @@
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceDialog;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.util.IPropertyChangeListener;
@@ -78,11 +83,14 @@
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.browser.IWebBrowser;
 import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
+import org.eclipse.ui.commands.ICommandService;
 import org.eclipse.ui.console.ConsolePlugin;
 import org.eclipse.ui.console.IConsole;
 import org.eclipse.ui.console.IConsoleConstants;
 import org.eclipse.ui.console.MessageConsole;
 import org.eclipse.ui.console.MessageConsoleStream;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.handlers.IHandlerService;
 import org.eclipse.ui.ide.IDE;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.texteditor.AbstractTextEditor;
@@ -162,15 +170,23 @@
      * checkSdkLocationAndId.
      */
     public static abstract class CheckSdkErrorHandler {
+
+        public enum Solution {
+            NONE,
+            OPEN_SDK_MANAGER,
+            OPEN_ANDROID_PREFS,
+            OPEN_P2_UPDATE
+        }
+
         /** Handle an error message during sdk location check. Returns whatever
          * checkSdkLocationAndId() should returns.
          */
-        public abstract boolean handleError(String message);
+        public abstract boolean handleError(Solution solution, String message);
 
         /** Handle a warning message during sdk location check. Returns whatever
          * checkSdkLocationAndId() should returns.
          */
-        public abstract boolean handleWarning(String message);
+        public abstract boolean handleWarning(Solution solution, String message);
     }
 
     /**
@@ -1064,17 +1080,133 @@
         }
 
         return checkSdkLocationAndId(sdkLocation, new CheckSdkErrorHandler() {
+            private String mTitle = "Android SDK Verification";
             @Override
-            public boolean handleError(String message) {
-                AdtPlugin.displayError("Android SDK Verification", message);
+            public boolean handleError(Solution solution, String message) {
+                displayMessage(solution, message, MessageDialog.ERROR);
                 return false;
             }
 
             @Override
-            public boolean handleWarning(String message) {
-                AdtPlugin.displayWarning("Android SDK Verification", message);
+            public boolean handleWarning(Solution solution, String message) {
+                displayMessage(solution, message, MessageDialog.WARNING);
                 return true;
             }
+
+            private void displayMessage(
+                    final Solution solution,
+                    final String message,
+                    final int dialogImageType) {
+                final Display disp = getDisplay();
+                disp.asyncExec(new Runnable() {
+                    @Override
+                    public void run() {
+                        Shell shell = disp.getActiveShell();
+                        if (shell == null) {
+                            return;
+                        }
+
+                        String customLabel = null;
+                        switch(solution) {
+                        case OPEN_ANDROID_PREFS:
+                            customLabel = "Open Preferences";
+                            break;
+                        case OPEN_P2_UPDATE:
+                            customLabel = "Check for Updates";
+                            break;
+                        case OPEN_SDK_MANAGER:
+                            customLabel = "Open SDK Manager";
+                            break;
+                        }
+
+                        String btnLabels[] = new String[customLabel == null ? 1 : 2];
+                        btnLabels[0] = customLabel;
+                        btnLabels[btnLabels.length - 1] = IDialogConstants.CLOSE_LABEL;
+
+                        MessageDialog dialog = new MessageDialog(
+                                shell, // parent
+                                mTitle,
+                                null, // dialogTitleImage
+                                message,
+                                dialogImageType,
+                                btnLabels,
+                                btnLabels.length - 1);
+                        int index = dialog.open();
+
+                        if (customLabel != null && index == 0) {
+                            switch(solution) {
+                            case OPEN_ANDROID_PREFS:
+                                openAndroidPrefs();
+                                break;
+                            case OPEN_P2_UPDATE:
+                                openP2Update();
+                                break;
+                            case OPEN_SDK_MANAGER:
+                                openSdkManager();
+                                break;
+                            }
+                        }
+                    }
+                });
+            }
+
+            private void openSdkManager() {
+                // Windows only: open the standalone external SDK Manager since we know
+                // that ADT on Windows is bound to be locking some SDK folders.
+                if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) {
+                    if (SdkManagerAction.openExternalSdkManager()) {
+                        return;
+                    }
+                }
+
+                // Otherwise open the regular SDK Manager bundled within ADT
+                if (!SdkManagerAction.openAdtSdkManager()) {
+                    // We failed because the SDK location is undefined. In this case
+                    // let's open the preferences instead.
+                    openAndroidPrefs();
+                }
+            }
+
+            private void openP2Update() {
+                Display disp = getDisplay();
+                if (disp == null) {
+                    return;
+                }
+                disp.asyncExec(new Runnable() {
+                    @Override
+                    public void run() {
+                        String cmdId = "org.eclipse.equinox.p2.ui.sdk.update";  //$NON-NLS-1$
+                        IWorkbench wb = PlatformUI.getWorkbench();
+                        if (wb == null) {
+                            return;
+                        }
+
+                        ICommandService cs = (ICommandService) wb.getService(ICommandService.class);
+                        IHandlerService is = (IHandlerService) wb.getService(IHandlerService.class);
+                        if (cs == null || is == null) {
+                            return;
+                        }
+
+                        Command cmd = cs.getCommand(cmdId);
+                        if (cmd != null && cmd.isDefined()) {
+                            try {
+                                is.executeCommand(cmdId, null/*event*/);
+                            } catch (Exception ignore) {
+                                AdtPlugin.log(ignore, "Failed to execute command %s", cmdId);
+                            }
+                        }
+                    }
+                });
+            }
+
+            private void openAndroidPrefs() {
+                PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(
+                        getDisplay().getActiveShell(),
+                        "com.android.ide.eclipse.preferences.main", //$NON-NLS-1$ preferencePageId
+                        null,  // displayedIds
+                        null); // data
+                dialog.open();
+            }
         });
     }
 
@@ -1093,6 +1225,7 @@
         File osSdkFolder = new File(osSdkLocation);
         if (osSdkFolder.isDirectory() == false) {
             return errorHandler.handleError(
+                    Solution.OPEN_ANDROID_PREFS,
                     String.format(Messages.Could_Not_Find_Folder, osSdkLocation));
         }
 
@@ -1100,6 +1233,7 @@
         File toolsFolder = new File(osTools);
         if (toolsFolder.isDirectory() == false) {
             return errorHandler.handleError(
+                    Solution.OPEN_ANDROID_PREFS,
                     String.format(Messages.Could_Not_Find_Folder_In_SDK,
                             SdkConstants.FD_TOOLS, osSdkLocation));
         }
@@ -1113,13 +1247,17 @@
         // check that we have both the tools component and the platform-tools component.
         String platformTools = osSdkLocation + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER;
         if (checkFolder(platformTools) == false) {
-            return errorHandler.handleWarning("SDK Platform Tools component is missing!\n" +
+            return errorHandler.handleWarning(
+                    Solution.OPEN_SDK_MANAGER,
+                    "SDK Platform Tools component is missing!\n" +
                     "Please use the SDK Manager to install it.");
         }
 
         String tools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER;
         if (checkFolder(tools) == false) {
-            return errorHandler.handleError("SDK Tools component is missing!\n" +
+            return errorHandler.handleError(
+                    Solution.OPEN_SDK_MANAGER,
+                    "SDK Tools component is missing!\n" +
                     "Please use the SDK Manager to install it.");
         }
 
@@ -1131,7 +1269,9 @@
         };
         for (String file : filesToCheck) {
             if (checkFile(file) == false) {
-                return errorHandler.handleError(String.format(Messages.Could_Not_Find, file));
+                return errorHandler.handleError(
+                        Solution.OPEN_ANDROID_PREFS,
+                        String.format(Messages.Could_Not_Find, file));
             }
         }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
index 9055b29..b468c5e 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution;
 import com.android.ide.eclipse.adt.Messages;
 import com.android.sdklib.SdkConstants;
 import com.android.sdklib.repository.PkgProps;
@@ -103,7 +104,9 @@
 
         // Failed to get the min plugin version number?
         if (minMajorVersion == -1 || minMinorVersion == -1 || minMicroVersion ==-1) {
-            return errorHandler.handleWarning(Messages.VersionCheck_Plugin_Version_Failed);
+            return errorHandler.handleWarning(
+                    Solution.OPEN_SDK_MANAGER,
+                    Messages.VersionCheck_Plugin_Version_Failed);
         }
 
         // test the plugin number
@@ -126,6 +129,7 @@
 
         if (valid == false) {
             return errorHandler.handleError(
+                    Solution.OPEN_P2_UPDATE,
                     String.format(Messages.VersionCheck_Plugin_Too_Old,
                             minMajorVersion, minMinorVersion, minMicroVersion, versionString));
         }
@@ -163,6 +167,7 @@
             // this is a warning only as we need to parse the SDK to allow updating
             // of the tools!
             return errorHandler.handleWarning(
+                    Solution.OPEN_SDK_MANAGER,
                     String.format(Messages.VersionCheck_Tools_Too_Old,
                             MIN_TOOLS_REV, toolsRevision));
         }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
index 970bc47..f8a080a 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java
@@ -22,6 +22,7 @@
 import com.android.sdklib.SdkConstants;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
 
 import org.eclipse.core.filesystem.EFS;
 import org.eclipse.core.filesystem.IFileStore;
@@ -175,7 +176,7 @@
 
                 int err = GrabProcessOutput.grabProcessOutput(
                         process,
-                        true /*waitForReaders*/,
+                        Wait.WAIT_FOR_READERS,
                         new IProcessOutput() {
                             @Override
                             public void out(String line) {
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
index ddf180f..0ff50b5 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
@@ -20,6 +20,11 @@
 import com.android.ide.eclipse.adt.internal.build.DexWrapper;

 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;

 import com.android.ide.eclipse.adt.internal.sdk.Sdk;

+import com.android.sdklib.SdkConstants;

+import com.android.sdklib.io.FileOp;

+import com.android.sdklib.util.GrabProcessOutput;

+import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;

+import com.android.sdklib.util.GrabProcessOutput.Wait;

 import com.android.sdkuilib.repository.ISdkChangeListener;

 import com.android.sdkuilib.repository.SdkUpdaterWindow;

 import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;

@@ -31,6 +36,8 @@
 import org.eclipse.ui.IWorkbenchWindow;

 import org.eclipse.ui.IWorkbenchWindowActionDelegate;

 

+import java.io.File;

+

 /**

  * Delegate for the toolbar/menu action "Android SDK Manager".

  * It displays the Android SDK Manager.

@@ -49,95 +56,163 @@
 

     @Override

     public void run(IAction action) {

-        final Sdk sdk = Sdk.getCurrent();

-        if (sdk != null) {

-

-            // Runs the updater window, directing only warning/errors logs to the ADT console

-            // (normal log is just dropped, which is fine since the SDK Manager has its own

-            // log window now.)

-

-            SdkUpdaterWindow window = new SdkUpdaterWindow(

-                    AdtPlugin.getDisplay().getActiveShell(),

-                    new AdtConsoleSdkLog() {

-                        @Override

-                        public void printf(String msgFormat, Object... args) {

-                            // Do not show non-error/warning log in Eclipse.

-                        };

-                    },

-                    sdk.getSdkLocation(),

-                    SdkInvocationContext.IDE);

-

-            ISdkChangeListener listener = new ISdkChangeListener() {

-                @Override

-                public void onSdkLoaded() {

-                    // Ignore initial load of the SDK.

-                }

-

-                /**

-                 * Unload all we can from the SDK before new packages are installed.

-                 * Typically we need to get rid of references to dx from platform-tools

-                 * and to any platform resource data.

-                 * <p/>

-                 * {@inheritDoc}

-                 */

-                @Override

-                public void preInstallHook() {

-

-                    // TODO we need to unload as much of as SDK as possible. Otherwise

-                    // on Windows we end up with Eclipse locking some files and we can't

-                    // replace them.

-                    //

-                    // At this point, we know what the user wants to install so it would be

-                    // possible to pass in flags to know what needs to be unloaded. Typically

-                    // we need to:

-                    // - unload dex if platform-tools is going to be updated. There's a vague

-                    //   attempt below at removing any references to dex and GCing. Seems

-                    //   to do the trick.

-                    // - unload any target that is going to be updated since it may have

-                    //   resource data used by a current layout editor (e.g. data/*.ttf

-                    //   and various data/res/*.xml).

-                    //

-                    // Most important we need to make sure there isn't a build going on

-                    // and if there is one, either abort it or wait for it to complete and

-                    // then we want to make sure we don't get any attempt to use the SDK

-                    // before the postInstallHook is called.

-

-                    if (sdk != null) {

-                        sdk.unloadTargetData(true /*preventReload*/);

-

-                        DexWrapper dx = sdk.getDexWrapper();

-                        dx.unload();

-                    }

-                }

-

-                /**

-                 * Nothing to do. We'll reparse the SDK later in onSdkReload.

-                 * <p/>

-                 * {@inheritDoc}

-                 */

-                @Override

-                public void postInstallHook() {

-                }

-

-                /**

-                 * Reparse the SDK in case anything was add/removed.

-                 * <p/>

-                 * {@inheritDoc}

-                 */

-                @Override

-                public void onSdkReload() {

-                    AdtPlugin.getDefault().reparseSdk();

-                }

-            };

-

-            window.addListener(listener);

-            window.open();

-        } else {

-            AdtPlugin.displayError("Android SDK",

+        if (!openAdtSdkManager()) {

+            AdtPlugin.displayError(

+                    "Android SDK",

                     "Location of the Android SDK has not been setup in the preferences.");

         }

     }

 

+    /**

+     * Opens the SDK Manager as an external application.

+     * This call is asynchronous, it doesn't wait for the manager to be closed.

+     *

+     * @return True if the application was found and executed. False if it could not

+     *   be located or could not be launched.

+     */

+    public static boolean openExternalSdkManager() {

+        final Sdk sdk = Sdk.getCurrent();

+        if (sdk == null) {

+            return false;

+        }

+

+        File androidBat = FileOp.append(

+                sdk.getSdkLocation(),

+                SdkConstants.FD_TOOLS,

+                SdkConstants.androidCmdName());

+

+        if (!androidBat.exists()) {

+            return false;

+        }

+

+        try {

+            final AdtConsoleSdkLog logger = new AdtConsoleSdkLog();

+

+            String command[] = new String[] {

+                    androidBat.getAbsolutePath(),

+                    "sdk"   //$NON-NLS-1$

+            };

+            Process process = Runtime.getRuntime().exec(command);

+            GrabProcessOutput.grabProcessOutput(

+                    process,

+                    Wait.ASYNC,

+                    new IProcessOutput() {

+                        @Override

+                        public void out(String line) {

+                            // Ignore stdout

+                        }

+

+                        @Override

+                        public void err(String line) {

+                            if (line != null) {

+                                logger.printf("[SDK Manager] %s", line);

+                            }

+                        }

+                    });

+        } catch (Exception ignore) {

+        }

+

+        return true;

+    }

+

+    /**

+     * Opens the SDK Manager bundled within ADT.

+     * The call is blocking and does not return till the SD Manager window is closed.

+     *

+     * @return True if the SDK location is known and the SDK Manager was started.

+     *   False if the SDK location is not set and we can't open a SDK Manager to

+     *   manage files in an unknown location.

+     */

+    public static boolean openAdtSdkManager() {

+        final Sdk sdk = Sdk.getCurrent();

+        if (sdk == null) {

+            return false;

+        }

+

+        // Runs the updater window, directing only warning/errors logs to the ADT console

+        // (normal log is just dropped, which is fine since the SDK Manager has its own

+        // log window now.)

+

+        SdkUpdaterWindow window = new SdkUpdaterWindow(

+                AdtPlugin.getDisplay().getActiveShell(),

+                new AdtConsoleSdkLog() {

+                    @Override

+                    public void printf(String msgFormat, Object... args) {

+                        // Do not show non-error/warning log in Eclipse.

+                    };

+                },

+                sdk.getSdkLocation(),

+                SdkInvocationContext.IDE);

+

+        ISdkChangeListener listener = new ISdkChangeListener() {

+            @Override

+            public void onSdkLoaded() {

+                // Ignore initial load of the SDK.

+            }

+

+            /**

+             * Unload all we can from the SDK before new packages are installed.

+             * Typically we need to get rid of references to dx from platform-tools

+             * and to any platform resource data.

+             * <p/>

+             * {@inheritDoc}

+             */

+            @Override

+            public void preInstallHook() {

+

+                // TODO we need to unload as much of as SDK as possible. Otherwise

+                // on Windows we end up with Eclipse locking some files and we can't

+                // replace them.

+                //

+                // At this point, we know what the user wants to install so it would be

+                // possible to pass in flags to know what needs to be unloaded. Typically

+                // we need to:

+                // - unload dex if platform-tools is going to be updated. There's a vague

+                //   attempt below at removing any references to dex and GCing. Seems

+                //   to do the trick.

+                // - unload any target that is going to be updated since it may have

+                //   resource data used by a current layout editor (e.g. data/*.ttf

+                //   and various data/res/*.xml).

+                //

+                // Most important we need to make sure there isn't a build going on

+                // and if there is one, either abort it or wait for it to complete and

+                // then we want to make sure we don't get any attempt to use the SDK

+                // before the postInstallHook is called.

+

+                if (sdk != null) {

+                    sdk.unloadTargetData(true /*preventReload*/);

+

+                    DexWrapper dx = sdk.getDexWrapper();

+                    dx.unload();

+                }

+            }

+

+            /**

+             * Nothing to do. We'll reparse the SDK later in onSdkReload.

+             * <p/>

+             * {@inheritDoc}

+             */

+            @Override

+            public void postInstallHook() {

+            }

+

+            /**

+             * Reparse the SDK in case anything was add/removed.

+             * <p/>

+             * {@inheritDoc}

+             */

+            @Override

+            public void onSdkReload() {

+                AdtPlugin.getDefault().reparseSdk();

+            }

+        };

+

+        window.addListener(listener);

+        window.open();

+

+        return true;

+    }

+

     @Override

     public void selectionChanged(IAction action, ISelection selection) {

         // nothing related to the current selection.

diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
index 5d2fe78..7536c2f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java
@@ -41,6 +41,7 @@
 import com.android.sdklib.internal.build.SignedJarBuilder;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
@@ -1199,7 +1200,7 @@
 
         return GrabProcessOutput.grabProcessOutput(
                 process,
-                false /*waitForReaders*/,
+                Wait.WAIT_FOR_PROCESS,
                 new IProcessOutput() {
 
                     @SuppressWarnings("unused")
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
index 426149e..eff883b 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.preferences;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener;
 import com.android.sdklib.IAndroidTarget;
@@ -140,13 +141,17 @@
             boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(fileName,
                     new AdtPlugin.CheckSdkErrorHandler() {
                 @Override
-                public boolean handleError(String message) {
+                public boolean handleError(
+                        CheckSdkErrorHandler.Solution solution,
+                        String message) {
                     setErrorMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
                     return false;  // Apply/OK must be disabled
                 }
 
                 @Override
-                public boolean handleWarning(String message) {
+                public boolean handleWarning(
+                        CheckSdkErrorHandler.Solution solution,
+                        String message) {
                     showMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$
                     return true;  // Apply/OK must be enabled
                 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
index 2b3e2c2..96add71 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.adt.internal.welcome;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
 import com.android.sdkstats.DdmsPreferenceStore;
 import com.android.sdkstats.SdkStatsService;
@@ -83,12 +84,16 @@
             boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(osSdkPath,
                     new AdtPlugin.CheckSdkErrorHandler() {
                 @Override
-                public boolean handleError(String message) {
+                public boolean handleError(
+                        CheckSdkErrorHandler.Solution solution,
+                        String message) {
                     return false;
                 }
 
                 @Override
-                public boolean handleWarning(String message) {
+                public boolean handleWarning(
+                        CheckSdkErrorHandler.Solution  solution,
+                        String message) {
                     return true;
                 }
             });
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
index d418432..4e580fe 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java
@@ -16,6 +16,7 @@
 package com.android.ide.eclipse.adt.internal.welcome;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler;
 
 import org.eclipse.jface.dialogs.IMessageProvider;
 import org.eclipse.jface.wizard.WizardPage;
@@ -262,14 +263,18 @@
                     AdtPlugin.getDefault().checkSdkLocationAndId(path,
                             new AdtPlugin.CheckSdkErrorHandler() {
                         @Override
-                        public boolean handleError(String message) {
+                        public boolean handleError(
+                                CheckSdkErrorHandler.Solution solution,
+                                String message) {
                             message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$
                             errorReference.set(message);
                             return false;  // Apply/OK must be disabled
                         }
 
                         @Override
-                        public boolean handleWarning(String message) {
+                        public boolean handleWarning(
+                                CheckSdkErrorHandler.Solution solution,
+                                String message) {
                             message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$
                             warningReference.set(message);
                             return true;  // Apply/OK must be enabled
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
index 4ce8230..8fcf902 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java
@@ -24,6 +24,7 @@
 import com.android.sdklib.internal.build.KeystoreHelper;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
@@ -543,7 +544,7 @@
 
             int status = GrabProcessOutput.grabProcessOutput(
                     process,
-                    true /*waitForReaders*/,
+                    Wait.WAIT_FOR_READERS,
                     new IProcessOutput() {
                         @Override
                         public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
index 436f2e8..db3cc33 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java
@@ -28,6 +28,7 @@
 import com.android.sdklib.internal.project.ProjectProperties;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
 import com.android.util.Pair;
 
 import java.io.File;
@@ -1398,7 +1399,7 @@
 
             int status = GrabProcessOutput.grabProcessOutput(
                     process,
-                    true /*waitForReaders*/,
+                    Wait.WAIT_FOR_READERS,
                     new IProcessOutput() {
                         @Override
                         public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
index 06f5351..af5b401 100644
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java
@@ -20,6 +20,7 @@
 import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException;
 import com.android.sdklib.util.GrabProcessOutput;
 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;
+import com.android.sdklib.util.GrabProcessOutput.Wait;
 
 import java.io.File;
 import java.io.IOException;
@@ -107,7 +108,7 @@
             Process process = Runtime.getRuntime().exec(commandArray);
             result = GrabProcessOutput.grabProcessOutput(
                     process,
-                    true /*waitForReaders*/,
+                    Wait.WAIT_FOR_READERS,
                     new IProcessOutput() {
                         @Override
                         public void out(String line) {
diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
index 9e50430..2e2396f 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java
@@ -25,6 +25,7 @@
 import com.android.sdklib.repository.RepoConstants;

 import com.android.sdklib.util.GrabProcessOutput;

 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;

+import com.android.sdklib.util.GrabProcessOutput.Wait;

 

 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;

 import org.apache.commons.compress.archivers.zip.ZipFile;

@@ -598,7 +599,7 @@
                 Process process = Runtime.getRuntime().exec(command);

                 int retCode = GrabProcessOutput.grabProcessOutput(

                         process,

-                        true /*waitForReaders*/,

+                        Wait.WAIT_FOR_READERS,

                         new IProcessOutput() {

                             @Override

                             public void out(String line) {

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
index ffd561f..3ddacb4 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java
@@ -25,6 +25,7 @@
 import com.android.sdklib.repository.SdkRepoConstants;

 import com.android.sdklib.util.GrabProcessOutput;

 import com.android.sdklib.util.GrabProcessOutput.IProcessOutput;

+import com.android.sdklib.util.GrabProcessOutput.Wait;

 

 import org.w3c.dom.Node;

 

@@ -276,7 +277,7 @@
             final String tag = scriptName;

             status = GrabProcessOutput.grabProcessOutput(

                     proc,

-                    false /*waitForReaders*/,

+                    Wait.WAIT_FOR_PROCESS,

                     new IProcessOutput() {

                         @Override

                         public void out(String line) {

diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
index b470153..2935493 100755
--- a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
+++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java
@@ -25,6 +25,35 @@
 
 public class GrabProcessOutput {
 
+    public enum Wait {
+        /**
+         * Doesn't wait for the exec to complete.
+         * This still monitors the output but does not wait for the process to finish.
+         * In this mode the process return code is unknown and always 0.
+         */
+        ASYNC,
+        /**
+         * This waits for the process to finish.
+         * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
+         * error code from the process.
+         * In some rare cases and depending on the OS, the process might not have
+         * finished dumping data into stdout/stderr.
+         * <p/>
+         * Use this when you don't particularly care for the output but instead
+         * care for the return code of the executed process.
+         */
+        WAIT_FOR_PROCESS,
+        /**
+         * This waits for the process to finish <em>and</em> for the stdout/stderr
+         * threads to complete.
+         * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the
+         * error code from the process.
+         * <p/>
+         * Use this one when capturing all the output from the process is important.
+         */
+        WAIT_FOR_READERS,
+    }
+
     public interface IProcessOutput {
         /**
          * Processes an stdout message line.
@@ -46,13 +75,13 @@
      * @param output Optional object to capture stdout/stderr.
      *      Note that on Windows capturing the output is not optional. If output is null
      *      the stdout/stderr will be captured and discarded.
-     * @param waitForReaders True to wait for the reader threads to finish.
+     * @param waitMode Whether to wait for the process and/or the readers to finish.
      * @return the process return code.
      * @throws InterruptedException if {@link Process#waitFor()} was interrupted.
      */
     public static int grabProcessOutput(
             @NonNull final Process process,
-            boolean waitForReaders,
+            Wait waitMode,
             @Nullable final IProcessOutput output) throws InterruptedException {
         // read the lines as they come. if null is returned, it's
         // because the process finished
@@ -104,10 +133,14 @@
         threadErr.start();
         threadOut.start();
 
+        if (waitMode == Wait.ASYNC) {
+            return 0;
+        }
+
         // it looks like on windows process#waitFor() can return
         // before the thread have filled the arrays, so we wait for both threads and the
         // process itself.
-        if (waitForReaders) {
+        if (waitMode == Wait.WAIT_FOR_READERS) {
             try {
                 threadErr.join();
             } catch (InterruptedException e) {