com.motorola.studio.android.emulator.logic.StartEmulatorProcessLogic.java Source code

Java tutorial

Introduction

Here is the source code for com.motorola.studio.android.emulator.logic.StartEmulatorProcessLogic.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.emulator.logic;

import static com.motorola.studio.android.common.log.StudioLogger.debug;
import static com.motorola.studio.android.common.log.StudioLogger.info;

import java.io.File;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IViewPart;
import org.osgi.framework.ServiceReference;

import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import com.motorola.studio.android.adt.DDMSFacade;
import com.motorola.studio.android.adt.SdkUtils;
import com.motorola.studio.android.common.exception.AndroidException;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorola.studio.android.emulator.EmulatorPlugin;
import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
import com.motorola.studio.android.nativeos.NativeUIUtils;

@SuppressWarnings("restriction")
public class StartEmulatorProcessLogic implements IAndroidLogic {
    /**
     * 
     */
    private static final String EMULATOR_NO_SNAPSHOT_LOAD = "-no-snapshot-load";

    /**
     * 
     */
    private static final String EMULATOR_NO_SNAPSHOT_SAVE = "-no-snapshot-save";

    /**
     * 
     */
    private static final String EMULATOR_HTTP_PROXY_PARAMETER = "-http-proxy";

    // Proxy constants
    private static final String PROXY_AT = "@";

    private static final String PROXY_COLON = ":";

    private static final String PROXY_HTTP = "http://";

    private static final String EMULATOR_VIEW = "com.motorola.studio.android.emulator.androidView";

    // Strings used to build the command line for launching the emulator process
    private static final String ARM_EMULATOR_RELATIVE_PATH = "/tools/emulator-arm";

    private static final String x86_EMULATOR_RELATIVE_PATH = "/tools/emulator-x86";

    private static final String EMULATOR_RELATIVE_PATH = "/tools/emulator";

    private static final String EMULATOR_VM_PARAMETER = "-avd";

    private static String selectedEmulatorPath = "";

    public void execute(final IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
            throws InstanceStartException, StartTimeoutException, StartCancelledException {

        long timeoutLimit = AndroidLogicUtils.getTimeoutLimit(timeout);

        info("Starting the Android Emulator process: " + instance);
        instance.setWindowHandle(0);

        File userData = instance.getUserdata();

        if (userData != null) {
            File userdataDir = userData.getParentFile();
            if ((userdataDir != null) && (!userdataDir.exists())) {
                userdataDir.mkdirs();
            }
        }

        selectedEmulatorPath = retrieveEmulatorExecutableName(instance);

        File emulatorExe = new File(SdkUtils.getSdkPath(), selectedEmulatorPath);

        List<String> cmdList = new LinkedList<String>();

        cmdList.add(emulatorExe.getAbsolutePath());
        cmdList.add(EMULATOR_VM_PARAMETER);
        cmdList.add(instance.getName());

        Properties propArgs = instance.getCommandLineArgumentsAsProperties();
        IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
        String adtEmuOptions = store.getString(AdtPrefs.PREFS_EMU_OPTIONS);

        StringTokenizer adtOptionsTokenizer = new StringTokenizer(adtEmuOptions, " ");
        while (adtOptionsTokenizer.hasMoreTokens()) {
            String nextToken = adtOptionsTokenizer.nextToken();
            cmdList.add(nextToken);
        }

        for (Object key : propArgs.keySet()) {
            String value = propArgs.getProperty(key.toString());

            if (key.equals("other")) {
                StringTokenizer stringTokenizer = new StringTokenizer(value, " ");
                while (stringTokenizer.hasMoreTokens()) {
                    cmdList.add(stringTokenizer.nextToken());
                }
            } else {
                if ((value.trim().length() > 0) && !value.equals(Boolean.TRUE.toString())) {
                    cmdList.add(key.toString());
                    if (Platform.getOS().equals(Platform.OS_MACOSX)) {
                        if (value.contains(" ")) {
                            value = "\"" + value + "\"";
                        }
                    } else {
                        if (value.contains("\\")) {
                            value = value.replace("\\", "\\\\");
                        }

                        if (value.contains(" ")) {
                            value = value.replace(" ", "\\ ");
                        }
                    }

                    cmdList.add(value);
                } else if ((value.trim().length() > 0) && value.equals(Boolean.TRUE.toString())) {
                    cmdList.add(key.toString());
                }
            }
        }

        // add proxy in case it is needed
        Properties properties = instance.getProperties();
        if (properties != null) {
            String useProxy = properties.getProperty(IDevicePropertiesConstants.useProxy,
                    IDevicePropertiesConstants.defaultUseProxyValue);
            if (Boolean.TRUE.toString().equals(useProxy)) {
                addEmulatorProxyParameter(cmdList);
            }
        }

        StringBuffer cmdLog = new StringBuffer("");

        boolean httpProxyParamFound = false;
        boolean logHttpProxyUsage = false;
        for (String param : cmdList) {
            // Do not log -http-proxy information
            if (!httpProxyParamFound) {
                if (!param.equals(EMULATOR_HTTP_PROXY_PARAMETER)) {
                    if (param.startsWith(emulatorExe.getAbsolutePath())) {
                        // do not log emulator full path
                        cmdLog.append(selectedEmulatorPath + " ");
                    } else {
                        cmdLog.append(param + " ");
                    }
                } else {
                    httpProxyParamFound = true;
                    logHttpProxyUsage = true;
                }
            } else {
                httpProxyParamFound = false;
            }
        }

        // Append http proxy usage to log
        if (logHttpProxyUsage) {
            cmdLog.append("\nProxy settings are being used by the started emulator (-http-proxy parameter).");
        }
        // add command to not start from snapshot
        if (properties != null) {
            String startFromSnapshot = properties.getProperty(IDevicePropertiesConstants.startFromSnapshot,
                    IDevicePropertiesConstants.defaultstartFromSnapshotValue);
            if (Boolean.FALSE.toString().equals(startFromSnapshot)) {
                cmdList.add(EMULATOR_NO_SNAPSHOT_LOAD);
            }
        }

        // Add the command to not save snapshot
        if (properties != null) {
            String saveSnapshot = properties.getProperty(IDevicePropertiesConstants.saveSnapshot,
                    IDevicePropertiesConstants.defaulSaveSnapshot);
            if (Boolean.FALSE.toString().equals(saveSnapshot)) {
                cmdList.add(EMULATOR_NO_SNAPSHOT_SAVE);
            }
        }

        Process p;
        try {
            p = AndroidLogicUtils.executeProcess(cmdList.toArray(new String[0]), cmdLog.toString());
        } catch (AndroidException e) {
            throw new InstanceStartException(e);
        }
        info("Wait until and emulator with the VM " + instance.getName() + " is up ");

        AndroidLogicUtils.testProcessStatus(p);
        instance.setProcess(p);
        instance.setComposite(null);

        final String avdName = instance.getName();

        if (!Platform.getOS().equals(Platform.OS_MACOSX)) {
            Collection<IViewPart> openedAndroidViews = EclipseUtils.getAllOpenedViewsWithId(EMULATOR_VIEW);

            if (!openedAndroidViews.isEmpty()) {
                Runnable runnable = new Runnable() {

                    public void run() {
                        long windowHandle = -1;
                        long timeOutToFindWindow = System.currentTimeMillis() + 30000;

                        do {
                            try {
                                Thread.sleep(250);
                            } catch (InterruptedException e) {
                                // do nothing
                            }

                            try {
                                AndroidLogicUtils.testTimeout(timeOutToFindWindow, "");
                            } catch (StartTimeoutException e) {
                                debug("Emulator window could not be found, instance :" + avdName);
                                break;
                            }

                            try {
                                int port = AndroidLogicUtils
                                        .getEmulatorPort(DDMSFacade.getSerialNumberByName(instance.getName()));
                                if (port > 0) {
                                    windowHandle = NativeUIUtils.getWindowHandle(instance.getName(), port);
                                }

                            } catch (Exception t) {
                                t.getCause().getMessage();
                                System.out.println(t.getCause().getMessage());
                            }
                        } while (windowHandle <= 0);

                        if (windowHandle > 0) {
                            instance.setWindowHandle(windowHandle);
                            NativeUIUtils.hideWindow(windowHandle);
                        }
                    }
                };
                Thread getHandleThread = new Thread(runnable, "Window Handle Thread");
                getHandleThread.start();
            }
        }

        if (instance.getProperties()
                .getProperty(IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc())
                .equals(Boolean.TRUE.toString())) {
            do {
                try {
                    Thread.sleep(450);
                } catch (InterruptedException e) {
                    // do nothing
                }

                AndroidLogicUtils.testCanceled(monitor);
                try {
                    AndroidLogicUtils.testTimeout(timeoutLimit,
                            NLS.bind(EmulatorNLS.EXC_TimeoutWhileStarting, avdName));
                } catch (StartTimeoutException e) {
                    debug("Emulator start timeout has been reached, instance :" + avdName + " has device: "
                            + instance.hasDevice() + "isOnline? "
                            + DDMSFacade.isDeviceOnline(DDMSFacade.getSerialNumberByName(avdName)));
                    throw e;
                }
            } while (!isEmulatorReady(avdName));

        }

        Thread t = new Thread("Process Error") {
            @Override
            public void run() {
                boolean shouldTryAgain = true;
                for (int i = 0; (i < 90) && shouldTryAgain; i++) {
                    try {
                        sleep(500);
                        Process p = instance.getProcess();
                        if (p != null) {
                            AndroidLogicUtils.testProcessStatus(p);
                        }
                    } catch (Exception e) {
                        StudioLogger.info(StartEmulatorProcessLogic.class,
                                "Trying to stop the emulator process: execution stopped too early");
                        DialogWithToggleUtils.showError(EmulatorPlugin.EMULATOR_UNEXPECTEDLY_STOPPED,
                                EmulatorNLS.GEN_Error,
                                NLS.bind(EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped, instance.getName()));
                        shouldTryAgain = false;
                        try {
                            instance.stop(true);
                        } catch (InstanceStopException ise) {
                            StudioLogger.error(StartEmulatorProcessLogic.class,
                                    "Error trying to stop instance on process error", ise);
                        }
                    }
                }
            }
        };
        t.start();

        debug("Emulator instance is now up and running... " + instance);
    }

    /**
     * retrives the emulator executable name according to abi type property
     * 
     * @param instance
     * @return
     */
    private static String retrieveEmulatorExecutableName(IAndroidLogicInstance instance) {
        String emulatorPath = null;

        Properties prop = instance.getProperties();
        String abiType = prop.getProperty("Abi_Type");

        if ((abiType == null) || (abiType.equals(""))) {
            emulatorPath = EMULATOR_RELATIVE_PATH;
        } else if (abiType.toLowerCase().contains("arm")) {
            emulatorPath = ARM_EMULATOR_RELATIVE_PATH;
        } else {
            emulatorPath = x86_EMULATOR_RELATIVE_PATH;
        }

        File emulatorExe = new File(SdkUtils.getSdkPath(), emulatorPath + ".exe");

        if (!emulatorExe.exists()) {
            emulatorPath = EMULATOR_RELATIVE_PATH;
        }

        return emulatorPath;
    }

    /**
     * Retrieve the Proxy service.
     * 
     * @return IProxyService instance.
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private IProxyService retrieveProxyService() {
        IProxyService proxyService = null;

        ServiceReference service = EmulatorPlugin.getDefault().getBundle().getBundleContext()
                .getServiceReference(IProxyService.class.getCanonicalName());
        if (service != null) {
            proxyService = (IProxyService) EmulatorPlugin.getDefault().getBundle().getBundleContext()
                    .getService(service);
        }

        return proxyService;
    }

    /**
     * Add the http-proxy parameter to the emulator command line.
     * 
     * @param cmdList
     *            List holding the commands to be called.
     */
    private void addEmulatorProxyParameter(List<String> cmdList) {
        IProxyService proxyService = retrieveProxyService();
        if (proxyService != null) {
            String host = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getHost();
            int port = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPort();
            boolean isAuthenticationRequired = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE)
                    .isRequiresAuthentication();
            String userId = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getUserId();
            String password = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPassword();

            // there must be a host in order to access via proxy
            if (host != null) {
                cmdList.add(EMULATOR_HTTP_PROXY_PARAMETER);

                // add proxy info to the command list - authentication needed
                if (isAuthenticationRequired) {
                    cmdList.add(PROXY_HTTP + userId + PROXY_COLON + password + PROXY_AT + host + PROXY_COLON
                            + Integer.valueOf(port).toString());
                }
                // add proxy info to the command list - no authentication needed
                else {
                    cmdList.add(PROXY_HTTP + host + PROXY_COLON + Integer.valueOf(port).toString());
                }
            }
        }
    }

    private boolean isEmulatorReady(String avdName) {
        String serialNum = DDMSFacade.getSerialNumberByName(avdName);
        return DDMSFacade.isDeviceOnline(serialNum);
    }
}