/*
* 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;
}
}