aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java876
1 files changed, 876 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java
new file mode 100644
index 000000000..e08080bf0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.ddms/src/com/android/ide/eclipse/ddms/DdmsPlugin.java
@@ -0,0 +1,876 @@
+/*
+ * Copyright (C) 2007 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.android.ide.eclipse.ddms;
+
+import com.android.annotations.NonNull;
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
+import com.android.ddmlib.Client;
+import com.android.ddmlib.DdmPreferences;
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.ILogOutput;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmuilib.DdmUiPreferences;
+import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
+import com.android.ddmuilib.StackTracePanel;
+import com.android.ddmuilib.console.DdmConsole;
+import com.android.ddmuilib.console.IDdmConsole;
+import com.android.ide.eclipse.ddms.i18n.Messages;
+import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener,
+ IUiSelectionListener, com.android.ddmuilib.StackTracePanel.ISourceRevealer {
+
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.android.ide.eclipse.ddms"; //$NON-NLS-1$
+
+ /** The singleton instance */
+ private static DdmsPlugin sPlugin;
+
+ /** Location of the adb command line executable */
+ private static String sAdbLocation;
+ private static String sToolsFolder;
+ private static String sHprofConverter;
+
+ private boolean mHasDebuggerConnectors;
+ /** debugger connectors for already running apps.
+ * Initialized from an extension point.
+ */
+ private IDebuggerConnector[] mDebuggerConnectors;
+ private ITraceviewLauncher[] mTraceviewLaunchers;
+ private List<IClientAction> mClientSpecificActions = null;
+
+ /** Console for DDMS log message */
+ private MessageConsole mDdmsConsole;
+
+ private IDevice mCurrentDevice;
+ private Client mCurrentClient;
+ private boolean mListeningToUiSelection = false;
+
+ private final ArrayList<ISelectionListener> mListeners = new ArrayList<ISelectionListener>();
+
+ private Color mRed;
+
+
+ /**
+ * Classes which implement this interface provide methods that deals
+ * with {@link IDevice} and {@link Client} selectionchanges.
+ */
+ public interface ISelectionListener {
+
+ /**
+ * Sent when a new {@link Client} is selected.
+ * @param selectedClient The selected client. If null, no clients are selected.
+ */
+ public void selectionChanged(Client selectedClient);
+
+ /**
+ * Sent when a new {@link IDevice} is selected.
+ * @param selectedDevice the selected device. If null, no devices are selected.
+ */
+ public void selectionChanged(IDevice selectedDevice);
+ }
+
+ /**
+ * The constructor
+ */
+ public DdmsPlugin() {
+ sPlugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+
+ final Display display = getDisplay();
+
+ // get the eclipse store
+ final IPreferenceStore eclipseStore = getPreferenceStore();
+
+ AndroidDebugBridge.addDeviceChangeListener(this);
+
+ DdmUiPreferences.setStore(eclipseStore);
+
+ //DdmUiPreferences.displayCharts();
+
+ // set the consoles.
+ mDdmsConsole = new MessageConsole("DDMS", null); //$NON-NLS-1$
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(
+ new IConsole[] {
+ mDdmsConsole
+ });
+
+ final MessageConsoleStream consoleStream = mDdmsConsole.newMessageStream();
+ final MessageConsoleStream errorConsoleStream = mDdmsConsole.newMessageStream();
+ mRed = new Color(display, 0xFF, 0x00, 0x00);
+
+ // because this can be run, in some cases, by a non UI thread, and because
+ // changing the console properties update the UI, we need to make this change
+ // in the UI thread.
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ errorConsoleStream.setColor(mRed);
+ }
+ });
+
+ // set up the ddms log to use the ddms console.
+ Log.setLogOutput(new ILogOutput() {
+ @Override
+ public void printLog(LogLevel logLevel, String tag, String message) {
+ if (logLevel.getPriority() >= LogLevel.ERROR.getPriority()) {
+ printToStream(errorConsoleStream, tag, message);
+ showConsoleView(mDdmsConsole);
+ } else {
+ printToStream(consoleStream, tag, message);
+ }
+ }
+
+ @Override
+ public void printAndPromptLog(final LogLevel logLevel, final String tag,
+ final String message) {
+ printLog(logLevel, tag, message);
+ // dialog box only run in UI thread..
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ Shell shell = display.getActiveShell();
+ if (logLevel == LogLevel.ERROR) {
+ MessageDialog.openError(shell, tag, message);
+ } else {
+ MessageDialog.openWarning(shell, tag, message);
+ }
+ }
+ });
+ }
+
+ });
+
+ // set up the ddms console to use this objects
+ DdmConsole.setConsole(new IDdmConsole() {
+ @Override
+ public void printErrorToConsole(String message) {
+ printToStream(errorConsoleStream, null, message);
+ showConsoleView(mDdmsConsole);
+ }
+ @Override
+ public void printErrorToConsole(String[] messages) {
+ for (String m : messages) {
+ printToStream(errorConsoleStream, null, m);
+ }
+ showConsoleView(mDdmsConsole);
+ }
+ @Override
+ public void printToConsole(String message) {
+ printToStream(consoleStream, null, message);
+ }
+ @Override
+ public void printToConsole(String[] messages) {
+ for (String m : messages) {
+ printToStream(consoleStream, null, m);
+ }
+ }
+ });
+
+ // set the listener for the preference change
+ eclipseStore.addPropertyChangeListener(new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ // get the name of the property that changed.
+ String property = event.getProperty();
+
+ if (PreferenceInitializer.ATTR_DEBUG_PORT_BASE.equals(property)) {
+ DdmPreferences.setDebugPortBase(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_DEBUG_PORT_BASE));
+ } else if (PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT.equals(property)) {
+ DdmPreferences.setSelectedDebugPort(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT));
+ } else if (PreferenceInitializer.ATTR_THREAD_INTERVAL.equals(property)) {
+ DdmUiPreferences.setThreadRefreshInterval(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_THREAD_INTERVAL));
+ } else if (PreferenceInitializer.ATTR_LOG_LEVEL.equals(property)) {
+ DdmPreferences.setLogLevel(
+ eclipseStore.getString(PreferenceInitializer.ATTR_LOG_LEVEL));
+ } else if (PreferenceInitializer.ATTR_TIME_OUT.equals(property)) {
+ DdmPreferences.setTimeOut(
+ eclipseStore.getInt(PreferenceInitializer.ATTR_TIME_OUT));
+ } else if (PreferenceInitializer.ATTR_USE_ADBHOST.equals(property)) {
+ DdmPreferences.setUseAdbHost(
+ eclipseStore.getBoolean(PreferenceInitializer.ATTR_USE_ADBHOST));
+ } else if (PreferenceInitializer.ATTR_ADBHOST_VALUE.equals(property)) {
+ DdmPreferences.setAdbHostValue(
+ eclipseStore.getString(PreferenceInitializer.ATTR_ADBHOST_VALUE));
+ }
+ }
+ });
+
+ // do some last initializations
+
+ // set the preferences.
+ PreferenceInitializer.setupPreferences();
+
+ // this class is set as the main source revealer and will look at all the implementations
+ // of the extension point. see #reveal(String, String, int)
+ StackTracePanel.setSourceRevealer(this);
+
+ /*
+ * Load the extension point implementations.
+ * The first step is to load the IConfigurationElement representing the implementations.
+ * The 2nd step is to use these objects to instantiate the implementation classes.
+ *
+ * Because the 2nd step will trigger loading the plug-ins providing the implementations,
+ * and those plug-ins could access DDMS classes (like ADT), this 2nd step should be done
+ * in a Job to ensure that DDMS is loaded, so that the other plug-ins can load.
+ *
+ * Both steps could be done in the 2nd step but some of DDMS UI rely on knowing if there
+ * is an implementation or not (DeviceView), so we do the first steps in start() and, in
+ * some case, record it.
+ *
+ */
+
+ // get the IConfigurationElement for the debuggerConnector right away.
+ final IConfigurationElement[] dcce = findConfigElements(
+ "com.android.ide.eclipse.ddms.debuggerConnector"); //$NON-NLS-1$
+ mHasDebuggerConnectors = dcce.length > 0;
+
+ // get the other configElements and instantiante them in a Job.
+ new Job(Messages.DdmsPlugin_DDMS_Post_Create_Init) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ // init the lib
+ AndroidDebugBridge.init(true /* debugger support */);
+
+ // get the available adb locators
+ IConfigurationElement[] elements = findConfigElements(
+ "com.android.ide.eclipse.ddms.toolsLocator"); //$NON-NLS-1$
+
+ IToolsLocator[] locators = instantiateToolsLocators(elements);
+
+ for (IToolsLocator locator : locators) {
+ try {
+ String adbLocation = locator.getAdbLocation();
+ String traceviewLocation = locator.getTraceViewLocation();
+ String hprofConvLocation = locator.getHprofConvLocation();
+ if (adbLocation != null && traceviewLocation != null &&
+ hprofConvLocation != null) {
+ // checks if the location is valid.
+ if (setToolsLocation(adbLocation, hprofConvLocation,
+ traceviewLocation)) {
+
+ AndroidDebugBridge.createBridge(sAdbLocation,
+ true /* forceNewBridge */);
+
+ // no need to look at the other locators.
+ break;
+ }
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+
+ // get the available debugger connectors
+ mDebuggerConnectors = instantiateDebuggerConnectors(dcce);
+
+ // get the available Traceview Launchers.
+ elements = findConfigElements("com.android.ide.eclipse.ddms.traceviewLauncher"); //$NON-NLS-1$
+ mTraceviewLaunchers = instantiateTraceviewLauncher(elements);
+
+ return Status.OK_STATUS;
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ }
+ }.schedule();
+ }
+
+ private void showConsoleView(MessageConsole console) {
+ ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
+ }
+
+
+ /** Obtain a list of configuration elements that extend the given extension point. */
+ IConfigurationElement[] findConfigElements(String extensionPointId) {
+ // get the adb location from an implementation of the ADB Locator extension point.
+ IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
+ IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(extensionPointId);
+ if (extensionPoint != null) {
+ return extensionPoint.getConfigurationElements();
+ }
+
+ // shouldn't happen or it means the plug-in is broken.
+ return new IConfigurationElement[0];
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called adbLocator.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private IToolsLocator[] instantiateToolsLocators(IConfigurationElement[] configElements)
+ throws CoreException {
+ ArrayList<IToolsLocator> list = new ArrayList<IToolsLocator>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof IToolsLocator) {
+ list.add((IToolsLocator) obj);
+ }
+ }
+
+ return list.toArray(new IToolsLocator[list.size()]);
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called debuggerConnector.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private IDebuggerConnector[] instantiateDebuggerConnectors(
+ IConfigurationElement[] configElements) throws CoreException {
+ ArrayList<IDebuggerConnector> list = new ArrayList<IDebuggerConnector>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof IDebuggerConnector) {
+ list.add((IDebuggerConnector) obj);
+ }
+ }
+
+ return list.toArray(new IDebuggerConnector[list.size()]);
+ }
+
+ /**
+ * Finds if any other plug-in is extending the exposed Extension Point called traceviewLauncher.
+ *
+ * @return an array of all locators found, or an empty array if none were found.
+ */
+ private ITraceviewLauncher[] instantiateTraceviewLauncher(
+ IConfigurationElement[] configElements)
+ throws CoreException {
+ ArrayList<ITraceviewLauncher> list = new ArrayList<ITraceviewLauncher>();
+
+ if (configElements.length > 0) {
+ // only use the first one, ignore the others.
+ IConfigurationElement configElement = configElements[0];
+
+ // instantiate the class
+ Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
+ if (obj instanceof ITraceviewLauncher) {
+ list.add((ITraceviewLauncher) obj);
+ }
+ }
+
+ return list.toArray(new ITraceviewLauncher[list.size()]);
+ }
+
+ /**
+ * Returns the classes that implement {@link IClientAction} in each of the extensions that
+ * extend clientAction extension point.
+ * @throws CoreException
+ */
+ private List<IClientAction> instantiateClientSpecificActions(IConfigurationElement[] elements)
+ throws CoreException {
+ if (elements == null || elements.length == 0) {
+ return Collections.emptyList();
+ }
+
+ List<IClientAction> extensions = new ArrayList<IClientAction>(1);
+
+ for (IConfigurationElement e : elements) {
+ Object o = e.createExecutableExtension("class"); //$NON-NLS-1$
+ if (o instanceof IClientAction) {
+ extensions.add((IClientAction) o);
+ }
+ }
+
+ return extensions;
+ }
+
+ public static Display getDisplay() {
+ IWorkbench bench = sPlugin.getWorkbench();
+ if (bench != null) {
+ return bench.getDisplay();
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ AndroidDebugBridge.removeDeviceChangeListener(this);
+
+ AndroidDebugBridge.terminate();
+
+ mRed.dispose();
+
+ sPlugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static DdmsPlugin getDefault() {
+ return sPlugin;
+ }
+
+ public static String getAdb() {
+ return sAdbLocation;
+ }
+
+ public static File getPlatformToolsFolder() {
+ return new File(sAdbLocation).getParentFile();
+ }
+
+ public static String getToolsFolder() {
+ return sToolsFolder;
+ }
+
+ public static String getHprofConverter() {
+ return sHprofConverter;
+ }
+
+ /**
+ * Stores the adb location. This returns true if the location is an existing file.
+ */
+ private static boolean setToolsLocation(String adbLocation, String hprofConvLocation,
+ String traceViewLocation) {
+
+ File adb = new File(adbLocation);
+ File hprofConverter = new File(hprofConvLocation);
+ File traceview = new File(traceViewLocation);
+
+ String missing = "";
+ if (adb.isFile() == false) {
+ missing += adb.getAbsolutePath() + " ";
+ }
+ if (hprofConverter.isFile() == false) {
+ missing += hprofConverter.getAbsolutePath() + " ";
+ }
+ if (traceview.isFile() == false) {
+ missing += traceview.getAbsolutePath() + " ";
+ }
+
+ if (missing.length() > 0) {
+ String msg = String.format("DDMS files not found: %1$s", missing);
+ Log.e("DDMS", msg);
+ Status status = new Status(IStatus.ERROR, PLUGIN_ID, msg, null /*exception*/);
+ getDefault().getLog().log(status);
+ return false;
+ }
+
+ sAdbLocation = adbLocation;
+ sHprofConverter = hprofConverter.getAbsolutePath();
+ DdmUiPreferences.setTraceviewLocation(traceview.getAbsolutePath());
+
+ sToolsFolder = traceview.getParent();
+
+ return true;
+ }
+
+ /**
+ * Set the location of the adb executable and optionally starts adb
+ * @param adb location of adb
+ * @param startAdb flag to start adb
+ */
+ public static void setToolsLocation(String adbLocation, boolean startAdb,
+ String hprofConvLocation, String traceViewLocation) {
+
+ if (setToolsLocation(adbLocation, hprofConvLocation, traceViewLocation)) {
+ // starts the server in a thread in case this is blocking.
+ if (startAdb) {
+ new Thread() {
+ @Override
+ public void run() {
+ // create and start the bridge
+ try {
+ AndroidDebugBridge.createBridge(sAdbLocation,
+ false /* forceNewBridge */);
+ } catch (Throwable t) {
+ Status status = new Status(IStatus.ERROR, PLUGIN_ID,
+ "Failed to create AndroidDebugBridge", t);
+ getDefault().getLog().log(status);
+ }
+ }
+ }.start();
+ }
+ }
+ }
+
+ /**
+ * Returns whether there are implementations of the debuggerConnectors extension point.
+ * <p/>
+ * This is guaranteed to return the correct value as soon as the plug-in is loaded.
+ */
+ public boolean hasDebuggerConnectors() {
+ return mHasDebuggerConnectors;
+ }
+
+ /**
+ * Returns the implementations of {@link IDebuggerConnector}.
+ * <p/>
+ * There may be a small amount of time right after the plug-in load where this can return
+ * null even if there are implementation.
+ * <p/>
+ * Since the use of the implementation likely require user input, the UI can use
+ * {@link #hasDebuggerConnectors()} to know if there are implementations before they are loaded.
+ */
+ public IDebuggerConnector[] getDebuggerConnectors() {
+ return mDebuggerConnectors;
+ }
+
+ public synchronized void addSelectionListener(ISelectionListener listener) {
+ mListeners.add(listener);
+
+ // notify the new listener of the current selection
+ listener.selectionChanged(mCurrentDevice);
+ listener.selectionChanged(mCurrentClient);
+ }
+
+ public synchronized void removeSelectionListener(ISelectionListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public synchronized void setListeningState(boolean state) {
+ mListeningToUiSelection = state;
+ }
+
+ /**
+ * Sent when the a device is connected to the {@link AndroidDebugBridge}.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the new device.
+ *
+ * @see IDeviceChangeListener#deviceConnected(IDevice)
+ */
+ @Override
+ public void deviceConnected(IDevice device) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+ if (mCurrentDevice == null) {
+ handleDefaultSelection(device);
+ }
+ }
+ }
+
+ /**
+ * Sent when the a device is disconnected to the {@link AndroidDebugBridge}.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the new device.
+ *
+ * @see IDeviceChangeListener#deviceDisconnected(IDevice)
+ */
+ @Override
+ public void deviceDisconnected(IDevice device) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+ // test if the disconnected device was the default selection.
+ if (mCurrentDevice == device) {
+ // try to find a new device
+ AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
+ if (bridge != null) {
+ // get the device list
+ IDevice[] devices = bridge.getDevices();
+
+ // check if we still have devices
+ if (devices.length == 0) {
+ handleDefaultSelection((IDevice)null);
+ } else {
+ handleDefaultSelection(devices[0]);
+ }
+ } else {
+ handleDefaultSelection((IDevice)null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sent when a device data changed, or when clients are started/terminated on the device.
+ * <p/>
+ * This is sent from a non UI thread.
+ * @param device the device that was updated.
+ * @param changeMask the mask indicating what changed.
+ *
+ * @see IDeviceChangeListener#deviceChanged(IDevice)
+ */
+ @Override
+ public void deviceChanged(IDevice device, int changeMask) {
+ // if we are listening to selection coming from the ui, then we do nothing, as
+ // any change in the devices/clients, will be handled by the UI, and we'll receive
+ // selection notification through our implementation of IUiSelectionListener.
+ if (mListeningToUiSelection == false) {
+
+ // check if this is our device
+ if (device == mCurrentDevice) {
+ if (mCurrentClient == null) {
+ handleDefaultSelection(device);
+ } else {
+ // get the clients and make sure ours is still in there.
+ Client[] clients = device.getClients();
+ boolean foundClient = false;
+ for (Client client : clients) {
+ if (client == mCurrentClient) {
+ foundClient = true;
+ break;
+ }
+ }
+
+ // if we haven't found our client, lets look for a new one
+ if (foundClient == false) {
+ mCurrentClient = null;
+ handleDefaultSelection(device);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sent when a new {@link IDevice} and {@link Client} are selected.
+ * @param selectedDevice the selected device. If null, no devices are selected.
+ * @param selectedClient The selected client. If null, no clients are selected.
+ */
+ @Override
+ public synchronized void selectionChanged(IDevice selectedDevice, Client selectedClient) {
+ if (mCurrentDevice != selectedDevice) {
+ mCurrentDevice = selectedDevice;
+
+ // notify of the new default device
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentDevice);
+ }
+ }
+
+ if (mCurrentClient != selectedClient) {
+ mCurrentClient = selectedClient;
+
+ // notify of the new default client
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentClient);
+ }
+ }
+ }
+
+ /**
+ * Handles a default selection of a {@link IDevice} and {@link Client}.
+ * @param device the selected device
+ */
+ private void handleDefaultSelection(final IDevice device) {
+ // because the listener expect to receive this from the UI thread, and this is called
+ // from the AndroidDebugBridge notifications, we need to run this in the UI thread.
+ try {
+ Display display = getDisplay();
+
+ display.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ // set the new device if different.
+ boolean newDevice = false;
+ if (mCurrentDevice != device) {
+ mCurrentDevice = device;
+ newDevice = true;
+
+ // notify of the new default device
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentDevice);
+ }
+ }
+
+ if (device != null) {
+ // if this is a device switch or the same device but we didn't find a valid
+ // client the last time, we go look for a client to use again.
+ if (newDevice || mCurrentClient == null) {
+ // now get the new client
+ Client[] clients = device.getClients();
+ if (clients.length > 0) {
+ handleDefaultSelection(clients[0]);
+ } else {
+ handleDefaultSelection((Client)null);
+ }
+ }
+ } else {
+ handleDefaultSelection((Client)null);
+ }
+ }
+ });
+ } catch (SWTException e) {
+ // display is disposed. Do nothing since we're quitting anyway.
+ }
+ }
+
+ private void handleDefaultSelection(Client client) {
+ mCurrentClient = client;
+
+ // notify of the new default client
+ for (ISelectionListener listener : mListeners) {
+ listener.selectionChanged(mCurrentClient);
+ }
+ }
+
+ /**
+ * Prints a message, associated with a project to the specified stream
+ * @param stream The stream to write to
+ * @param tag The tag associated to the message. Can be null
+ * @param message The message to print.
+ */
+ private static synchronized void printToStream(MessageConsoleStream stream, String tag,
+ String message) {
+ String dateTag = getMessageTag(tag);
+
+ stream.print(dateTag);
+ if (!dateTag.endsWith(" ")) {
+ stream.print(" "); //$NON-NLS-1$
+ }
+ stream.println(message);
+ }
+
+ /**
+ * Creates a string containing the current date/time, and the tag
+ * @param tag The tag associated to the message. Can be null
+ * @return The dateTag
+ */
+ private static String getMessageTag(String tag) {
+ Calendar c = Calendar.getInstance();
+
+ if (tag == null) {
+ return String.format(Messages.DdmsPlugin_Message_Tag_Mask_1, c);
+ }
+
+ return String.format(Messages.DdmsPlugin_Message_Tag_Mask_2, c, tag);
+ }
+
+ /**
+ * Implementation of com.android.ddmuilib.StackTracePanel.ISourceRevealer.
+ */
+ @Override
+ public void reveal(String applicationName, String className, int line) {
+ JavaSourceRevealer.reveal(applicationName, className, line);
+ }
+
+ public boolean launchTraceview(String osPath) {
+ if (mTraceviewLaunchers != null) {
+ for (ITraceviewLauncher launcher : mTraceviewLaunchers) {
+ try {
+ if (launcher.openFile(osPath)) {
+ return true;
+ }
+ } catch (Throwable t) {
+ // ignore, we'll just not use this implementation.
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the list of clients that extend the clientAction extension point.
+ */
+ @NonNull
+ public synchronized List<IClientAction> getClientSpecificActions() {
+ if (mClientSpecificActions == null) {
+ // get available client specific action extensions
+ IConfigurationElement[] elements =
+ findConfigElements("com.android.ide.eclipse.ddms.clientAction"); //$NON-NLS-1$
+ try {
+ mClientSpecificActions = instantiateClientSpecificActions(elements);
+ } catch (CoreException e) {
+ mClientSpecificActions = Collections.emptyList();
+ }
+ }
+
+ return mClientSpecificActions;
+ }
+
+ private LogCatMonitor mLogCatMonitor;
+ public void startLogCatMonitor(IDevice device) {
+ if (mLogCatMonitor == null) {
+ mLogCatMonitor = new LogCatMonitor(getDebuggerConnectors(), getPreferenceStore());
+ }
+
+ mLogCatMonitor.monitorDevice(device);
+ }
+
+ /** Returns an image descriptor for the image file at the given plug-in relative path */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ return imageDescriptorFromPlugin(PLUGIN_ID, path);
+ }
+}