/* * 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; import static com.motorola.studio.android.common.log.StudioLogger.error; import static com.motorola.studio.android.common.log.StudioLogger.info; import java.util.List; import java.util.Properties; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.osgi.util.NLS; import org.eclipse.sequoyah.device.common.utilities.BasePlugin; import org.eclipse.sequoyah.device.framework.events.IInstanceListener; import org.eclipse.sequoyah.device.framework.events.InstanceAdapter; import org.eclipse.sequoyah.device.framework.events.InstanceEvent; import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType; import org.eclipse.sequoyah.device.framework.events.InstanceEventManager; import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry; import org.eclipse.sequoyah.device.framework.model.IDeviceType; import org.eclipse.sequoyah.device.framework.model.IService; import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler; import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler; import org.eclipse.sequoyah.device.framework.ui.DeviceUIPlugin; import org.eclipse.sequoyah.device.framework.ui.view.InstanceMgtView; import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import com.motorola.studio.android.AndroidPlugin; import com.motorola.studio.android.adt.DDMSFacade; import com.motorola.studio.android.adt.DdmsRunnable; import com.motorola.studio.android.adt.StudioAndroidEventManager; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.preferences.DialogWithToggleUtils; import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager; import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; import com.motorola.studio.android.emulator.device.AndroidDeviceUtils; import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants; import com.motorola.studio.android.emulator.device.SequoyahLogRedirector; import com.motorola.studio.android.emulator.device.instance.AndroidDevInstListener; import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance; import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh; import com.motorola.studio.android.emulator.device.sync.DeviceViewsSync; import com.motorola.studio.android.emulator.i18n.EmulatorNLS; import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView; /** * The activator class controls the plug-in life cycle */ public class EmulatorPlugin extends AbstractUIPlugin { // The plug-in ID public static final String PLUGIN_ID = "com.motorola.studio.android.emulator"; // The shared instance private static EmulatorPlugin plugin; // The ID of the device declared by this plug-in public static final String DEVICE_ID = PLUGIN_ID + ".androidDevice"; // The ID of all the status declared by this plug-in public static final String STATUS_ONLINE_ID = PLUGIN_ID + ".status.online"; public static final String STATUS_OFFLINE_NO_DATA = PLUGIN_ID + ".status.offlineNoData"; public static final String STATUS_OFFLINE = PLUGIN_ID + ".status.offline"; public static final String STATUS_NOT_AVAILABLE = PLUGIN_ID + ".status.notavailable"; public static final String SERVICE_INIT_ID = PLUGIN_ID + ".initEmulatorService"; public static final String STOP_SERVICE_ID = PLUGIN_ID + ".stopService"; public static final String START_SERVICE_ID = PLUGIN_ID + ".startService"; private static final String DEV_MANAGER_HELP = DeviceUIPlugin.PLUGIN_ID + ".devmgr"; private static final String NEW_DEVICE_HELP = DeviceUIPlugin.PLUGIN_ID + ".newdev"; /** * Reference the id of the extension point with the default Android Emulator definitions... */ public static String DEFAULT_EMULATOR_DEFINITION = "com.motorola.studio.android.emulator10.defaultEmulatorDefinitions"; public static final String FORCE_ATTR = "force"; public static final String EMULATOR_UNEXPECTEDLY_STOPPED = "emulator.unexpectedly.stopped"; private static AndroidDevInstListener instanceListener; private static DdmsRunnable connectedListener = new DdmsRunnable() { @Override public void run(String serialNumber) { if (DDMSFacade.isEmulator(serialNumber)) { InstancesListRefresh.refresh(); info("New Device connected at " + serialNumber); String vmName = DDMSFacade.getNameBySerialNumber(serialNumber); if (vmName != null) { DeviceFrameworkManager devFrameworkManager = DeviceFrameworkManager.getInstance(); IAndroidEmulatorInstance instance = devFrameworkManager.getInstanceByName(vmName); if (instance instanceof AndroidDeviceInstance) { final AndroidDeviceInstance emulatorInstance = (AndroidDeviceInstance) instance; AndroidDeviceUtils.fireDummyStartTransition(emulatorInstance, serialNumber); } } } } }; private static DdmsRunnable disconnectedListener = new DdmsRunnable() { @Override public void run(String serialNum) { if (DDMSFacade.isEmulator(serialNum)) { info("Device just disconnected from serial=" + serialNum); String vmName = DDMSFacade.getNameBySerialNumber(serialNum); if (vmName != null) { IAndroidEmulatorInstance instance = DeviceFrameworkManager.getInstance().getInstanceByName(vmName); if ((instance != null) && (instance.isStarted())) { try { instance.stop(true); DialogWithToggleUtils.showError(EMULATOR_UNEXPECTEDLY_STOPPED, EmulatorNLS.GEN_Error, NLS.bind( EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped, instance.getName())); } catch (Exception e) { error("Error trying to force the stop process on instance associated to disconnected device: " + instance); } } if (instance instanceof AndroidDeviceInstance) { ((AndroidDeviceInstance) instance).setNameSuffix(null); InstanceEventManager.getInstance().notifyListeners( new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, (AndroidDeviceInstance) instance)); } } else { // This block is executed if we get a vmName == null condition. This can happen if // ADT updates the device in a way that it makes the name not accessible. // // What is needed to be done in such a case is to iterate on all TmL instances, looking for // objects that contain serialNumber as the instance suffix. This guarantees that we will not // leave a not consistent serial number being displayed at the Instance Management view. for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance() .getAllInstances()) { if (instance instanceof AndroidDeviceInstance) { AndroidDeviceInstance androidInstance = (AndroidDeviceInstance) instance; String instanceSuffix = androidInstance.getNameSuffix(); if ((instanceSuffix != null) && instanceSuffix.equals(serialNum)) { androidInstance.setNameSuffix(null); InstanceEventManager.getInstance().notifyListeners( new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, androidInstance)); } } } } } } }; private static final Runnable sdkLoaderListener = new Runnable() { @Override public void run() { InstancesListRefresh.refresh(); if (!Platform.getOS().equals(Platform.OS_MACOSX)) { IPreferenceStore store = getDefault().getPreferenceStore(); boolean deviceStartupOptionsUpdated = store.getBoolean("DeviceStartupOptionsUpdated"); if (!deviceStartupOptionsUpdated) { for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance() .getAllInstances()) { if (instance instanceof AndroidDeviceInstance) { AndroidDeviceInstance androidInstance = (AndroidDeviceInstance) instance; Properties emuProperties = androidInstance.getProperties(); String commandline = emuProperties.getProperty( IDevicePropertiesConstants.commandline, ""); if (commandline.contains("-no-window")) { commandline = commandline.replace("-no-window", ""); } emuProperties.setProperty(IDevicePropertiesConstants.commandline, commandline); androidInstance.setProperties(emuProperties); InstanceEventManager.getInstance().notifyListeners( new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, androidInstance)); } } store.setValue("DeviceStartupOptionsUpdated", true); } } } }; private static IInstanceListener sequoyahInstanceListener = new InstanceAdapter() { @Override public void instanceUpdated(InstanceEvent e) { AbstractAndroidView.updateInstanceName(e.getInstance()); } }; private static ServiceHandler stopServiceHandler = null; private static ServiceHandler startServiceHandler = null; private static String stopServiceId = null; private static String startServiceId = null; /** * The constructor */ public EmulatorPlugin() { plugin = this; } /** * Activates the plug-in and initializes the logger * * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) */ @Override public void start(BundleContext context) throws Exception { StudioLogger.debug(EmulatorPlugin.class, "Starting MOTODEV Android Emulator Plugin..."); super.start(context); start(); StudioLogger.debug(EmulatorPlugin.class, "MOTODEV Android Emulator Plugin started."); } private void start() { // Setting the TmL logger to redirect logs to the logger controlled // by this class SequoyahLogRedirector tmlLogger = new SequoyahLogRedirector(); org.eclipse.sequoyah.vnc.utilities.logger.Logger.setLogger(tmlLogger); BasePlugin.getBaseDefault().setLogger(tmlLogger); instanceListener = new AndroidDevInstListener(); InstanceEventManager.getInstance().addInstanceListener(instanceListener); StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener, disconnectedListener); AndroidPlugin.getDefault().addSDKLoaderListener(sdkLoaderListener); // Emulator Views synchronization DeviceViewsSync.getInstance().initialize(); // Setting context sensitive help IDs for the TmL screens we use DefaultDeviceTypeMenuWizardPage.setHelpContextId(NEW_DEVICE_HELP); InstanceMgtView.setHelp(DEV_MANAGER_HELP); InstanceEventManager.getInstance().addInstanceListener(sequoyahInstanceListener); registerStopServiceId(STOP_SERVICE_ID); registerStartServiceId(START_SERVICE_ID); } /** * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ @Override public void stop(BundleContext context) throws Exception { AndroidPlugin.getDefault().removeSDKLoaderListener(sdkLoaderListener); InstanceEventManager.getInstance().removeInstanceListener(instanceListener); StudioAndroidEventManager.asyncRemoveDeviceChangeListeners(connectedListener, disconnectedListener); InstanceEventManager.getInstance().removeInstanceListener(sequoyahInstanceListener); unregisterStopServiceHandler(); unregisterStartServiceHandler(); plugin = null; super.stop(context); } /** * Registers a stop service id, through which the stop service handler will be found and * used to delegate stop action of the instances * if possible * * @param stopServiceId The stop service id to be registered */ public static void registerStopServiceId(String stopServiceId) { EmulatorPlugin.stopServiceId = stopServiceId; } /** * Unregisters the current stop service handler and stop service id. * * After this method is called, it will not be possible for the instance class to delegate the * stop action to a handler. */ public static void unregisterStopServiceHandler() { stopServiceHandler = null; stopServiceId = null; } /** * Retrieves the stop service handler. * * @return The currently registered stop service handler, or <null> if no handler is registered. */ public static ServiceHandler getStopServiceHandler() { if ((stopServiceHandler == null) && (stopServiceId != null)) { // find the appropriate stop service handler IDeviceType device = DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID); List<IService> services = device.getServices(); for (IService service : services) { IServiceHandler handler = service.getHandler(); if (handler.getService().getId().equals(stopServiceId)) { stopServiceHandler = (ServiceHandler) handler; break; } } } return stopServiceHandler; } /** * Registers a start service id, through which the stop service handler will be found and * used to delegate start action of the instances * if possible * * @param stopServiceId The stop service id to be registered */ public static void registerStartServiceId(String startServiceId) { EmulatorPlugin.startServiceId = startServiceId; } /** * Unregisters the current start service handler and stop service id. * * After this method is called, it will not be possible for the instance class to delegate the * start action to a handler. */ public static void unregisterStartServiceHandler() { startServiceHandler = null; startServiceId = null; } /** * Retrieves the start service handler. * * @return The currently registered start service handler, or <null> if no handler is registered. */ public static ServiceHandler getStartServiceHandler() { if ((startServiceHandler == null) && (startServiceId != null)) { // find the appropriate stop service handler IDeviceType device = DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID); List<IService> services = device.getServices(); for (IService service : services) { IServiceHandler handler = service.getHandler(); if (handler.getService().getId().equals(startServiceId)) { startServiceHandler = (ServiceHandler) handler; break; } } } return startServiceHandler; } /** * Returns the shared instance * * @return the shared instance */ public static EmulatorPlugin getDefault() { return plugin; } }