FileDocCategorySizeDatePackage
AndroidLaunchController.javaAPI DocAndroid 1.5 API74623Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.adt.launch

AndroidLaunchController

public final class AndroidLaunchController extends Object implements com.android.ddmlib.AndroidDebugBridge.IClientChangeListener, com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener, com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener, ILaunchController
Controls the launch of Android application either on a device or on the emulator. If an emulator is already running, this class will attempt to reuse it.

Fields Summary
private static final String
FLAG_AVD
private static final String
FLAG_NETDELAY
private static final String
FLAG_NETSPEED
private static final String
FLAG_WIPE_DATA
private static final String
FLAG_NO_BOOT_ANIM
private static final HashMap
sRunningAppMap
Map to store {@link ILaunchConfiguration} objects that must be launched as simple connection to running application. The integer is the port on which to connect. ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!
private static final Object
sListLock
private final ArrayList
mWaitingForEmulatorLaunches
List of {@link DelayedLaunchInfo} waiting for an emulator to connect.

Once an emulator has connected, {@link DelayedLaunchInfo#getDevice()} is set and the DelayedLaunchInfo object is moved to {@link AndroidLaunchController#mWaitingForReadyEmulatorList}. ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!

private final ArrayList
mWaitingForReadyEmulatorList
List of application waiting to be launched on a device/emulator.
ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!
private final ArrayList
mWaitingForDebuggerApplications
Application waiting to show up as waiting for debugger. ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!
private final ArrayList
mUnknownClientsWaitingForDebugger
List of clients that have appeared as waiting for debugger before their name was available. ALL ACCESS MUST BE INSIDE A synchronized (sListLock) block!
private static AndroidLaunchController
sThis
static instance for singleton
Constructors Summary
private AndroidLaunchController()
private constructor to enforce singleton

        AndroidDebugBridge.addDebugBridgeChangeListener(this);
        AndroidDebugBridge.addDeviceChangeListener(this);
        AndroidDebugBridge.addClientChangeListener(this);
    
Methods Summary
public voidbridgeChanged(com.android.ddmlib.AndroidDebugBridge bridge)
Sent when a new {@link AndroidDebugBridge} is started.

This is sent from a non UI thread.

param
bridge the new {@link AndroidDebugBridge} object.
see
IDebugBridgeChangeListener#bridgeChanged(AndroidDebugBridge)

        // The adb server has changed. We cancel any pending launches.
        String message = "adb server change: cancelling '%1$s'!";
        synchronized (sListLock) {
            for (DelayedLaunchInfo launchInfo : mWaitingForReadyEmulatorList) {
                AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                    String.format(message, launchInfo.getLaunchAction().getLaunchDescription()));
                stopLaunch(launchInfo);
            }
            for (DelayedLaunchInfo launchInfo : mWaitingForDebuggerApplications) {
                AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                        String.format(message, 
                                launchInfo.getLaunchAction().getLaunchDescription()));
                stopLaunch(launchInfo);
            }

            mWaitingForReadyEmulatorList.clear();
            mWaitingForDebuggerApplications.clear();
        }
    
private booleancheckBuildInfo(DelayedLaunchInfo launchInfo, com.android.ddmlib.IDevice device)
Checks the build information, and returns whether the launch should continue.

The value tested are:

  • Minimum API version requested by the application. If the target device does not match, the launch is canceled.
  • Debuggable attribute of the application and whether or not the device requires it. If the device requires it and it is not set in the manifest, the launch will be forced to "release" mode instead of "debug"
    •         if (device != null) {
                  // check the app required API level versus the target device API level
                  
                  String deviceApiVersionName = device.getProperty(IDevice.PROP_BUILD_VERSION);
                  String value = device.getProperty(IDevice.PROP_BUILD_VERSION_NUMBER);
                  int deviceApiVersionNumber = AndroidManifestParser.INVALID_MIN_SDK;
                  try {
                      deviceApiVersionNumber = Integer.parseInt(value);
                  } catch (NumberFormatException e) {
                      // pass, we'll keep the deviceVersionNumber value at 0.
                  }
                  
                  if (launchInfo.getRequiredApiVersionNumber() == AndroidManifestParser.INVALID_MIN_SDK) {
                      // warn the API level requirement is not set.
                      AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                              "WARNING: Application does not specify an API level requirement!");
      
                      // and display the target device API level (if known)
                      if (deviceApiVersionName == null ||
                              deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) {
                          AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                                  "WARNING: Unknown device API version!");
                      } else {
                          AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format(
                                  "Device API version is %1$d (Android %2$s)", deviceApiVersionNumber,
                                  deviceApiVersionName));
                      }
                  } else { // app requires a specific API level
                      if (deviceApiVersionName == null ||
                              deviceApiVersionNumber == AndroidManifestParser.INVALID_MIN_SDK) {
                          AdtPlugin.printToConsole(launchInfo.getProject(),
                                  "WARNING: Unknown device API version!");
                      } else if (deviceApiVersionNumber < launchInfo.getRequiredApiVersionNumber()) {
                          String msg = String.format(
                                  "ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).",
                                  launchInfo.getRequiredApiVersionNumber(), deviceApiVersionNumber,
                                  deviceApiVersionName);
                          AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
                          
                          // abort the launch
                          return false;
                      }
                  }
      
                  // now checks that the device/app can be debugged (if needed)
                  if (device.isEmulator() == false && launchInfo.isDebugMode()) {
                      String debuggableDevice = device.getProperty(IDevice.PROP_DEBUGGABLE);
                      if (debuggableDevice != null && debuggableDevice.equals("0")) { //$NON-NLS-1$
                          // the device is "secure" and requires apps to declare themselves as debuggable!
                          if (launchInfo.getDebuggable() == null) {
                              String message1 = String.format(
                                      "Device '%1$s' requires that applications explicitely declare themselves as debuggable in their manifest.",
                                      device.getSerialNumber());
                              String message2 = String.format("Application '%1$s' does not have the attribute 'debuggable' set to TRUE in its manifest and cannot be debugged.",
                                      launchInfo.getPackageName());
                              AdtPlugin.printErrorToConsole(launchInfo.getProject(), message1, message2);
                              
                              // because am -D does not check for ro.debuggable and the
                              // 'debuggable' attribute, it is important we do not use the -D option
                              // in this case or the app will wait for a debugger forever and never
                              // really launch.
                              launchInfo.setDebugMode(false);
                          } else if (launchInfo.getDebuggable() == Boolean.FALSE) {
                              String message = String.format("Application '%1$s' has its 'debuggable' attribute set to FALSE and cannot be debugged.",
                                      launchInfo.getPackageName());
                              AdtPlugin.printErrorToConsole(launchInfo.getProject(), message);
      
                              // because am -D does not check for ro.debuggable and the
                              // 'debuggable' attribute, it is important we do not use the -D option
                              // in this case or the app will wait for a debugger forever and never
                              // really launch.
                              launchInfo.setDebugMode(false);
                          }
                      }
                  }
              }
              
              return true;
          
private booleancheckInstallResult(java.lang.String result, com.android.ddmlib.IDevice device, DelayedLaunchInfo launchInfo, java.lang.String remotePath, com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode retryMode)
Checks the result of an installation, and takes optional actions based on it.

param
result the result string from the installation
param
device the device on which the installation occured.
param
launchInfo the {@link DelayedLaunchInfo}
param
remotePath the temporary path of the package on the device
param
retryMode indicates what to do in case, a package already exists.
return
true if success, false otherwise.
throws
IOException

        if (result == null) {
            AdtPlugin.printToConsole(launchInfo.getProject(), "Success!");
            return true;
        } else if (result.equals("INSTALL_FAILED_ALREADY_EXISTS")) { //$NON-NLS-1$
            if (retryMode == InstallRetryMode.PROMPT) {
                boolean prompt = AdtPlugin.displayPrompt("Application Install",
                        "A previous installation needs to be uninstalled before the new package can be installed.\nDo you want to uninstall?");
                if (prompt) {
                    retryMode = InstallRetryMode.ALWAYS;
                } else {
                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                        "Installation error! The package already exists.");
                    return false;
                }
            }

            if (retryMode == InstallRetryMode.ALWAYS) {
                /*
                 * TODO: create a UI that gives the dev the choice to:
                 * - clean uninstall on launch
                 * - full uninstall if application exists.
                 * - soft uninstall if application exists (keeps the app data around).
                 * - always ask (choice of soft-reinstall, full reinstall)
                AdtPlugin.printErrorToConsole(launchInfo.mProject,
                        "Application already exists, uninstalling...");
                String res = doUninstall(device, launchInfo);
                if (res == null) {
                    AdtPlugin.printToConsole(launchInfo.mProject, "Success!");
                } else {
                    AdtPlugin.printErrorToConsole(launchInfo.mProject,
                            String.format("Failed to uninstall: %1$s", res));
                    return false;
                }
                */

                AdtPlugin.printToConsole(launchInfo.getProject(),
                        "Application already exists. Attempting to re-install instead...");
                String res = doInstall(launchInfo, remotePath, device, true /* reinstall */);
                return checkInstallResult(res, device, launchInfo, remotePath,
                        InstallRetryMode.NEVER);
            }

            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                    "Installation error! The package already exists.");
        } else if (result.equals("INSTALL_FAILED_INVALID_APK")) { //$NON-NLS-1$
            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                "Installation failed due to invalid APK file!",
                "Please check logcat output for more details.");
        } else if (result.equals("INSTALL_FAILED_INVALID_URI")) { //$NON-NLS-1$
            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                "Installation failed due to invalid URI!",
                "Please check logcat output for more details.");
        } else if (result.equals("INSTALL_FAILED_COULDNT_COPY")) { //$NON-NLS-1$
            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                String.format("Installation failed: Could not copy %1$s to its final location!",
                        launchInfo.getPackageFile().getName()),
                "Please check logcat output for more details.");
        } else if (result.equals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES")) {
            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                    "Re-installation failed due to different application signatures.",
                    "You must perform a full uninstall of the application. WARNING: This will remove the application data!",
                    String.format("Please execute 'adb uninstall %1$s' in a shell.", launchInfo.getPackageName()));
        } else {
            AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                String.format("Installation error: %1$s", result),
                "Please check logcat output for more details.");
        }

        return false;
    
public voidclientChanged(com.android.ddmlib.Client client, int changeMask)
Sent when an existing client information changed.

This is sent from a non UI thread.

param
client the updated client.
param
changeMask the bit mask describing the changed properties. It can contain any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
see
IClientChangeListener#clientChanged(Client, int)

        boolean connectDebugger = false;
        if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME) {
            String applicationName = client.getClientData().getClientDescription();
            if (applicationName != null) {
                IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
                String home = store.getString(AdtPlugin.PREFS_HOME_PACKAGE);
                
                if (home.equals(applicationName)) {
                    
                    // looks like home is up, get its device
                    IDevice device = client.getDevice();
                    
                    // look for application waiting for home
                    synchronized (sListLock) {
                        for (int i = 0; i < mWaitingForReadyEmulatorList.size(); ) {
                            DelayedLaunchInfo launchInfo = mWaitingForReadyEmulatorList.get(i);
                            if (launchInfo.getDevice() == device) {
                                // it's match, remove from the list
                                mWaitingForReadyEmulatorList.remove(i);
                                
                                // We couldn't check earlier the API level of the device
                                // (it's asynchronous when the device boot, and usually
                                // deviceConnected is called before it's queried for its build info)
                                // so we check now
                                if (checkBuildInfo(launchInfo, device) == false) {
                                    // device is not the proper API!
                                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                                            "Launch canceled!");
                                    stopLaunch(launchInfo);
                                    return;
                                }
        
                                AdtPlugin.printToConsole(launchInfo.getProject(),
                                        String.format("HOME is up on device '%1$s'",
                                                device.getSerialNumber()));
                                
                                // attempt to sync the new package onto the device.
                                if (syncApp(launchInfo, device)) {
                                    // application package is sync'ed, lets attempt to launch it.
                                    launchApp(launchInfo, device);
                                } else {
                                    // failure! Cancel and return
                                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                                    "Launch canceled!");
                                    stopLaunch(launchInfo);
                                }
                                
                                break;
                            } else {
                                i++;
                            }
                        }
                    }
                }
    
                // check if it's already waiting for a debugger, and if so we connect to it.
                if (client.getClientData().getDebuggerConnectionStatus() == ClientData.DEBUGGER_WAITING) {
                    // search for this client in the list;
                    synchronized (sListLock) {
                        int index = mUnknownClientsWaitingForDebugger.indexOf(client);
                        if (index != -1) {
                            connectDebugger = true;
                            mUnknownClientsWaitingForDebugger.remove(client);
                        }
                    }
                }
            }
        }
        
        // if it's not home, it could be an app that is now in debugger mode that we're waiting for
        // lets check it

        if ((changeMask & Client.CHANGE_DEBUGGER_INTEREST) == Client.CHANGE_DEBUGGER_INTEREST) {
            ClientData clientData = client.getClientData();
            String applicationName = client.getClientData().getClientDescription();
            if (clientData.getDebuggerConnectionStatus() == ClientData.DEBUGGER_WAITING) {
                // Get the application name, and make sure its valid.
                if (applicationName == null) {
                    // looks like we don't have the client yet, so we keep it around for when its
                    // name becomes available.
                    synchronized (sListLock) {
                        mUnknownClientsWaitingForDebugger.add(client);
                    }
                    return;
                } else {
                    connectDebugger = true;
                }
            }
        }

        if (connectDebugger) {
            Log.d("adt", "Debugging " + client);
            // now check it against the apps waiting for a debugger
            String applicationName = client.getClientData().getClientDescription();
            Log.d("adt", "App Name: " + applicationName);
            synchronized (sListLock) {
                for (int i = 0; i < mWaitingForDebuggerApplications.size(); ) {
                    final DelayedLaunchInfo launchInfo = mWaitingForDebuggerApplications.get(i);
                    if (client.getDevice() == launchInfo.getDevice() &&
                            applicationName.equals(launchInfo.getDebugPackageName())) {
                        // this is a match. We remove the launch info from the list
                        mWaitingForDebuggerApplications.remove(i);
                        
                        // and connect the debugger.
                        String msg = String.format(
                                "Attempting to connect debugger to '%1$s' on port %2$d",
                                launchInfo.getDebugPackageName(), client.getDebuggerListenPort());
                        AdtPlugin.printToConsole(launchInfo.getProject(), msg);
                        
                        new Thread("Debugger Connection") { //$NON-NLS-1$
                            @Override
                            public void run() {
                                try {
                                    if (connectRemoteDebugger(
                                            client.getDebuggerListenPort(),
                                            launchInfo.getLaunch(), 
                                            launchInfo.getMonitor()) == false) {
                                        return;
                                    }
                                } catch (CoreException e) {
                                    // well something went wrong.
                                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                                            String.format("Launch error: %s", e.getMessage()));
                                    // stop the launch
                                    stopLaunch(launchInfo);
                                }

                                launchInfo.getMonitor().done();
                            }
                        }.start();
                        
                        // we're done processing this client.
                        return;

                    } else {
                        i++;
                    }
                }
            }
            
            // if we get here, we haven't found an app that we were launching, so we look
            // for opened android projects that contains the app asking for a debugger.
            // If we find one, we automatically connect to it.
            IProject project = ProjectHelper.findAndroidProjectByAppName(applicationName);
            
            if (project != null) {
                debugRunningApp(project, client.getDebuggerListenPort());
            }
        }
    
public static booleanconnectRemoteDebugger(int debugPort, AndroidLaunch launch, org.eclipse.core.runtime.IProgressMonitor monitor)
Connects a remote debugger on the specified port.

param
debugPort The port to connect the debugger to
param
launch The associated AndroidLaunch object.
param
monitor A Progress monitor
return
false if cancelled by the monitor
throws
CoreException

        // get some default parameters.
        int connectTimeout = JavaRuntime.getPreferences().getInt(JavaRuntime.PREF_CONNECT_TIMEOUT);

        HashMap<String, String> newMap = new HashMap<String, String>();

        newMap.put("hostname", "localhost");  //$NON-NLS-1$ //$NON-NLS-2$

        newMap.put("port", Integer.toString(debugPort)); //$NON-NLS-1$

        newMap.put("timeout", Integer.toString(connectTimeout));

        // get the default VM connector
        IVMConnector connector = JavaRuntime.getDefaultVMConnector();

        // connect to remote VM
        connector.connect(newMap, monitor, launch);

        // check for cancellation
        if (monitor.isCanceled()) {
            IDebugTarget[] debugTargets = launch.getDebugTargets();
            for (IDebugTarget target : debugTargets) {
                if (target.canDisconnect()) {
                    target.disconnect();
                }
            }
            return false;
        }

        return true;
    
voidcontinueLaunch(com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse response, org.eclipse.core.resources.IProject project, AndroidLaunch launch, DelayedLaunchInfo launchInfo, AndroidLaunchConfiguration config)
Continues the launch based on the DeviceChooser response.

param
response the device chooser response
param
project The project being launched
param
launch The eclipse launch info
param
launchInfo The {@link DelayedLaunchInfo}
param
config The config needed to start a new emulator.


        // Since this is called from the UI thread we spawn a new thread
        // to finish the launch.
        new Thread() {
            @Override
            public void run() {
                if (response.getAvdToLaunch() != null) {
                    // there was no selected device, we start a new emulator.
                    synchronized (sListLock) {
                        AvdInfo info = response.getAvdToLaunch();
                        mWaitingForEmulatorLaunches.add(launchInfo);
                        AdtPlugin.printToConsole(project, String.format(
                                "Launching a new emulator with Virtual Device '%1$s'",
                                info.getName()));
                        boolean status = launchEmulator(config, info);
            
                        if (status == false) {
                            // launching the emulator failed!
                            AdtPlugin.displayError("Emulator Launch",
                                    "Couldn't launch the emulator! Make sure the SDK directory is properly setup and the emulator is not missing.");
            
                            // stop the launch and return
                            mWaitingForEmulatorLaunches.remove(launchInfo);
                            AdtPlugin.printErrorToConsole(project, "Launch canceled!");
                            stopLaunch(launchInfo);
                            return;
                        }
                        
                        return;
                    }
                } else if (response.getDeviceToUse() != null) {
                    launchInfo.setDevice(response.getDeviceToUse());
                    simpleLaunch(launchInfo, launchInfo.getDevice());
                }
            }
        }.start();
    
public static voiddebugRunningApp(org.eclipse.core.resources.IProject project, int debugPort)
Launches a remote java debugging session on an already running application

param
project The project of the application to debug.
param
debugPort The port to connect the debugger to.

        // get an existing or new launch configuration
        ILaunchConfiguration config = AndroidLaunchController.getLaunchConfig(project);
        
        if (config != null) {
            setPortLaunchConfigAssociation(config, debugPort);
            
            // and launch
            DebugUITools.launch(config, ILaunchManager.DEBUG_MODE);
        }
    
public voiddeviceChanged(com.android.ddmlib.Device device, int changeMask)
Sent when a device data changed, or when clients are started/terminated on the device.

This is sent from a non UI thread.

param
device the device that was updated.
param
changeMask the mask indicating what changed.
see
IDeviceChangeListener#deviceChanged(Device, int)

        // We could check if any starting device we care about is now ready, but we can wait for
        // its home app to show up, so...
    
public voiddeviceConnected(com.android.ddmlib.Device device)
Sent when the a device is connected to the {@link AndroidDebugBridge}.

This is sent from a non UI thread.

param
device the new device.
see
IDeviceChangeListener#deviceConnected(Device)

        synchronized (sListLock) {
            // look if there's an app waiting for a device
            if (mWaitingForEmulatorLaunches.size() > 0) {
                // get/remove first launch item from the list
                // FIXME: what if we have multiple launches waiting?
                DelayedLaunchInfo launchInfo = mWaitingForEmulatorLaunches.get(0);
                mWaitingForEmulatorLaunches.remove(0);

                // give the launch item its device for later use.
                launchInfo.setDevice(device);

                // and move it to the other list
                mWaitingForReadyEmulatorList.add(launchInfo);
                
                // and tell the user about it
                AdtPlugin.printToConsole(launchInfo.getProject(),
                        String.format("New emulator found: %1$s", device.getSerialNumber()));
                AdtPlugin.printToConsole(launchInfo.getProject(),
                        String.format("Waiting for HOME ('%1$s') to be launched...",
                            AdtPlugin.getDefault().getPreferenceStore().getString(
                                    AdtPlugin.PREFS_HOME_PACKAGE)));
            }
        }
    
public voiddeviceDisconnected(com.android.ddmlib.Device device)
Sent when the a device is connected to the {@link AndroidDebugBridge}.

This is sent from a non UI thread.

param
device the new device.
see
IDeviceChangeListener#deviceDisconnected(Device)

        // any pending launch on this device must be canceled.
        String message = "%1$s disconnected! Cancelling '%2$s'!";
        synchronized (sListLock) {
            ArrayList<DelayedLaunchInfo> copyList =
                (ArrayList<DelayedLaunchInfo>) mWaitingForReadyEmulatorList.clone();
            for (DelayedLaunchInfo launchInfo : copyList) {
                if (launchInfo.getDevice() == device) {
                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                            String.format(message, device.getSerialNumber(), 
                                    launchInfo.getLaunchAction().getLaunchDescription()));
                    stopLaunch(launchInfo);
                }
            }
            copyList = (ArrayList<DelayedLaunchInfo>) mWaitingForDebuggerApplications.clone();
            for (DelayedLaunchInfo launchInfo : copyList) {
                if (launchInfo.getDevice() == device) {
                    AdtPlugin.printErrorToConsole(launchInfo.getProject(),
                            String.format(message, device.getSerialNumber(), 
                                    launchInfo.getLaunchAction().getLaunchDescription()));
                    stopLaunch(launchInfo);
                }
            }
        }
    
private java.lang.StringdoInstall(DelayedLaunchInfo launchInfo, java.lang.String remotePath, com.android.ddmlib.IDevice device, boolean reinstall)
Performs the installation of an application whose package has been uploaded on the device.

param
launchInfo the {@link DelayedLaunchInfo}.
param
remotePath the path of the application package in the device tmp folder.
param
device the device on which to install the application.
param
reinstall
return
a {@link String} with an error code, or null if success.
throws
IOException

        InstallReceiver receiver = new InstallReceiver();
        try {
            String cmd = String.format(
                    reinstall ? "pm install -r \"%1$s\"" : "pm install \"%1$s\"", //$NON-NLS-1$ //$NON-NLS-2$
                    remotePath); //$NON-NLS-1$ //$NON-NLS-2$
            device.executeShellCommand(cmd, receiver);
        } catch (IOException e) {
            String msg = String.format(
                    "Failed to install %1$s on device '%2$s': %3$s",
                    launchInfo.getPackageFile().getName(), device.getSerialNumber(), 
                    e.getMessage());
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
            throw e;
        }
        
        return receiver.getSuccess();
    
private booleandoSyncApp(DelayedLaunchInfo launchInfo, com.android.ddmlib.IDevice device)
Syncs the application on the device/emulator.

param
launchInfo The Launch information object.
param
device the device on which to sync the application
return
true if the install succeeded.

        SyncService sync = device.getSyncService();
        if (sync != null) {
            IPath path = launchInfo.getPackageFile().getLocation();
            String message = String.format("Uploading %1$s onto device '%2$s'",
                    path.lastSegment(), device.getSerialNumber());
            AdtPlugin.printToConsole(launchInfo.getProject(), message);

            String osLocalPath = path.toOSString();
            String apkName = launchInfo.getPackageFile().getName();
            String remotePath = "/data/local/tmp/" + apkName; //$NON-NLS-1$

            SyncResult result = sync.pushFile(osLocalPath, remotePath,
                    SyncService.getNullProgressMonitor());

            if (result.getCode() != SyncService.RESULT_OK) {
                String msg = String.format("Failed to upload %1$s on '%2$s': %3$s",
                        apkName, device.getSerialNumber(), result.getMessage());
                AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
                return false;
            }

            // Now that the package is uploaded, we can install it properly.
            // This will check that there isn't another apk declaring the same package, or
            // that another install used a different key.
            boolean installResult =  installPackage(launchInfo, remotePath, device);
            
            // now we delete the app we sync'ed
            try {
                device.executeShellCommand("rm " + remotePath, new MultiLineReceiver() { //$NON-NLS-1$
                    @Override
                    public void processNewLines(String[] lines) {
                        // pass
                    }
                    public boolean isCancelled() {
                        return false;
                    }
                });
            } catch (IOException e) {
                AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format(
                        "Failed to delete temporary package: %1$s", e.getMessage()));
                return false;
            }
            
            // if the installation succeeded, we register it.
            if (installResult) {
                ApkInstallManager.getInstance().registerInstallation(
                        launchInfo.getProject(), device);
            }
            
            return installResult;
        }

        String msg = String.format(
                "Failed to upload %1$s on device '%2$s': Unable to open sync connection!",
                launchInfo.getPackageFile().getName(), device.getSerialNumber());
        AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);

        return false;
    
private java.lang.StringdoUninstall(com.android.ddmlib.IDevice device, DelayedLaunchInfo launchInfo)
Performs the uninstallation of an application.

param
device the device on which to install the application.
param
launchInfo the {@link DelayedLaunchInfo}.
return
a {@link String} with an error code, or null if success.
throws
IOException

        InstallReceiver receiver = new InstallReceiver();
        try {
            device.executeShellCommand("pm uninstall " + launchInfo.getPackageName(), //$NON-NLS-1$
                    receiver);
        } catch (IOException e) {
            String msg = String.format(
                    "Failed to uninstall %1$s: %2$s", launchInfo.getPackageName(), e.getMessage());
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg);
            throw e;
        }
        
        return receiver.getSuccess();
    
private static org.eclipse.debug.core.ILaunchConfigurationfindConfig(org.eclipse.debug.core.ILaunchManager manager, org.eclipse.debug.core.ILaunchConfigurationType type, java.lang.String projectName)
Looks for and returns an existing {@link ILaunchConfiguration} object for a specified project.

param
manager The {@link ILaunchManager}.
param
type The {@link ILaunchConfigurationType}.
param
projectName The name of the project
return
an existing ILaunchConfiguration object matching the project, or null.

        try {
            ILaunchConfiguration[] configs = manager.getLaunchConfigurations(type);

            for (ILaunchConfiguration config : configs) {
                if (config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME,
                        "").equals(projectName)) {  //$NON-NLS-1$
                    return config;
                }
            }
        } catch (CoreException e) {
            MessageDialog.openError(AdtPlugin.getDisplay().getActiveShell(),
                    "Launch Error", e.getStatus().getMessage());
        }

        // didn't find anything that matches. Return null
        return null;

    
public java.util.ListgetDependenciesLaunchInfo(DelayedLaunchInfo launchInfo)
For the current launchInfo, create additional DelayedLaunchInfo that should be used to sync APKs that we are dependent on to the device.

param
launchInfo the original launch info that we want to find the
return
a list of DelayedLaunchInfo (may be empty if no dependencies were found or error)

        List<DelayedLaunchInfo> dependencies = new ArrayList<DelayedLaunchInfo>();

        // Convert to equivalent JavaProject
        IJavaProject javaProject;
        try {
            //assuming this is an Android (and Java) project since it is attached to the launchInfo.
            javaProject = BaseProjectHelper.getJavaProject(launchInfo.getProject());
        } catch (CoreException e) {
            // return empty dependencies
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), e);
            return dependencies;
        }
        
        // Get all projects that this depends on
        List<IJavaProject> androidProjectList;
        try {
            androidProjectList = ProjectHelper.getAndroidProjectDependencies(javaProject);
        } catch (JavaModelException e) {
            // return empty dependencies
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), e);
            return dependencies;
        }
        
        // for each project, parse manifest and create launch information
        for (IJavaProject androidProject : androidProjectList) {
            // Parse the Manifest to get various required information
            // copied from LaunchConfigDelegate
            AndroidManifestParser manifestParser;
            try {
                manifestParser = AndroidManifestParser.parse(
                        androidProject, null /* errorListener */,
                        true /* gatherData */, false /* markErrors */);
            } catch (CoreException e) {
                AdtPlugin.printErrorToConsole(
                        launchInfo.getProject(), 
                        String.format("Error parsing manifest of %s", 
                                androidProject.getElementName()));
                continue;
            }
            
            // Get the APK location (can return null)
            IFile apk = ProjectHelper.getApplicationPackage(androidProject.getProject());
            if (apk == null) {
                // getApplicationPackage will have logged an error message
                continue;      
            }
            
            // Create new launchInfo as an hybrid between parent and dependency information
            DelayedLaunchInfo delayedLaunchInfo = new DelayedLaunchInfo(
                    androidProject.getProject(), 
                    manifestParser.getPackage(),
                    manifestParser.getPackage(),
                    launchInfo.getLaunchAction(), 
                    apk, 
                    manifestParser.getDebuggable(), 
                    manifestParser.getApiLevelRequirement(), 
                    launchInfo.getLaunch(), 
                    launchInfo.getMonitor());
            
            // Add to the list
            dependencies.add(delayedLaunchInfo);
        }
        
        return dependencies;
    
public static com.android.ide.eclipse.adt.launch.AndroidLaunchControllergetInstance()
Returns the singleton reference.

        return sThis;
    
public static org.eclipse.debug.core.ILaunchConfigurationgetLaunchConfig(org.eclipse.core.resources.IProject project)
Returns an {@link ILaunchConfiguration} for the specified {@link IProject}.

param
project the project
return
a new or already existing ILaunchConfiguration or null if there was an error when creating a new one.

        // get the launch manager
        ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();

        // now get the config type for our particular android type.
        ILaunchConfigurationType configType = manager.getLaunchConfigurationType(
                        LaunchConfigDelegate.ANDROID_LAUNCH_TYPE_ID);

        String name = project.getName();

        // search for an existing launch configuration
        ILaunchConfiguration config = findConfig(manager, configType, name);

        // test if we found one or not
        if (config == null) {
            // Didn't find a matching config, so we make one.
            // It'll be made in the "working copy" object first.
            ILaunchConfigurationWorkingCopy wc = null;

            try {
                // make the working copy object
                wc = configType.newInstance(null,
                        manager.generateUniqueLaunchConfigurationNameFrom(name));

                // set the project name
                wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, name);

                // set the launch mode to default.
                wc.setAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION,
                        LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION);

                // set default target mode
                wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
                        LaunchConfigDelegate.DEFAULT_TARGET_MODE.getValue());

                // default AVD: None
                wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String) null);

                // set the default network speed
                wc.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
                        LaunchConfigDelegate.DEFAULT_SPEED);

                // and delay
                wc.setAttribute(LaunchConfigDelegate.ATTR_DELAY,
                        LaunchConfigDelegate.DEFAULT_DELAY);
                
                // default wipe data mode
                wc.setAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA,
                        LaunchConfigDelegate.DEFAULT_WIPE_DATA);
                
                // default disable boot animation option
                wc.setAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM,
                        LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM);
                
                // set default emulator options
                IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
                String emuOptions = store.getString(AdtPlugin.PREFS_EMU_OPTIONS);
                wc.setAttribute(LaunchConfigDelegate.ATTR_COMMANDLINE, emuOptions);
                
                // map the config and the project
                wc.setMappedResources(getResourcesToMap(project));

                // save the working copy to get the launch config object which we return.
                return wc.doSave();

            } catch (CoreException e) {
                String msg = String.format(
                        "Failed to create a Launch config for project '%1$s': %2$s",
                        project.getName(), e.getMessage());
                AdtPlugin.printErrorToConsole(project, msg);

                // no launch!
                return null;
            }
        }
        
        return config;
    
static intgetPortForConfig(org.eclipse.debug.core.ILaunchConfiguration launchConfig)
Queries for a debugger port for a specific {@link ILaunchConfiguration}.

If the configuration and a debugger port where added through {@link #setPortLaunchConfigAssociation(ILaunchConfiguration, int)}, then this method will return the debugger port, and remove the configuration from the list.

param
launchConfig the {@link ILaunchConfiguration}
return
the debugger port or {@link LaunchConfigDelegate#INVALID_DEBUG_PORT} if the configuration was not setup.

        synchronized (sListLock) {
            Integer port = sRunningAppMap.get(launchConfig);
            if (port != null) {
                sRunningAppMap.remove(launchConfig);
                return port;
            }
        }
        
        return LaunchConfigDelegate.INVALID_DEBUG_PORT;
    
public static org.eclipse.core.resources.IResource[]getResourcesToMap(org.eclipse.core.resources.IProject project)
Returns the list of resources to map to a Launch Configuration.

param
project the project associated to the launch configuration.

        ArrayList<IResource> array = new ArrayList<IResource>(2);
        array.add(project);
        
        IFile manifest = AndroidManifestParser.getManifest(project);
        if (manifest != null) {
            array.add(manifest);
        }
        
        return array.toArray(new IResource[array.size()]);
    
private voidgrabEmulatorOutput(java.lang.Process process)
Get the stderr/stdout outputs of a process and return when the process is done. Both must be read or the process will block on windows.

param
process The process to get the ouput from

        // read the lines as they come. if null is returned, it's
        // because the process finished
        new Thread("") { //$NON-NLS-1$
            @Override
            public void run() {
                // create a buffer to read the stderr output
                InputStreamReader is = new InputStreamReader(process.getErrorStream());
                BufferedReader errReader = new BufferedReader(is);

                try {
                    while (true) {
                        String line = errReader.readLine();
                        if (line != null) {
                            AdtPlugin.printErrorToConsole("Emulator", line);
                        } else {
                            break;
                        }
                    }
                } catch (IOException e) {
                    // do nothing.
                }
            }
        }.start();

        new Thread("") { //$NON-NLS-1$
            @Override
            public void run() {
                InputStreamReader is = new InputStreamReader(process.getInputStream());
                BufferedReader outReader = new BufferedReader(is);

                try {
                    while (true) {
                        String line = outReader.readLine();
                        if (line != null) {
                            AdtPlugin.printToConsole("Emulator", line);
                        } else {
                            break;
                        }
                    }
                } catch (IOException e) {
                    // do nothing.
                }
            }
        }.start();
    
private booleaninstallPackage(DelayedLaunchInfo launchInfo, java.lang.String remotePath, com.android.ddmlib.IDevice device)
Installs the application package that was pushed to a temporary location on the device.

param
launchInfo The launch information
param
remotePath The remote path of the package.
param
device The device on which the launch is done.


        String message = String.format("Installing %1$s...", launchInfo.getPackageFile().getName());
        AdtPlugin.printToConsole(launchInfo.getProject(), message);

        try {
            String result = doInstall(launchInfo, remotePath, device, false /* reinstall */);
            
            /* For now we force to retry the install (after uninstalling) because there's no
             * other way around it: adb install does not want to update a package w/o uninstalling
             * the old one first!
             */
            return checkInstallResult(result, device, launchInfo, remotePath,
                    InstallRetryMode.ALWAYS);
        } catch (IOException e) {
            // do nothing, we'll return false
        }
        
        return false;
    
public voidlaunch(org.eclipse.core.resources.IProject project, java.lang.String mode, org.eclipse.core.resources.IFile apk, java.lang.String packageName, java.lang.String debugPackageName, java.lang.Boolean debuggable, int requiredApiVersionNumber, IAndroidLaunchAction launchAction, AndroidLaunchConfiguration config, AndroidLaunch launch, org.eclipse.core.runtime.IProgressMonitor monitor)
Launches an android app on the device or emulator

param
project The project we're launching
param
mode the mode in which to launch, one of the mode constants defined by ILaunchManager - RUN_MODE or DEBUG_MODE.
param
apk the resource to the apk to launch.
param
packageName the Android package name of the app
param
debugPackageName the Android package name to debug
param
debuggable the debuggable value of the app, or null if not set.
param
requiredApiVersionNumber the api version required by the app, or {@link AndroidManifestParser#INVALID_MIN_SDK} if none.
param
launchAction the action to perform after app sync
param
config the launch configuration
param
launch the launch object

        
        String message = String.format("Performing %1$s", launchAction.getLaunchDescription());
        AdtPlugin.printToConsole(project, message);

        // create the launch info
        final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName,
                debugPackageName, launchAction, apk, debuggable, requiredApiVersionNumber, launch,
                monitor);

        // set the debug mode
        launchInfo.setDebugMode(mode.equals(ILaunchManager.DEBUG_MODE));

        // get the SDK
        Sdk currentSdk = Sdk.getCurrent();
        AvdManager avdManager = currentSdk.getAvdManager();
        
        // reload the AVDs to make sure we are up to date
        try {
            avdManager.reloadAvds();
        } catch (AndroidLocationException e1) {
            // this happens if the AVD Manager failed to find the folder in which the AVDs are
            // stored. This is unlikely to happen, but if it does, we should force to go manual
            // to allow using physical devices.
            config.mTargetMode = TargetMode.MANUAL;
        }

        // get the project target
        final IAndroidTarget projectTarget = currentSdk.getTarget(project);
        
        // FIXME: check errors on missing sdk, AVD manager, or project target.
        
        // device chooser response object.
        final DeviceChooserResponse response = new DeviceChooserResponse();
        
        /*
         * Launch logic:
         * - Manually Mode
         *       Always display a UI that lets a user see the current running emulators/devices.
         *       The UI must show which devices are compatibles, and allow launching new emulators
         *       with compatible (and not yet running) AVD.
         * - Automatic Way
         *     * Preferred AVD set.
         *           If Preferred AVD is not running: launch it.
         *           Launch the application on the preferred AVD.
         *     * No preferred AVD.
         *           Count the number of compatible emulators/devices.
         *           If != 1, display a UI similar to manual mode.
         *           If == 1, launch the application on this AVD/device.
         */
        
        if (config.mTargetMode == TargetMode.AUTO) {
            // if we are in automatic target mode, we need to find the current devices
            IDevice[] devices = AndroidDebugBridge.getBridge().getDevices();
            
            // first check if we have a preferred AVD name, and if it actually exists, and is valid
            // (ie able to run the project).
            // We need to check this in case the AVD was recreated with a different target that is
            // not compatible.
            AvdInfo preferredAvd = null;
            if (config.mAvdName != null) {
                preferredAvd = avdManager.getAvd(config.mAvdName, true /*validAvdOnly*/);
                if (projectTarget.isCompatibleBaseFor(preferredAvd.getTarget()) == false) {
                    preferredAvd = null;

                    AdtPlugin.printErrorToConsole(project, String.format(
                            "Preferred AVD '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible AVD...",
                            config.mAvdName, projectTarget.getName()));
                }
            }
                
            if (preferredAvd != null) {
                // look for a matching device
                for (IDevice d : devices) {
                    String deviceAvd = d.getAvdName();
                    if (deviceAvd != null && deviceAvd.equals(config.mAvdName)) {
                        response.setDeviceToUse(d);

                        AdtPlugin.printToConsole(project, String.format(
                                "Automatic Target Mode: Preferred AVD '%1$s' is available on emulator '%2$s'",
                                config.mAvdName, d));

                        continueLaunch(response, project, launch, launchInfo, config);
                        return;
                    }
                }
                
                // at this point we have a valid preferred AVD that is not running.
                // We need to start it.
                response.setAvdToLaunch(preferredAvd);

                AdtPlugin.printToConsole(project, String.format(
                        "Automatic Target Mode: Preferred AVD '%1$s' is not available. Launching new emulator.",
                        config.mAvdName));

                continueLaunch(response, project, launch, launchInfo, config);
                return;
            }

            // no (valid) preferred AVD? look for one.
            HashMap<IDevice, AvdInfo> compatibleRunningAvds = new HashMap<IDevice, AvdInfo>();
            boolean hasDevice = false; // if there's 1+ device running, we may force manual mode,
                                       // as we cannot always detect proper compatibility with
                                       // devices. This is the case if the project target is not
                                       // a standard platform
            for (IDevice d : devices) {
                String deviceAvd = d.getAvdName();
                if (deviceAvd != null) { // physical devices return null.
                    AvdInfo info = avdManager.getAvd(deviceAvd, true /*validAvdOnly*/);
                    if (info != null && projectTarget.isCompatibleBaseFor(info.getTarget())) {
                        compatibleRunningAvds.put(d, info);
                    }
                } else {
                    if (projectTarget.isPlatform()) { // means this can run on any device as long
                                                      // as api level is high enough
                        String apiString = d.getProperty(SdkManager.PROP_VERSION_SDK);
                        try {
                            int apiNumber = Integer.parseInt(apiString);
                            if (apiNumber >= projectTarget.getApiVersionNumber()) {
                                // device is compatible with project
                                compatibleRunningAvds.put(d, null);
                                continue;
                            }
                        } catch (NumberFormatException e) {
                            // do nothing, we'll consider it a non compatible device below.
                        }
                    }
                    hasDevice = true;
                }
            }
            
            // depending on the number of devices, we'll simulate an automatic choice
            // from the device chooser or simply show up the device chooser.
            if (hasDevice == false && compatibleRunningAvds.size() == 0) {
                // if zero emulators/devices, we launch an emulator.
                // We need to figure out which AVD first.
                
                // we are going to take the closest AVD. ie a compatible AVD that has the API level
                // closest to the project target.
                AvdInfo[] avds = avdManager.getValidAvds();
                AvdInfo defaultAvd = null;
                for (AvdInfo avd : avds) {
                    if (projectTarget.isCompatibleBaseFor(avd.getTarget())) {
                        if (defaultAvd == null ||
                                avd.getTarget().getApiVersionNumber() <
                                    defaultAvd.getTarget().getApiVersionNumber()) {
                            defaultAvd = avd;
                        }
                    }
                }

                if (defaultAvd != null) {
                    response.setAvdToLaunch(defaultAvd);

                    AdtPlugin.printToConsole(project, String.format(
                            "Automatic Target Mode: launching new emulator with compatible AVD '%1$s'",
                            defaultAvd.getName()));

                    continueLaunch(response, project, launch, launchInfo, config);
                    return;
                } else {
                    // FIXME: ask the user if he wants to create a AVD.
                    // we found no compatible AVD.
                    AdtPlugin.printErrorToConsole(project, String.format(
                            "Failed to find an AVD compatible with target '%1$s'. Launch aborted.",
                            projectTarget.getName()));
                    stopLaunch(launchInfo);
                    return;
                }
            } else if (hasDevice == false && compatibleRunningAvds.size() == 1) {
                Entry<IDevice, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next();
                response.setDeviceToUse(e.getKey());

                // get the AvdInfo, if null, the device is a physical device.
                AvdInfo avdInfo = e.getValue();
                if (avdInfo != null) {
                    message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible AVD '%2$s'",
                            response.getDeviceToUse(), e.getValue().getName());
                } else {
                    message = String.format("Automatic Target Mode: using device '%1$s'",
                            response.getDeviceToUse());
                }
                AdtPlugin.printToConsole(project, message);

                continueLaunch(response, project, launch, launchInfo, config);
                return;
            }

            // if more than one device, we'll bring up the DeviceChooser dialog below.
            if (compatibleRunningAvds.size() >= 2) {
                message = "Automatic Target Mode: Several compatible targets. Please select a target device."; 
            } else if (hasDevice) {
                message = "Automatic Target Mode: Unable to detect device compatibility. Please select a target device."; 
            }

            AdtPlugin.printToConsole(project, message);
        }
        
        // bring up the device chooser.
        AdtPlugin.getDisplay().asyncExec(new Runnable() {
            public void run() {
                try {
                    // open the chooser dialog. It'll fill 'response' with the device to use
                    // or the AVD to launch.
                    DeviceChooserDialog dialog = new DeviceChooserDialog(
                            AdtPlugin.getDisplay().getActiveShell(),
                            response, launchInfo.getPackageName(), projectTarget);
                    if (dialog.open() == Dialog.OK) {
                        AndroidLaunchController.this.continueLaunch(response, project, launch,
                                launchInfo, config);
                    } else {
                        AdtPlugin.printErrorToConsole(project, "Launch canceled!");
                        stopLaunch(launchInfo);
                        return;
                    }
                } catch (Exception e) {
                    // there seems to be some case where the shell will be null. (might be
                    // an OS X bug). Because of this the creation of the dialog will throw
                    // and IllegalArg exception interrupting the launch with no user feedback.
                    // So we trap all the exception and display something.
                    String msg = e.getMessage();
                    if (msg == null) {
                        msg = e.getClass().getCanonicalName();
                    }
                    AdtPlugin.printErrorToConsole(project,
                            String.format("Error during launch: %s", msg));
                    stopLaunch(launchInfo);
                }
            }
        });
    
public voidlaunchApp(DelayedLaunchInfo info, com.android.ddmlib.IDevice device)
launches an application on a device or emulator

param
info the {@link DelayedLaunchInfo} that indicates the launch action
param
device the device or emulator to launch the application on

        if (info.isDebugMode()) {
            synchronized (sListLock) {
                if (mWaitingForDebuggerApplications.contains(info) == false) {
                    mWaitingForDebuggerApplications.add(info);
                }
            }
        }
        if (info.getLaunchAction().doLaunchAction(info, device)) {
            // if the app is not a debug app, we need to do some clean up, as
            // the process is done!
            if (info.isDebugMode() == false) {
                // stop the launch object, since there's no debug, and it can't
                // provide any control over the app
                stopLaunch(info);
            }
        } else {
            // something went wrong or no further launch action needed
            // lets stop the Launch
            stopLaunch(info);
        }
    
private booleanlaunchEmulator(AndroidLaunchConfiguration config, com.android.sdklib.avd.AvdManager.AvdInfo avdToLaunch)


        // split the custom command line in segments
        ArrayList<String> customArgs = new ArrayList<String>();
        boolean hasWipeData = false;
        if (config.mEmulatorCommandLine != null && config.mEmulatorCommandLine.length() > 0) {
            String[] segments = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$

            // we need to remove the empty strings
            for (String s : segments) {
                if (s.length() > 0) {
                    customArgs.add(s);
                    if (!hasWipeData && s.equals(FLAG_WIPE_DATA)) {
                        hasWipeData = true;
                    }
                }
            }
        }

        boolean needsWipeData = config.mWipeData && !hasWipeData;
        if (needsWipeData) {
            if (!AdtPlugin.displayPrompt("Android Launch", "Are you sure you want to wipe all user data when starting this emulator?")) {
                needsWipeData = false;
            }
        }
        
        // build the command line based on the available parameters.
        ArrayList<String> list = new ArrayList<String>();

        list.add(AdtPlugin.getOsAbsoluteEmulator());
        list.add(FLAG_AVD);
        list.add(avdToLaunch.getName());
        
        if (config.mNetworkSpeed != null) {
            list.add(FLAG_NETSPEED);
            list.add(config.mNetworkSpeed);
        }
        
        if (config.mNetworkDelay != null) {
            list.add(FLAG_NETDELAY);
            list.add(config.mNetworkDelay);
        }
        
        if (needsWipeData) {
            list.add(FLAG_WIPE_DATA);
        }

        if (config.mNoBootAnim) {
            list.add(FLAG_NO_BOOT_ANIM);
        }

        list.addAll(customArgs);
        
        // convert the list into an array for the call to exec.
        String[] command = list.toArray(new String[list.size()]);

        // launch the emulator
        try {
            Process process = Runtime.getRuntime().exec(command);
            grabEmulatorOutput(process);
        } catch (IOException e) {
            return false;
        }

        return true;
    
public static voidlaunchRemoteDebugger(int debugPort, AndroidLaunch androidLaunch, org.eclipse.core.runtime.IProgressMonitor monitor)
Launch a new thread that connects a remote debugger on the specified port.

param
debugPort The port to connect the debugger to
param
androidLaunch The associated AndroidLaunch object.
param
monitor A Progress monitor
see
#connectRemoteDebugger(int, AndroidLaunch, IProgressMonitor)

        new Thread("Debugger connection") { //$NON-NLS-1$
            @Override
            public void run() {
                try {
                    connectRemoteDebugger(debugPort, androidLaunch, monitor);
                } catch (CoreException e) {
                    androidLaunch.stopLaunch();
                }
                monitor.done();
            }
        }.start();
    
private static voidsetPortLaunchConfigAssociation(org.eclipse.debug.core.ILaunchConfiguration launchConfig, int port)
Set a {@link ILaunchConfiguration} and its associated debug port, in the list of launch config to connect directly to a running app instead of doing full launch (sync, launch, and connect to).

param
launchConfig the {@link ILaunchConfiguration} object.
param
port The debugger port to connect to.

        synchronized (sListLock) {
            sRunningAppMap.put(launchConfig, port);
        }
    
private booleansimpleLaunch(DelayedLaunchInfo launchInfo, com.android.ddmlib.IDevice device)
Do a simple launch on the specified device, attempting to sync the new package, and then launching the application. Failed sync/launch will stop the current AndroidLaunch and return false;

param
launchInfo
param
device
return
true if succeed

        // API level check
        if (checkBuildInfo(launchInfo, device) == false) {
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!");
            stopLaunch(launchInfo);
            return false;
        }

        // sync the app
        if (syncApp(launchInfo, device) == false) {
            AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!");
            stopLaunch(launchInfo);
            return false;
        }

        // launch the app
        launchApp(launchInfo, device);

        return true;
    
public voidstopLaunch(DelayedLaunchInfo launchInfo)

        launchInfo.getLaunch().stopLaunch();
        synchronized (sListLock) {
            mWaitingForReadyEmulatorList.remove(launchInfo);
            mWaitingForDebuggerApplications.remove(launchInfo);
        }       
    
private booleansyncApp(DelayedLaunchInfo launchInfo, com.android.ddmlib.IDevice device)
If needed, syncs the application and all its dependencies on the device/emulator.

param
launchInfo The Launch information object.
param
device the device on which to sync the application
return
true if the install succeeded.

        boolean alreadyInstalled = ApkInstallManager.getInstance().isApplicationInstalled(
                launchInfo.getProject(), device);
        
        if (alreadyInstalled) {
            AdtPlugin.printToConsole(launchInfo.getProject(),
            "Application already deployed. No need to reinstall.");
        } else {
            if (doSyncApp(launchInfo, device) == false) {
                return false;
            }
        }

        // The app is now installed, now try the dependent projects
        for (DelayedLaunchInfo dependentLaunchInfo : getDependenciesLaunchInfo(launchInfo)) {
            String msg = String.format("Project dependency found, installing: %s",
                    dependentLaunchInfo.getProject().getName());
            AdtPlugin.printToConsole(launchInfo.getProject(), msg);
            if (syncApp(dependentLaunchInfo, device) == false) {
                return false;
            }
        }
        
        return true;