• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.ddms;
18 
19 import com.android.ddmlib.AndroidDebugBridge;
20 import com.android.ddmlib.Client;
21 import com.android.ddmlib.DdmPreferences;
22 import com.android.ddmlib.IDevice;
23 import com.android.ddmlib.Log;
24 import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener;
25 import com.android.ddmlib.Log.ILogOutput;
26 import com.android.ddmlib.Log.LogLevel;
27 import com.android.ddmuilib.DdmUiPreferences;
28 import com.android.ddmuilib.StackTracePanel;
29 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
30 import com.android.ide.eclipse.ddms.i18n.Messages;
31 import com.android.ide.eclipse.ddms.preferences.PreferenceInitializer;
32 
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IConfigurationElement;
35 import org.eclipse.core.runtime.IExtensionPoint;
36 import org.eclipse.core.runtime.IExtensionRegistry;
37 import org.eclipse.core.runtime.IProgressMonitor;
38 import org.eclipse.core.runtime.IStatus;
39 import org.eclipse.core.runtime.Platform;
40 import org.eclipse.core.runtime.Status;
41 import org.eclipse.core.runtime.jobs.Job;
42 import org.eclipse.jface.dialogs.MessageDialog;
43 import org.eclipse.jface.preference.IPreferenceStore;
44 import org.eclipse.jface.util.IPropertyChangeListener;
45 import org.eclipse.jface.util.PropertyChangeEvent;
46 import org.eclipse.swt.SWTException;
47 import org.eclipse.swt.graphics.Color;
48 import org.eclipse.swt.widgets.Display;
49 import org.eclipse.swt.widgets.Shell;
50 import org.eclipse.ui.IWorkbench;
51 import org.eclipse.ui.console.ConsolePlugin;
52 import org.eclipse.ui.console.IConsole;
53 import org.eclipse.ui.console.MessageConsole;
54 import org.eclipse.ui.console.MessageConsoleStream;
55 import org.eclipse.ui.plugin.AbstractUIPlugin;
56 import org.osgi.framework.BundleContext;
57 
58 import java.io.File;
59 import java.util.ArrayList;
60 import java.util.Calendar;
61 
62 /**
63  * The activator class controls the plug-in life cycle
64  */
65 public final class DdmsPlugin extends AbstractUIPlugin implements IDeviceChangeListener,
66         IUiSelectionListener, com.android.ddmuilib.StackTracePanel.ISourceRevealer {
67 
68 
69     // The plug-in ID
70     public static final String PLUGIN_ID = "com.android.ide.eclipse.ddms"; //$NON-NLS-1$
71 
72     /** The singleton instance */
73     private static DdmsPlugin sPlugin;
74 
75     /** Location of the adb command line executable */
76     private static String sAdbLocation;
77     private static String sToolsFolder;
78     private static String sHprofConverter;
79 
80     private boolean mHasDebuggerConnectors;
81     /** debugger connectors for already running apps.
82      * Initialized from an extension point.
83      */
84     private IDebuggerConnector[] mDebuggerConnectors;
85     private ISourceRevealer[] mSourceRevealers;
86     private ITraceviewLauncher[] mTraceviewLaunchers;
87 
88 
89     /** Console for DDMS log message */
90     private MessageConsole mDdmsConsole;
91 
92     private IDevice mCurrentDevice;
93     private Client mCurrentClient;
94     private boolean mListeningToUiSelection = false;
95 
96     private final ArrayList<ISelectionListener> mListeners = new ArrayList<ISelectionListener>();
97 
98     private Color mRed;
99 
100 
101     /**
102      * Classes which implement this interface provide methods that deals
103      * with {@link IDevice} and {@link Client} selectionchanges.
104      */
105     public interface ISelectionListener {
106 
107         /**
108          * Sent when a new {@link Client} is selected.
109          * @param selectedClient The selected client. If null, no clients are selected.
110          */
selectionChanged(Client selectedClient)111         public void selectionChanged(Client selectedClient);
112 
113         /**
114          * Sent when a new {@link IDevice} is selected.
115          * @param selectedDevice the selected device. If null, no devices are selected.
116          */
selectionChanged(IDevice selectedDevice)117         public void selectionChanged(IDevice selectedDevice);
118     }
119 
120     /**
121      * The constructor
122      */
DdmsPlugin()123     public DdmsPlugin() {
124         sPlugin = this;
125     }
126 
127     /*
128      * (non-Javadoc)
129      *
130      * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
131      */
132     @Override
start(BundleContext context)133     public void start(BundleContext context) throws Exception {
134         super.start(context);
135 
136         final Display display = getDisplay();
137 
138         // get the eclipse store
139         final IPreferenceStore eclipseStore = getPreferenceStore();
140 
141         AndroidDebugBridge.addDeviceChangeListener(this);
142 
143         DdmUiPreferences.setStore(eclipseStore);
144 
145         //DdmUiPreferences.displayCharts();
146 
147         // set the consoles.
148         mDdmsConsole = new MessageConsole("DDMS", null); //$NON-NLS-1$
149         ConsolePlugin.getDefault().getConsoleManager().addConsoles(
150                 new IConsole[] {
151                     mDdmsConsole
152                 });
153 
154         final MessageConsoleStream consoleStream = mDdmsConsole.newMessageStream();
155         final MessageConsoleStream errorConsoleStream = mDdmsConsole.newMessageStream();
156         mRed = new Color(display, 0xFF, 0x00, 0x00);
157 
158         // because this can be run, in some cases, by a non UI thread, and because
159         // changing the console properties update the UI, we need to make this change
160         // in the UI thread.
161         display.asyncExec(new Runnable() {
162             public void run() {
163                 errorConsoleStream.setColor(mRed);
164             }
165         });
166 
167         // set up the ddms log to use the ddms console.
168         Log.setLogOutput(new ILogOutput() {
169             public void printLog(LogLevel logLevel, String tag, String message) {
170                 if (logLevel.getPriority() >= LogLevel.ERROR.getPriority()) {
171                     printToStream(errorConsoleStream, tag, message);
172                     ConsolePlugin.getDefault().getConsoleManager().showConsoleView(mDdmsConsole);
173                 } else {
174                     printToStream(consoleStream, tag, message);
175                 }
176             }
177 
178             public void printAndPromptLog(final LogLevel logLevel, final String tag,
179                     final String message) {
180                 printLog(logLevel, tag, message);
181                 // dialog box only run in UI thread..
182                 display.asyncExec(new Runnable() {
183                     public void run() {
184                         Shell shell = display.getActiveShell();
185                         if (logLevel == LogLevel.ERROR) {
186                             MessageDialog.openError(shell, tag, message);
187                         } else {
188                             MessageDialog.openWarning(shell, tag, message);
189                         }
190                     }
191                 });
192             }
193 
194         });
195 
196         // set the listener for the preference change
197         eclipseStore.addPropertyChangeListener(new IPropertyChangeListener() {
198             public void propertyChange(PropertyChangeEvent event) {
199                 // get the name of the property that changed.
200                 String property = event.getProperty();
201 
202                 if (PreferenceInitializer.ATTR_DEBUG_PORT_BASE.equals(property)) {
203                     DdmPreferences.setDebugPortBase(
204                             eclipseStore.getInt(PreferenceInitializer.ATTR_DEBUG_PORT_BASE));
205                 } else if (PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT.equals(property)) {
206                     DdmPreferences.setSelectedDebugPort(
207                             eclipseStore.getInt(PreferenceInitializer.ATTR_SELECTED_DEBUG_PORT));
208                 } else if (PreferenceInitializer.ATTR_THREAD_INTERVAL.equals(property)) {
209                     DdmUiPreferences.setThreadRefreshInterval(
210                             eclipseStore.getInt(PreferenceInitializer.ATTR_THREAD_INTERVAL));
211                 } else if (PreferenceInitializer.ATTR_LOG_LEVEL.equals(property)) {
212                     DdmPreferences.setLogLevel(
213                             eclipseStore.getString(PreferenceInitializer.ATTR_LOG_LEVEL));
214                 } else if (PreferenceInitializer.ATTR_TIME_OUT.equals(property)) {
215                     DdmPreferences.setTimeOut(
216                             eclipseStore.getInt(PreferenceInitializer.ATTR_TIME_OUT));
217                 } else if (PreferenceInitializer.ATTR_USE_ADBHOST.equals(property)) {
218                     DdmPreferences.setUseAdbHost(
219                             eclipseStore.getBoolean(PreferenceInitializer.ATTR_USE_ADBHOST));
220                 } else if (PreferenceInitializer.ATTR_ADBHOST_VALUE.equals(property)) {
221                     DdmPreferences.setAdbHostValue(
222                             eclipseStore.getString(PreferenceInitializer.ATTR_ADBHOST_VALUE));
223                 }
224             }
225         });
226 
227         // do some last initializations
228 
229         // set the preferences.
230         PreferenceInitializer.setupPreferences();
231 
232         // this class is set as the main source revealer and will look at all the implementations
233         // of the extension point. see #reveal(String, String, int)
234         StackTracePanel.setSourceRevealer(this);
235 
236         /*
237          * Load the extension point implementations.
238          * The first step is to load the IConfigurationElement representing the implementations.
239          * The 2nd step is to use these objects to instantiate the implementation classes.
240          *
241          * Because the 2nd step will trigger loading the plug-ins providing the implementations,
242          * and those plug-ins could access DDMS classes (like ADT), this 2nd step should be done
243          * in a Job to ensure that DDMS is loaded, so that the other plug-ins can load.
244          *
245          * Both steps could be done in the 2nd step but some of DDMS UI rely on knowing if there
246          * is an implementation or not (DeviceView), so we do the first steps in start() and, in
247          * some case, record it.
248          *
249          */
250 
251         // get the IConfigurationElement for the debuggerConnector right away.
252         final IConfigurationElement[] dcce = findConfigElements(
253                 "com.android.ide.eclipse.ddms.debuggerConnector"); //$NON-NLS-1$
254         mHasDebuggerConnectors = dcce.length > 0;
255 
256         // get the other configElements and instantiante them in a Job.
257         new Job(Messages.DdmsPlugin_DDMS_Post_Create_Init) {
258             @Override
259             protected IStatus run(IProgressMonitor monitor) {
260                 try {
261                     // init the lib
262                     AndroidDebugBridge.init(true /* debugger support */);
263 
264                     // get the available adb locators
265                     IConfigurationElement[] elements = findConfigElements(
266                             "com.android.ide.eclipse.ddms.toolsLocator"); //$NON-NLS-1$
267 
268                     IToolsLocator[] locators = instantiateToolsLocators(elements);
269 
270                     for (IToolsLocator locator : locators) {
271                         try {
272                             String adbLocation = locator.getAdbLocation();
273                             String traceviewLocation = locator.getTraceViewLocation();
274                             String hprofConvLocation = locator.getHprofConvLocation();
275                             if (adbLocation != null && traceviewLocation != null &&
276                                     hprofConvLocation != null) {
277                                 // checks if the location is valid.
278                                 if (setToolsLocation(adbLocation, hprofConvLocation,
279                                         traceviewLocation)) {
280 
281                                     AndroidDebugBridge.createBridge(sAdbLocation,
282                                             true /* forceNewBridge */);
283 
284                                     // no need to look at the other locators.
285                                     break;
286                                 }
287                             }
288                         } catch (Throwable t) {
289                             // ignore, we'll just not use this implementation.
290                         }
291                     }
292 
293                     // get the available debugger connectors
294                     mDebuggerConnectors = instantiateDebuggerConnectors(dcce);
295 
296                     // get the available source revealers
297                     elements = findConfigElements("com.android.ide.eclipse.ddms.sourceRevealer"); //$NON-NLS-1$
298                     mSourceRevealers = instantiateSourceRevealers(elements);
299 
300                     // get the available Traceview Launchers.
301                     elements = findConfigElements("com.android.ide.eclipse.ddms.traceviewLauncher"); //$NON-NLS-1$
302                     mTraceviewLaunchers = instantiateTraceviewLauncher(elements);
303 
304                     return Status.OK_STATUS;
305                 } catch (CoreException e) {
306                     return e.getStatus();
307                 }
308             }
309         }.schedule();
310     }
311 
findConfigElements(String name)312     private IConfigurationElement[] findConfigElements(String name) {
313 
314         // get the adb location from an implementation of the ADB Locator extension point.
315         IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
316         IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(name);
317         if (extensionPoint != null) {
318             return extensionPoint.getConfigurationElements();
319         }
320 
321         // shouldn't happen or it means the plug-in is broken.
322         return new IConfigurationElement[0];
323     }
324 
325     /**
326      * Finds if any other plug-in is extending the exposed Extension Point called adbLocator.
327      *
328      * @return an array of all locators found, or an empty array if none were found.
329      */
instantiateToolsLocators(IConfigurationElement[] configElements)330     private IToolsLocator[] instantiateToolsLocators(IConfigurationElement[] configElements)
331             throws CoreException {
332         ArrayList<IToolsLocator> list = new ArrayList<IToolsLocator>();
333 
334         if (configElements.length > 0) {
335             // only use the first one, ignore the others.
336             IConfigurationElement configElement = configElements[0];
337 
338             // instantiate the class
339             Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
340             if (obj instanceof IToolsLocator) {
341                 list.add((IToolsLocator) obj);
342             }
343         }
344 
345         return list.toArray(new IToolsLocator[list.size()]);
346     }
347 
348     /**
349      * Finds if any other plug-in is extending the exposed Extension Point called debuggerConnector.
350      *
351      * @return an array of all locators found, or an empty array if none were found.
352      */
instantiateDebuggerConnectors( IConfigurationElement[] configElements)353     private IDebuggerConnector[] instantiateDebuggerConnectors(
354             IConfigurationElement[] configElements) throws CoreException {
355         ArrayList<IDebuggerConnector> list = new ArrayList<IDebuggerConnector>();
356 
357         if (configElements.length > 0) {
358             // only use the first one, ignore the others.
359             IConfigurationElement configElement = configElements[0];
360 
361             // instantiate the class
362             Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
363             if (obj instanceof IDebuggerConnector) {
364                 list.add((IDebuggerConnector) obj);
365             }
366         }
367 
368         return list.toArray(new IDebuggerConnector[list.size()]);
369     }
370 
371     /**
372      * Finds if any other plug-in is extending the exposed Extension Point called sourceRevealer.
373      *
374      * @return an array of all locators found, or an empty array if none were found.
375      */
instantiateSourceRevealers(IConfigurationElement[] configElements)376     private ISourceRevealer[] instantiateSourceRevealers(IConfigurationElement[] configElements)
377             throws CoreException {
378         ArrayList<ISourceRevealer> list = new ArrayList<ISourceRevealer>();
379 
380         if (configElements.length > 0) {
381             // only use the first one, ignore the others.
382             IConfigurationElement configElement = configElements[0];
383 
384             // instantiate the class
385             Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
386             if (obj instanceof ISourceRevealer) {
387                 list.add((ISourceRevealer) obj);
388             }
389         }
390 
391         return list.toArray(new ISourceRevealer[list.size()]);
392     }
393 
394     /**
395      * Finds if any other plug-in is extending the exposed Extension Point called traceviewLauncher.
396      *
397      * @return an array of all locators found, or an empty array if none were found.
398      */
instantiateTraceviewLauncher( IConfigurationElement[] configElements)399     private ITraceviewLauncher[] instantiateTraceviewLauncher(
400             IConfigurationElement[] configElements)
401             throws CoreException {
402         ArrayList<ITraceviewLauncher> list = new ArrayList<ITraceviewLauncher>();
403 
404         if (configElements.length > 0) {
405             // only use the first one, ignore the others.
406             IConfigurationElement configElement = configElements[0];
407 
408             // instantiate the class
409             Object obj = configElement.createExecutableExtension("class"); //$NON-NLS-1$
410             if (obj instanceof ITraceviewLauncher) {
411                 list.add((ITraceviewLauncher) obj);
412             }
413         }
414 
415         return list.toArray(new ITraceviewLauncher[list.size()]);
416     }
417 
getDisplay()418     public static Display getDisplay() {
419         IWorkbench bench = sPlugin.getWorkbench();
420         if (bench != null) {
421             return bench.getDisplay();
422         }
423         return null;
424     }
425 
426     /*
427      * (non-Javadoc)
428      *
429      * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
430      */
431     @Override
stop(BundleContext context)432     public void stop(BundleContext context) throws Exception {
433         AndroidDebugBridge.removeDeviceChangeListener(this);
434 
435         AndroidDebugBridge.terminate();
436 
437         mRed.dispose();
438 
439         sPlugin = null;
440         super.stop(context);
441     }
442 
443     /**
444      * Returns the shared instance
445      *
446      * @return the shared instance
447      */
getDefault()448     public static DdmsPlugin getDefault() {
449         return sPlugin;
450     }
451 
getAdb()452     public static String getAdb() {
453         return sAdbLocation;
454     }
455 
getToolsFolder2()456     public static String getToolsFolder2() {
457         return sToolsFolder;
458     }
459 
getHprofConverter()460     public static String getHprofConverter() {
461         return sHprofConverter;
462     }
463 
464     /**
465      * Stores the adb location. This returns true if the location is an existing file.
466      */
setToolsLocation(String adbLocation, String hprofConvLocation, String traceViewLocation)467     private static boolean setToolsLocation(String adbLocation, String hprofConvLocation,
468             String traceViewLocation) {
469 
470         File adb = new File(adbLocation);
471         File hprofConverter = new File(hprofConvLocation);
472         File traceview = new File(traceViewLocation);
473 
474         String missing = "";
475         if (adb.isFile() == false) {
476             missing += adb.getAbsolutePath() + " ";
477         }
478         if (hprofConverter.isFile() == false) {
479             missing += hprofConverter.getAbsolutePath() + " ";
480         }
481         if (traceview.isFile() == false) {
482             missing += traceview.getAbsolutePath() + " ";
483         }
484 
485         if (missing.length() > 0) {
486             String msg = String.format("DDMS files not found: %1$s", missing);
487             Log.e("DDMS", msg);
488             Status status = new Status(IStatus.ERROR, PLUGIN_ID, msg, null /*exception*/);
489             getDefault().getLog().log(status);
490             return false;
491         }
492 
493         sAdbLocation = adbLocation;
494         sHprofConverter = hprofConverter.getAbsolutePath();
495         DdmUiPreferences.setTraceviewLocation(traceview.getAbsolutePath());
496 
497         return true;
498     }
499 
500     /**
501      * Set the location of the adb executable and optionally starts adb
502      * @param adb location of adb
503      * @param startAdb flag to start adb
504      */
setToolsLocation(String adbLocation, boolean startAdb, String hprofConvLocation, String traceViewLocation)505     public static void setToolsLocation(String adbLocation, boolean startAdb,
506             String hprofConvLocation, String traceViewLocation) {
507 
508         if (setToolsLocation(adbLocation, hprofConvLocation, traceViewLocation)) {
509             // starts the server in a thread in case this is blocking.
510             if (startAdb) {
511                 new Thread() {
512                     @Override
513                     public void run() {
514                         // create and start the bridge
515                         try {
516                             AndroidDebugBridge.createBridge(sAdbLocation,
517                                     false /* forceNewBridge */);
518                         } catch (Throwable t) {
519                             Status status = new Status(IStatus.ERROR, PLUGIN_ID,
520                                     "Failed to create AndroidDebugBridge", t);
521                             getDefault().getLog().log(status);
522                         }
523                     }
524                 }.start();
525             }
526         }
527     }
528 
529     /**
530      * Returns whether there are implementations of the debuggerConnectors extension point.
531      * <p/>
532      * This is guaranteed to return the correct value as soon as the plug-in is loaded.
533      */
hasDebuggerConnectors()534     public boolean hasDebuggerConnectors() {
535         return mHasDebuggerConnectors;
536     }
537 
538     /**
539      * Returns the implementations of {@link IDebuggerConnector}.
540      * <p/>
541      * There may be a small amount of time right after the plug-in load where this can return
542      * null even if there are implementation.
543      * <p/>
544      * Since the use of the implementation likely require user input, the UI can use
545      * {@link #hasDebuggerConnectors()} to know if there are implementations before they are loaded.
546      */
getDebuggerConnectors()547     public IDebuggerConnector[] getDebuggerConnectors() {
548         return mDebuggerConnectors;
549     }
550 
addSelectionListener(ISelectionListener listener)551     public synchronized void addSelectionListener(ISelectionListener listener) {
552         mListeners.add(listener);
553 
554         // notify the new listener of the current selection
555        listener.selectionChanged(mCurrentDevice);
556        listener.selectionChanged(mCurrentClient);
557     }
558 
removeSelectionListener(ISelectionListener listener)559     public synchronized void removeSelectionListener(ISelectionListener listener) {
560         mListeners.remove(listener);
561     }
562 
setListeningState(boolean state)563     public synchronized void setListeningState(boolean state) {
564         mListeningToUiSelection = state;
565     }
566 
567     /**
568      * Sent when the a device is connected to the {@link AndroidDebugBridge}.
569      * <p/>
570      * This is sent from a non UI thread.
571      * @param device the new device.
572      *
573      * @see IDeviceChangeListener#deviceConnected(IDevice)
574      */
deviceConnected(IDevice device)575     public void deviceConnected(IDevice device) {
576         // if we are listening to selection coming from the ui, then we do nothing, as
577         // any change in the devices/clients, will be handled by the UI, and we'll receive
578         // selection notification through our implementation of IUiSelectionListener.
579         if (mListeningToUiSelection == false) {
580             if (mCurrentDevice == null) {
581                 handleDefaultSelection(device);
582             }
583         }
584     }
585 
586     /**
587      * Sent when the a device is disconnected to the {@link AndroidDebugBridge}.
588      * <p/>
589      * This is sent from a non UI thread.
590      * @param device the new device.
591      *
592      * @see IDeviceChangeListener#deviceDisconnected(IDevice)
593      */
deviceDisconnected(IDevice device)594     public void deviceDisconnected(IDevice device) {
595         // if we are listening to selection coming from the ui, then we do nothing, as
596         // any change in the devices/clients, will be handled by the UI, and we'll receive
597         // selection notification through our implementation of IUiSelectionListener.
598         if (mListeningToUiSelection == false) {
599             // test if the disconnected device was the default selection.
600             if (mCurrentDevice == device) {
601                 // try to find a new device
602                 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
603                 if (bridge != null) {
604                     // get the device list
605                     IDevice[] devices = bridge.getDevices();
606 
607                     // check if we still have devices
608                     if (devices.length == 0) {
609                         handleDefaultSelection((IDevice)null);
610                     } else {
611                         handleDefaultSelection(devices[0]);
612                     }
613                 } else {
614                     handleDefaultSelection((IDevice)null);
615                 }
616             }
617         }
618     }
619 
620     /**
621      * Sent when a device data changed, or when clients are started/terminated on the device.
622      * <p/>
623      * This is sent from a non UI thread.
624      * @param device the device that was updated.
625      * @param changeMask the mask indicating what changed.
626      *
627      * @see IDeviceChangeListener#deviceChanged(IDevice)
628      */
deviceChanged(IDevice device, int changeMask)629     public void deviceChanged(IDevice device, int changeMask) {
630         // if we are listening to selection coming from the ui, then we do nothing, as
631         // any change in the devices/clients, will be handled by the UI, and we'll receive
632         // selection notification through our implementation of IUiSelectionListener.
633         if (mListeningToUiSelection == false) {
634 
635             // check if this is our device
636             if (device == mCurrentDevice) {
637                 if (mCurrentClient == null) {
638                     handleDefaultSelection(device);
639                 } else {
640                     // get the clients and make sure ours is still in there.
641                     Client[] clients = device.getClients();
642                     boolean foundClient = false;
643                     for (Client client : clients) {
644                         if (client == mCurrentClient) {
645                             foundClient = true;
646                             break;
647                         }
648                     }
649 
650                     // if we haven't found our client, lets look for a new one
651                     if (foundClient == false) {
652                         mCurrentClient = null;
653                         handleDefaultSelection(device);
654                     }
655                 }
656             }
657         }
658     }
659 
660     /**
661      * Sent when a new {@link IDevice} and {@link Client} are selected.
662      * @param selectedDevice the selected device. If null, no devices are selected.
663      * @param selectedClient The selected client. If null, no clients are selected.
664      */
selectionChanged(IDevice selectedDevice, Client selectedClient)665     public synchronized void selectionChanged(IDevice selectedDevice, Client selectedClient) {
666         if (mCurrentDevice != selectedDevice) {
667             mCurrentDevice = selectedDevice;
668 
669             // notify of the new default device
670             for (ISelectionListener listener : mListeners) {
671                 listener.selectionChanged(mCurrentDevice);
672             }
673         }
674 
675         if (mCurrentClient != selectedClient) {
676             mCurrentClient = selectedClient;
677 
678             // notify of the new default client
679             for (ISelectionListener listener : mListeners) {
680                 listener.selectionChanged(mCurrentClient);
681             }
682         }
683     }
684 
685     /**
686      * Handles a default selection of a {@link IDevice} and {@link Client}.
687      * @param device the selected device
688      */
handleDefaultSelection(final IDevice device)689     private void handleDefaultSelection(final IDevice device) {
690         // because the listener expect to receive this from the UI thread, and this is called
691         // from the AndroidDebugBridge notifications, we need to run this in the UI thread.
692         try {
693             Display display = getDisplay();
694 
695             display.asyncExec(new Runnable() {
696                 public void run() {
697                     // set the new device if different.
698                     boolean newDevice = false;
699                     if (mCurrentDevice != device) {
700                         mCurrentDevice = device;
701                         newDevice = true;
702 
703                         // notify of the new default device
704                         for (ISelectionListener listener : mListeners) {
705                             listener.selectionChanged(mCurrentDevice);
706                         }
707                     }
708 
709                     if (device != null) {
710                         // if this is a device switch or the same device but we didn't find a valid
711                         // client the last time, we go look for a client to use again.
712                         if (newDevice || mCurrentClient == null) {
713                             // now get the new client
714                             Client[] clients =  device.getClients();
715                             if (clients.length > 0) {
716                                 handleDefaultSelection(clients[0]);
717                             } else {
718                                 handleDefaultSelection((Client)null);
719                             }
720                         }
721                     } else {
722                         handleDefaultSelection((Client)null);
723                     }
724                 }
725             });
726         } catch (SWTException e) {
727             // display is disposed. Do nothing since we're quitting anyway.
728         }
729     }
730 
handleDefaultSelection(Client client)731     private void handleDefaultSelection(Client client) {
732         mCurrentClient = client;
733 
734         // notify of the new default client
735         for (ISelectionListener listener : mListeners) {
736             listener.selectionChanged(mCurrentClient);
737         }
738     }
739 
740     /**
741      * Prints a message, associated with a project to the specified stream
742      * @param stream The stream to write to
743      * @param tag The tag associated to the message. Can be null
744      * @param message The message to print.
745      */
printToStream(MessageConsoleStream stream, String tag, String message)746     private static synchronized void printToStream(MessageConsoleStream stream, String tag,
747             String message) {
748         String dateTag = getMessageTag(tag);
749 
750         stream.print(dateTag);
751         if (!dateTag.endsWith(" ")) {
752             stream.print(" ");          //$NON-NLS-1$
753         }
754         stream.println(message);
755     }
756 
757     /**
758      * Creates a string containing the current date/time, and the tag
759      * @param tag The tag associated to the message. Can be null
760      * @return The dateTag
761      */
getMessageTag(String tag)762     private static String getMessageTag(String tag) {
763         Calendar c = Calendar.getInstance();
764 
765         if (tag == null) {
766             return String.format(Messages.DdmsPlugin_Message_Tag_Mask_1, c);
767         }
768 
769         return String.format(Messages.DdmsPlugin_Message_Tag_Mask_2, c, tag);
770     }
771 
772     /**
773      * Implementation of com.android.ddmuilib.StackTracePanel.ISourceRevealer.
774      */
reveal(String applicationName, String className, int line)775     public void reveal(String applicationName, String className, int line) {
776         // loop on all source revealer till one succeeds
777         if (mSourceRevealers != null) {
778             for (ISourceRevealer revealer : mSourceRevealers) {
779                 try {
780                     if (revealer.reveal(applicationName, className, line)) {
781                         break;
782                     }
783                 } catch (Throwable t) {
784                     // ignore, we'll just not use this implementation.
785                 }
786             }
787         }
788     }
789 
launchTraceview(String osPath)790     public boolean launchTraceview(String osPath) {
791         if (mTraceviewLaunchers != null) {
792             for (ITraceviewLauncher launcher : mTraceviewLaunchers) {
793                 try {
794                     if (launcher.openFile(osPath)) {
795                         return true;
796                     }
797                 } catch (Throwable t) {
798                     // ignore, we'll just not use this implementation.
799                 }
800             }
801         }
802 
803         return false;
804     }
805 
806     private LogCatMonitor mLogCatMonitor;
startLogCatMonitor(IDevice device)807     public void startLogCatMonitor(IDevice device) {
808         if (mLogCatMonitor == null) {
809             mLogCatMonitor = new LogCatMonitor(getDebuggerConnectors(), getPreferenceStore());
810         }
811 
812         mLogCatMonitor.monitorDevice(device);
813     }
814 }
815