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 */ 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 */ 117 public void selectionChanged(IDevice selectedDevice); 118 } 119 120 /** 121 * The constructor 122 */ 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 */ 448 public static DdmsPlugin getDefault() { 449 return sPlugin; 450 } 451 452 public static String getAdb() { 453 return sAdbLocation; 454 } 455 456 public static String getToolsFolder2() { 457 return sToolsFolder; 458 } 459 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 */ 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 */ 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 */ 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 */ 547 public IDebuggerConnector[] getDebuggerConnectors() { 548 return mDebuggerConnectors; 549 } 550 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 559 public synchronized void removeSelectionListener(ISelectionListener listener) { 560 mListeners.remove(listener); 561 } 562 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 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; 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