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