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