com.motorola.studio.android.adt.DDMSUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.motorola.studio.android.adt.DDMSUtils.java

Source

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.motorola.studio.android.adt;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.IOConsoleOutputStream;

import com.android.ddmlib.FileListingService;
import com.android.ddmlib.FileListingService.FileEntry;
import com.android.ddmlib.IDevice;
import com.android.ddmuilib.ScreenShotDialog;
import com.android.sdklib.IAndroidTarget;
import com.motorola.studio.android.AndroidPlugin;
import com.motorola.studio.android.adt.StudioAndroidEventManager.EventType;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.log.UsageDataConstants;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorola.studio.android.common.utilities.FileUtil;
import com.motorola.studio.android.i18n.AndroidNLS;
import com.motorola.studio.android.wizards.installapp.DeployWizard;
import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
import com.motorola.studio.android.wizards.installapp.UninstallAppWizard;
import com.motorola.studio.android.wizards.mat.DumpHPROFWizard;
import com.motorola.studio.android.wizards.monkey.IMonkeyConfigurationConstants;

public class DDMSUtils {
    private static final Map<String, FileListingService> deviceFileListingServiceMap = new HashMap<String, FileListingService>();

    /**
     * The APK extension
     */
    private static final String APK_FILE_EXTENSION = "apk";

    /**
     * Parameter for overwriting existing applications, if any
     */
    private static final String ADB_INSTALL_OVERWRITE = "-r";

    /**
     * Options to be used with adb to indicate package manager application
     */
    private static final String PM_CMD = "pm";

    /**
     * Options to be used with adb to run monkey application
     */
    private static final String MONKEY_CMD = "monkey";

    /**
     * Uninstall directive to the package manager
     */
    private static final String PM_UNINSTALL_DIRECTIVE = "uninstall";

    /**
     * List packages directive
     */
    private static final String PM_LIST_DIRECTIVE = "list";

    /**
     * List packages directive
     */
    private static final String PM_PACKAGES_DIRECTIVE = "packages";

    /**
     * List packages force directive
     */
    private static final String PM_PACKAGES_DIRECTIVE_FORCE = "-f";

    /**
     * Monkey packages directive
     */
    private static final String MONKEY_PACKAGES_DIRECTIVE = " -p ";

    /**
     * Options to be used with adb to indicate install operation
     */
    private static final String INSTALL_CMD = "install";

    /**
     * This word must exist in the adb install commmand answer to indicate
     * succefull installation
     */
    private static final String SUCCESS_CONSTANT = "Success";

    private static final DdmsRunnable disconnectedListener = new DdmsRunnable() {
        public void run(String serialNumber) {
            synchronized (deviceFileListingServiceMap) {
                deviceFileListingServiceMap.remove(serialNumber);
            }
        }
    };

    static {
        StudioAndroidEventManager.asyncAddDeviceChangeListeners(null, disconnectedListener);
    }

    public static void takeScreenshot(final String serialNumber) {
        Display.getDefault().asyncExec(new Runnable() {

            public void run() {
                Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
                ScreenShotDialog sshot = new ScreenShotDialog(new Shell(shell));
                sshot.open(DDMSFacade.getDeviceBySerialNumber(serialNumber));
            }
        });
    }

    public static void getApplicationDatabases(String serialNumber, String applicationName,
            IDatabaseListingListener listener) {
        LinkedList<String> pathSegments = new LinkedList<String>();
        pathSegments.add("data");
        pathSegments.add("data");
        pathSegments.add(applicationName);
        pathSegments.add("databases");

        FileListingService fileListing = getFileListingService(serialNumber);

        if (fileListing != null) {
            FileEntry root = fileListing.getRoot();
            FileEntry[] children = null;

            children = fileListing.getChildren(root, true,
                    new FileListingReceiver(pathSegments, fileListing, listener));
            /*
             * If children isn't null means that file listing service found the
             * files in it cache to speed up the operation
             */
            if (children != null) {
                List<String> databases = new ArrayList<String>();
                FileEntry temp1 = null, temp2 = root;
                // start from root
                while ((children != null)) {
                    // if we have something to search for
                    if (pathSegments.size() > 0) {
                        String theSegment = pathSegments.remove(0);
                        temp1 = temp2.findChild(theSegment);

                        if (temp1 != null) {
                            temp2 = temp1;
                            children = fileListing.getChildren(temp2, true,
                                    new FileListingReceiver(pathSegments, fileListing, listener));
                        } else {
                            children = null;
                            listener.databasesFound(databases);
                        }
                    }
                    // else just notify the listener
                    else {
                        if (children != null) {
                            for (FileEntry child : children) {
                                if (child.getName().endsWith(".db")) {
                                    databases.add(child.getName());
                                }
                            }
                            children = null;
                        }
                        listener.databasesFound(databases);
                    }
                }
            }
        }
    }

    public static List<String> getApplicationDatabases(String serialNumber, String applicationName)
            throws IOException {
        List<String> dbs = new ArrayList<String>();

        String appDbPath = "/data/data/" + applicationName + "/databases/";

        Collection<String> commandOutput = DDMSFacade.execRemoteApp(serialNumber, "ls " + appDbPath,
                new NullProgressMonitor());
        List<String> dbPathCandidates = new ArrayList(commandOutput.size() + 10);

        for (String commandOutline : commandOutput) {
            String[] strings = commandOutline.split(" ");
            for (String string : strings) {
                if (string.trim().endsWith(".db")) {
                    dbPathCandidates.add(string);
                }
            }
        }

        return dbPathCandidates;
    }

    /**
     * @param serialNumber
     * @return
     */
    private static FileListingService getFileListingService(String serialNumber) {
        FileListingService fileListing = null;
        IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber);
        if (dev != null) {
            synchronized (dev) {
                fileListing = deviceFileListingServiceMap.get(serialNumber);
            }
            if (fileListing == null) {
                fileListing = dev.getFileListingService();
                synchronized (deviceFileListingServiceMap) {
                    deviceFileListingServiceMap.put(serialNumber, fileListing);
                }

            }
        }
        return fileListing;
    }

    /**
     * This method returns the current language and country in use by given
     * emulation instance.
     * 
     * @param serialNumber The serial number of emulation instance
     * @return An array of Strings containing the command results.
     */
    public static String[] getCurrentEmulatorLanguageAndCountry(final String serialNumber) {
        ArrayList<String[]> commands = createCurrentEmulatorLanguageAndCountryCommand(serialNumber);
        String[] responses = new String[2];
        String[] languageCommand = commands.get(0);
        String[] countryCommand = commands.get(1);
        String languageCommandResult = null;
        String countryCommandResult = null;

        try {
            // Do not write the command output to the console
            languageCommandResult = DDMSFacade.executeCommand(languageCommand, null);
            countryCommandResult = DDMSFacade.executeCommand(countryCommand, null);
            responses[0] = languageCommandResult.replaceAll("\\n$", "");
            responses[1] = countryCommandResult.replaceAll("\\n$", "");
        } catch (IOException e) {
            StudioLogger.error("Deploy: Could not execute adb current language command.");
        }
        return responses;
    }

    public static InstallPackageBean installPackageWizard() {

        final InstallPackageBean bean = new InstallPackageBean();

        final Display display = PlatformUI.getWorkbench().getDisplay();
        display.syncExec(new Runnable() {
            public void run() {
                try {
                    String defaultPath = null;
                    DeployWizard wizard;
                    IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
                    if (window != null) {
                        ISelection selection = window.getSelectionService().getSelection();
                        if (selection instanceof IStructuredSelection) {
                            IStructuredSelection workbenchSSelection = (IStructuredSelection) selection;
                            for (Object o : workbenchSSelection.toList()) {
                                if (o instanceof IFile) {
                                    IFile file = (IFile) o;
                                    if (file.getFileExtension().equalsIgnoreCase(APK_FILE_EXTENSION)) {
                                        defaultPath = file.getLocation().toOSString();
                                    }
                                }
                            }
                        }
                    }
                    wizard = new DeployWizard(defaultPath);
                    wizard.init(PlatformUI.getWorkbench(), new StructuredSelection());
                    WizardDialog dialog = new WizardDialog(
                            PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), wizard);
                    dialog.setPageSize(500, 200);
                    if (dialog.open() == IDialogConstants.OK_ID) {
                        bean.setPackagePath(wizard.getPackagePath());
                        bean.setCanOverwrite(wizard.canOverwrite());
                    }
                } catch (Throwable e) {
                    StudioLogger.error(DDMSFacade.class, "Error executing deploy wizard", e);
                }
            }
        });

        return bean;
    }

    public static IStatus installPackage(final String serialNumber, InstallPackageBean bean) {
        IStatus status = Status.CANCEL_STATUS;

        if ((bean.getPackagePath() != null) && (bean.getCanOverwrite() != null)) {
            OutputStream consoleOut = null;
            try {
                consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
                status = installPackage(serialNumber, bean.getPackagePath(), bean.getCanOverwrite(), consoleOut);
            } finally {
                try {
                    if (consoleOut != null) {
                        consoleOut.close();
                    }
                } catch (IOException e) {
                    StudioLogger.error("Install App: could not close console stream" + e.getMessage());
                }
            }
        }

        if (status.isOK()) {
            StudioAndroidEventManager.fireEvent(EventType.PACKAGE_INSTALLED, serialNumber);
        }

        return status;
    }

    /**
     * Install an application on an emulator instance
     * 
     * @param serialNumber
     *            The serial number of the device where the application will be
     *            installed
     * @param path
     *            Path of the package containing the application to be installed
     * @param installationType
     *            one of the {@link INSTALL_TYPE} install types available
     * @param force
     *            Perform the operation without asking for user intervention
     * 
     * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
     */
    public static IStatus installPackage(String serialNumber, String path, INSTALL_TYPE installationType,
            OutputStream processOut) {
        IStatus status = null;

        if (installationType.equals(INSTALL_TYPE.UNINSTALL)) {
            status = uninstallPackage(new File(path), serialNumber, processOut);
        }

        boolean overwrite = installationType.equals(INSTALL_TYPE.OVERWRITE);
        status = installPackage(serialNumber, path, overwrite, processOut);

        return status;
    }

    /**
     * Uninstall the given package (if installed) in the given serialNumber
     * device
     * 
     * @param path
     *            the package path
     * @param serialNumber
     *            the device serial number
     */
    public static IStatus uninstallPackage(File path, String serialNumber, OutputStream processOut) {
        IStatus returnStatus = null;
        if ((path != null) && path.exists() && path.isFile()) {
            IDevice dev = DDMSFacade.getDeviceBySerialNumber(serialNumber);
            String apiLevel = dev.getProperty("ro.build.version.sdk");
            IAndroidTarget target = SdkUtils.getTargetByAPILevel(Integer.parseInt(apiLevel));
            String aaptPath = SdkUtils.getTargetAAPTPath(target);
            if (aaptPath != null) {

                // resolve package name
                String[] aaptComm = new String[] { aaptPath, "d", "badging", path.toString() };

                InputStreamReader reader = null;
                BufferedReader bufferedReader = null;

                try {
                    Process aapt = Runtime.getRuntime().exec(aaptComm);

                    reader = new InputStreamReader(aapt.getInputStream());
                    bufferedReader = new BufferedReader(reader);
                    String infoLine = bufferedReader.readLine();

                    infoLine = infoLine.split(" ")[1].split("=")[1].replaceAll("'", "");
                    returnStatus = uninstallPackage(serialNumber, infoLine, processOut);

                } catch (Exception e) {
                    returnStatus = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                            AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e);
                } finally {
                    try {
                        if (reader != null) {
                            reader.close();
                        }
                        if (bufferedReader != null) {
                            bufferedReader.close();
                        }
                    } catch (IOException e) {
                        StudioLogger.error("Uninstall app could not close stream. " + e.getMessage());
                    }

                }
            } else {
                StudioLogger.error(DDMSFacade.class,
                        "Impossible to check APK package name. No android targets found inside SDK");
            }

        } else {
            returnStatus = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                    AndroidNLS.ERR_DDMSFacade_UninstallPackage);
        }
        return returnStatus;
    }

    /**
     * Uninstall the given package within device with given serial number
     * 
     * @param serialNumber
     * @param packageName
     * @param processOutput
     *            outputStream to write output of the process
     */
    public static IStatus uninstallPackage(String serialNumber, String packageName, OutputStream processOutput) {
        IStatus status = Status.OK_STATUS;
        String command[] = createUninstallCommand(serialNumber, packageName);

        try {
            String commandResult = DDMSFacade.executeCommand(command, processOutput);
            if (!commandResult.toLowerCase().contains(SUCCESS_CONSTANT.toLowerCase())) {
                status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                        AndroidNLS.ERR_DDMSFacade_UninstallPackageError + ": " + packageName);
            }

        } catch (Exception e) {
            status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                    AndroidNLS.ERR_DDMSFacade_UninstallPackageException, e);
            StudioLogger.error(DDMSFacade.class,
                    "Failed to remove package: " + packageName + ". " + e.getMessage());
        }
        return status;
    }

    /**
     * Run the Monkey command for the given package within device with given serial number
     * 
     * @param serialNumber
     * @param packageName
     * @param processOutput            
     * @param otherCmd
     * @return the status of the monkey process
     */
    public static IStatus runMonkey(String serialNumber, String allPackages, OutputStream processOutput,
            String otherCmd) {
        IStatus status = Status.OK_STATUS;
        String command[] = createMonkeyCommand(serialNumber, allPackages, otherCmd);

        try {
            DDMSFacade.executeCommand(command, processOutput);

        } catch (Exception e) {
            EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, AndroidNLS.UI_MonkeyError_Msg);
            StudioLogger.error(DDMSFacade.class, "Failed to run monkey command: " + command + " " + e.getMessage());
        }
        return status;
    }

    /**
     * Uninstall packages from the given serialNumber device
     * 
     * @param serialNumber
     * @param packagesToUninstall
     * @param outputStream
     * @return the status of the uninstall process or null if no packages were
     *         uninstalled
     */
    private static IStatus uninstallPackages(String serialNumber, ArrayList<String> packagesToUninstall,
            OutputStream outputStream) {

        IStatus returnStatus = null;
        for (String packageToUninstall : packagesToUninstall) {
            StudioLogger.info(DDMSUtils.class, "Removing package: " + packageToUninstall);
            IStatus temp = uninstallPackage(serialNumber, packageToUninstall, outputStream);
            if (!temp.isOK()) {
                if (returnStatus == null) {
                    returnStatus = new MultiStatus(AndroidPlugin.PLUGIN_ID, 0,
                            AndroidNLS.ERR_DDMSFacade_UninstallPackageError, null);
                }

                ((MultiStatus) returnStatus).add(temp);
            }
        }
        return returnStatus == null ? Status.OK_STATUS : returnStatus;
    }

    /**
     * Run monkey command on the given packages with the specified commands.
     * 
     * @param serialNumber
     * @param packagesToRunMonkey
     * @param outputStream
     * @param otherCmds
     * @return the status of the monkey process or null if the process was not successful
     */
    private static IStatus runMonkey(String serialNumber, ArrayList<String> packagesToRunMonkey,
            OutputStream outputStream, String otherCmds) {

        IStatus returnStatus = null;
        String allPackages = "";
        for (String packageToRunMonkey : packagesToRunMonkey) {
            allPackages += MONKEY_PACKAGES_DIRECTIVE + packageToRunMonkey;
        }
        StudioLogger.info(DDMSUtils.class, "Running monkey for: " + allPackages);
        IStatus temp = runMonkey(serialNumber, allPackages, outputStream, otherCmds);
        if (!temp.isOK()) {
            if (returnStatus == null) {
                returnStatus = new MultiStatus(AndroidPlugin.PLUGIN_ID, 0, AndroidNLS.ERR_DDMSFacade_MonkeyError,
                        null);
            }

            ((MultiStatus) returnStatus).add(temp);
        }
        return returnStatus == null ? Status.OK_STATUS : returnStatus;
    }

    /**
     * Uninstall packages from the given device. A Wizard will be opened asking
     * the user which packages to uninstall
     * 
     * @param serialNumber
     * @return the status of the operation
     */
    public static IStatus uninstallPackage(final String serialNumber) {
        final ArrayList<String> packagesToUninstall = new ArrayList<String>();
        IStatus status = null;
        //wrap the dialog within a final variable
        final UninstallAppWizard[] wizard = new UninstallAppWizard[1];

        // do package listing async
        Thread listingThread = new Thread("listPackages") {

            @Override
            public void run() {
                Map<String, String> installedPackages = null;
                try {
                    installedPackages = listInstalledPackages(serialNumber);
                } catch (IOException e1) {
                    installedPackages = new HashMap<String, String>(0);
                }

                while (wizard[0] == null) {
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        //do nothing
                    }
                }
                wizard[0].setAvailablePackages(installedPackages);
            };
        };

        listingThread.start();

        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
            public void run() {
                UninstallAppWizard unAppWiz = new UninstallAppWizard();
                WizardDialog dialog = new WizardDialog(
                        new Shell(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()), unAppWiz);
                wizard[0] = unAppWiz;
                dialog.open();
                List<String> selectedPackages = wizard[0].getSelectedPackages();
                if (selectedPackages != null) {
                    for (String aPackage : selectedPackages) {
                        packagesToUninstall.add(aPackage);
                    }
                }
            }
        });

        if (packagesToUninstall.size() > 0) {
            OutputStream consoleOut = null;

            try {
                consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
                status = uninstallPackages(serialNumber, packagesToUninstall, consoleOut);
            } finally {
                try {
                    if (consoleOut != null) {
                        consoleOut.close();
                    }
                } catch (IOException e) {
                    StudioLogger.error("Uninstall App: could not close console stream" + e.getMessage());
                }
            }
        }
        if (status != null) {
            if (status.isOK()) {
                Display.getDefault().asyncExec(new Runnable() {
                    public void run() {
                        MessageDialog.openInformation(
                                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
                                AndroidNLS.UI_UninstallApp_SucessDialogTitle, AndroidNLS.UI_UninstallApp_Message);
                    }
                });
                StudioAndroidEventManager.fireEvent(EventType.PACKAGE_UNINSTALLED, serialNumber);
            } else {
                EclipseUtils.showErrorDialog(AndroidNLS.UI_UninstallApp_ERRDialogTitle,
                        AndroidNLS.UI_UninstallApp_ERRUninstallApp, status);
            }
        }
        // all return is successful since operations are async
        return Status.OK_STATUS;

    }

    /**
     * If there is no Monkey Launch configuration for the given deviceName, it is created.
     * @param deviceName
     */
    private static ILaunchConfiguration createLaunchConfiguration(String deviceName) {
        ILaunchConfiguration config = null;
        ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
        ILaunchConfigurationType motodevLaunchType = launchManager
                .getLaunchConfigurationType(IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
        String launchConfigurationName = launchManager
                .generateUniqueLaunchConfigurationNameFrom(IMonkeyConfigurationConstants.NEW_CONFIGURATION_NAME);
        try {
            ILaunchConfigurationWorkingCopy workingCopy = motodevLaunchType.newInstance(null,
                    launchConfigurationName);
            workingCopy.setAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, deviceName);
            config = workingCopy.doSave();
        } catch (CoreException e) {
            EclipseUtils.showErrorDialog(AndroidNLS.UI_MonkeyError_Title, e.getMessage());
        }
        return config;

    }

    /**
     * Run adb monkey.
     * 
     * @param serialNumber
     * @param deviceName
     * @return the status of the operation
     */
    public static IStatus runMonkey(final String serialNumber, final String deviceName) {

        ILaunchConfigurationType type = DebugPlugin.getDefault().getLaunchManager()
                .getLaunchConfigurationType(IMonkeyConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID);
        ILaunchConfiguration[] launchs;
        try {
            launchs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(type);

            ILaunchConfiguration launchConfig = null;
            for (int i = 0; i < launchs.length; i++) {
                launchConfig = launchs[i];
                String configDeviceName = launchConfig
                        .getAttribute(IMonkeyConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, "");
                if (configDeviceName.equals(deviceName)) {
                    break;
                } else {
                    launchConfig = null;
                }

            }
            if (launchConfig == null) {
                launchConfig = createLaunchConfiguration(deviceName);

            }

            final ILaunchGroup lc = DebugUITools.getLaunchGroup(launchConfig, ILaunchManager.RUN_MODE);

            final IStructuredSelection selection = new StructuredSelection(launchConfig);

            PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                public void run() {

                    DebugUITools.openLaunchConfigurationDialogOnGroup(
                            new Shell(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()), selection,
                            lc.getIdentifier(), null);

                }
            });

        } catch (CoreException e) {
            StudioLogger.error("Monkey: could not open the launch configuration dialog " + e.getMessage());

        }

        // UDC log for monkey execution
        StudioLogger.collectUsageData(UsageDataConstants.WHAT_MONKEY_EXEC, //$NON-NLS-1$
                UsageDataConstants.KIND_MONKEY_EXEC, "Monkey executed", //$NON-NLS-1$
                AndroidPlugin.PLUGIN_ID,
                AndroidPlugin.getDefault().getBundle().getBundleContext().getBundle().getVersion().toString());

        // all return is successful since operations are async
        return Status.OK_STATUS;

    }

    /**
     * Run adb monkey.
     * 
     * @param serialNumber
     * @param packagesToRunMonkey
     * @param otherCmds
     * @return the status of the operation
     */
    public static IStatus runMonkey(final String serialNumber, ArrayList<String> packagesToRunMonkey,
            String otherCmds) {

        if (packagesToRunMonkey.size() > 0) {
            OutputStream consoleOut = null;

            try {
                consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
                runMonkey(serialNumber, packagesToRunMonkey, consoleOut, otherCmds);
            } finally {
                try {
                    if (consoleOut != null) {
                        consoleOut.close();
                    }
                } catch (IOException e) {
                    StudioLogger.error("Monkey: could not close console stream" + e.getMessage());
                }
            }
        }

        // all return is successful since operations are async
        return Status.OK_STATUS;

    }

    /**
     * List the installed packages in the device with the serial number Each
     * package entry carries their package location
     * 
     * @param serialNumber
     * @return a HashMap where keys are the package names and values are package
     *         location
     * @throws IOException 
     */
    public static Map<String, String> listInstalledPackages(String serialNumber) throws IOException {
        Map<String, String> packages = new LinkedHashMap<String, String>();
        String sdkPath = SdkUtils.getSdkPath();
        String command[] = new String[] {
                sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, DDMSFacade.SHELL_CMD, PM_CMD, PM_LIST_DIRECTIVE,
                PM_PACKAGES_DIRECTIVE, PM_PACKAGES_DIRECTIVE_FORCE };

        String commResult = DDMSFacade.executeCommand(command, null);
        String[] packageList = null;
        if ((commResult != null) && (commResult.length() > 0) && !commResult.contains("system running?")) {
            packageList = commResult.trim().replaceAll("\n\n", "\n").split("\n");
            int i = 0;
            String aPackage = null;
            String[] packageUnit = null;
            while (i < packageList.length) {
                if (packageList[i].toLowerCase().contains("package:")) {
                    String[] splittedPackage = packageList[i].split(":");
                    if (splittedPackage.length >= 2) {
                        aPackage = splittedPackage[1].trim();
                        packageUnit = aPackage.split("=");
                        if (packageUnit.length > 1) {
                            packages.put(packageUnit[1], packageUnit[0]);
                        }
                    }
                }
                i++;
            }
        }
        return packages;
    }

    /**
     * Install an application on an emulator instance
     * 
     * @param serialNumber
     *            The serial number of the device where the application will be
     *            installed
     * @param path
     *            Path of the package containing the application to be installed
     * @param canOverwrite
     *            If the application will be installed even if there is a
     *            previous installation
     * @param force
     *            Perform the operation without asking for user intervention
     * 
     * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
     */
    public static IStatus installPackage(String serialNumber, String path, boolean canOverwrite,
            OutputStream processOut) {
        IStatus status = Status.OK_STATUS;

        // Return if no instance is selected
        if (serialNumber == null) {
            StudioLogger.error("Abort deploy operation. Serial number is null.");
            status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                    AndroidNLS.ERR_DDMSFacade_SerialNumberNullPointer);
        }

        // Return if instance is not started
        if (status.isOK() && !DDMSFacade.isDeviceOnline(serialNumber)) {
            StudioLogger.error("Abort deploy operation. Device is not online.");
            status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, "");
        }

        String command_results = "";
        if (status.isOK()) {
            try {
                String[] cmd = createInstallCommand(canOverwrite, path, serialNumber);
                command_results = DDMSFacade.executeCommand(cmd, processOut, serialNumber);

                // Check if the result has a success message
                if (!command_results.contains(SUCCESS_CONSTANT)) {
                    status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID,
                            "Error executing the operation. Execution results: " + command_results);
                }
            } catch (IOException e) {
                StudioLogger.error("Deploy: Could not execute adb install command.");
                status = new Status(IStatus.ERROR, AndroidPlugin.PLUGIN_ID, e.getMessage());
            }
        }

        return status;
    }

    /**
     * Change the emulator language
     * 
     * @param serialNumber
     *            The serial number of the device
     * @param language
     *            the language id
     * @param country
     *            the country id
     * @return the status of the operation (OK, Error+ErrorMessage)
     */
    public static void changeLanguage(final String serialNumber, final String language, final String country) {

        if ((language != null) || (country != null)) {
            /*
             * A new thread is used since this command takes too long to be executed
             */
            Thread thead = new Thread(new Runnable() {

                public void run() {

                    String[] cmd = createChangeLanguageCommand(serialNumber, language, country);
                    try {

                        IOConsoleOutputStream consoleOut = EclipseUtils.getStudioConsoleOutputStream(true);
                        if (language != null) {
                            consoleOut.write(AndroidNLS.UI_ChangeLang_Language + " " + language + "\n");
                        }
                        if (country != null) {
                            consoleOut.write(AndroidNLS.UI_ChangeLang_Country + " " + country + "\n");
                        }

                        DDMSFacade.executeCommand(cmd, consoleOut);
                        consoleOut.write("\n " + serialNumber + ":"
                                + AndroidNLS.UI_ChangeLang_Restart_Device_Manually + "\n\n");
                        StudioAndroidEventManager.fireEvent(EventType.LANGUAGE_CHANGED, serialNumber);
                    } catch (IOException e) {
                        StudioLogger.error("Language: Could not execute adb change language command.");
                    }

                }
            });
            thead.start();
        }

    }

    /**
     * Creates a string with the command that should be called in order to
     * change the device language
     * 
     * @param serialNumber
     *            The serial number of the device
     * @param language
     *            the language id
     * @param country
     *            the country id
     * @return the command to be used to change the device language
     */
    private static String[] createChangeLanguageCommand(String serialNumber, String language, String country) {
        String cmd[];
        String sdkPath = SdkUtils.getSdkPath();

        String CHANGE_LANGUAGE_CMD = "";
        if (language != null) {
            CHANGE_LANGUAGE_CMD += "setprop persist.sys.language " + language;
        }
        if (country != null) {
            CHANGE_LANGUAGE_CMD += ((CHANGE_LANGUAGE_CMD.length() > 0) ? ";" : "") + "setprop persist.sys.country "
                    + country;
        }

        // The tools folder should exist and be here, but double-checking
        // once more wont kill
        File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        if (!f.exists()) {
            StudioLogger.error("Language: Could not find tools folder on " + sdkPath
                    + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        } else {
            if (!f.isDirectory()) {
                StudioLogger.error("Language: Invalid tools folder " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER
                        + File.separator);
            }
        }

        String cmdTemp[] = { sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, "shell", CHANGE_LANGUAGE_CMD };
        cmd = cmdTemp;

        return cmd;
    }

    /**
     * Creates strings with the command that should be called in order to
     * retrieve the current language and country in use by given emulator instance.
     * 
     * @param serialNumber The serial number of the device
     * @return An ArrayList with command strings to be used to get the 
     * current emulator language and country.
     */
    private static ArrayList<String[]> createCurrentEmulatorLanguageAndCountryCommand(String serialNumber) {
        String languageCommand[];
        String countryCommand[];
        String sdkPath = SdkUtils.getSdkPath();
        String GET_LANGUAGE_CMD = "getprop persist.sys.language";
        String GET_COUNTRY_CMD = "getprop persist.sys.country";
        // The tools folder should exist and be here, but double-cheking
        // once more wont kill
        File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        if (!f.exists()) {
            StudioLogger.error("Language: Could not find tools folder on " + sdkPath
                    + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        } else {
            if (!f.isDirectory()) {
                StudioLogger.error("Language: Invalid tools folder " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER
                        + File.separator);
            }
        }
        String langCmdTemp[] = {
                sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, "shell", GET_LANGUAGE_CMD };
        String countryCmdTemp[] = {
                sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, "shell", GET_COUNTRY_CMD };
        languageCommand = langCmdTemp;
        countryCommand = countryCmdTemp;

        ArrayList<String[]> commands = new ArrayList<String[]>();
        commands.add(0, languageCommand);
        commands.add(1, countryCommand);
        return commands;
    }

    /**
     * Creates a string with the command that should be called in order to
     * install the application
     * 
     * @param canOverwrite
     *            If true, than existent application will be overwritten
     * @param path
     *            Location of the package containing the application
     * @param serialNumber
     *            The serial number of the device to be targeted
     * 
     * @return
     */
    private static String[] createInstallCommand(boolean canOverwrite, String path, String serialNumber) {
        String cmd[];
        String sdkPath = SdkUtils.getSdkPath();

        // The tools folder should exist and be here, but double-checking
        // once more wont kill
        File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        if (!f.exists()) {
            StudioLogger.error("Deploy: Could not find tools folder on " + sdkPath
                    + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        } else {
            if (!f.isDirectory()) {
                StudioLogger.error("Deploy: Invalid tools folder " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER
                        + File.separator);
            }
        }

        if (canOverwrite) {
            // If overwrite option is checked, create command with the -r
            // paramater
            String cmdTemp[] = {
                    sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                    DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, INSTALL_CMD, ADB_INSTALL_OVERWRITE, path };
            cmd = cmdTemp;
        } else {
            // If overwrite option is unchecked, create command without the -r
            // paramater
            String cmdTemp[] = {
                    sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                    DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, INSTALL_CMD, path };
            cmd = cmdTemp;
        }

        return cmd;
    }

    private static String[] createUninstallCommand(String serialNumber, String packageName) {
        String sdkPath = SdkUtils.getSdkPath();
        // The tools folder should exist and be here, but double-checking
        // once more wont kill
        File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        if (!f.exists()) {
            StudioLogger.error("Run: Could not find tools folder on " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER
                    + File.separator);
        } else {
            if (!f.isDirectory()) {
                StudioLogger.error(
                        "Run: Invalid tools folder " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
            }
        }

        String cmd[] = { sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, DDMSFacade.SHELL_CMD, PM_CMD,
                PM_UNINSTALL_DIRECTIVE, packageName };

        return cmd;

    }

    /**
     * Mount the adb monkey command based on the given parameters. 
     * @param serialNumber
     * @param packagesName
     * @param otherCmd
     * @return the array of strings containing the monkey command to be executed.
     */
    private static String[] createMonkeyCommand(String serialNumber, String packagesName, String otherCmd) {
        String sdkPath = SdkUtils.getSdkPath();
        // The tools folder should exist and be here, but double-checking
        // once more wont kill
        File f = new File(sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
        if (!f.exists()) {
            StudioLogger.error("Run: Could not find tools folder on " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER
                    + File.separator);
        } else {
            if (!f.isDirectory()) {
                StudioLogger.error(
                        "Run: Invalid tools folder " + sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator);
            }
        }

        String cmd[] = { sdkPath + DDMSFacade.PLATFORM_TOOLS_FOLDER + File.separator + DDMSFacade.ADB_COMMAND,
                DDMSFacade.ADB_INSTANCE_PARAMETER, serialNumber, DDMSFacade.SHELL_CMD, MONKEY_CMD, packagesName,
                otherCmd };

        return cmd;

    }

    /**
     * Dump HPROF service implementation
     * @param serialNumber The device serial number
     * @param monitor
     * @return A IStatus describing if the service was successful or not
     */
    public static IStatus dumpHPROF(final String serialNumber, IProgressMonitor monitor) {
        IStatus status;

        // Selected app. The array should have only 1 element
        final String[] selectedAppSet = new String[1];

        // Instantiate the wizard and populate it with the retrieved process list
        PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
            public void run() {
                DumpHPROFWizard dumpHPROFWizard = new DumpHPROFWizard(serialNumber);
                WizardDialog dialog = new WizardDialog(
                        new Shell(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell()),
                        dumpHPROFWizard);
                dialog.open();

                // Get the selected application
                selectedAppSet[0] = dumpHPROFWizard.getSelectedApp();

            }
        });

        if (selectedAppSet[0] != null) {
            // Dump HPROF file based on the selected application
            status = DDMSFacade.dumpHprofFile(selectedAppSet[0], serialNumber, monitor);
        } else {
            status = Status.CANCEL_STATUS;
        }

        return status;
    }

    public static int getDeviceApiVersion(String serialNumber) {
        int deviceSdkVersion = -1;
        String deviceProperty = DDMSFacade.getDeviceProperty(serialNumber, "ro.build.version.sdk");
        if (deviceProperty != null) {
            deviceSdkVersion = Integer.parseInt(deviceProperty);
        }

        return deviceSdkVersion;
    }

    public static boolean remoteFileExists(String serialNumber, String remotePath) throws IOException {
        boolean found = false;
        Collection<String> results = DDMSFacade.execRemoteApp(serialNumber,
                "ls " + FileUtil.getEscapedPath(remotePath, Platform.OS_LINUX), new NullProgressMonitor());
        for (String result : results) {
            if (result.equals(remotePath)) {
                found = true;
                break;
            }
        }
        return found;
    }
}