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.ddmlib; 18 19 import com.android.ddmlib.Log.LogLevel; 20 21 import java.io.BufferedReader; 22 import java.io.IOException; 23 import java.io.InputStreamReader; 24 import java.lang.Thread.State; 25 import java.net.InetAddress; 26 import java.net.InetSocketAddress; 27 import java.net.UnknownHostException; 28 import java.security.InvalidParameterException; 29 import java.util.ArrayList; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 33 /** 34 * A connection to the host-side android debug bridge (adb) 35 * <p/>This is the central point to communicate with any devices, emulators, or the applications 36 * running on them. 37 * <p/><b>{@link #init(boolean)} must be called before anything is done.</b> 38 */ 39 public final class AndroidDebugBridge { 40 41 /* 42 * Minimum and maximum version of adb supported. This correspond to 43 * ADB_SERVER_VERSION found in //device/tools/adb/adb.h 44 */ 45 46 private final static int ADB_VERSION_MICRO_MIN = 20; 47 private final static int ADB_VERSION_MICRO_MAX = -1; 48 49 private final static Pattern sAdbVersion = Pattern.compile( 50 "^.*(\\d+)\\.(\\d+)\\.(\\d+)$"); //$NON-NLS-1$ 51 52 private final static String ADB = "adb"; //$NON-NLS-1$ 53 private final static String DDMS = "ddms"; //$NON-NLS-1$ 54 55 // Where to find the ADB bridge. 56 final static String ADB_HOST = "127.0.0.1"; //$NON-NLS-1$ 57 final static int ADB_PORT = 5037; 58 59 static InetAddress sHostAddr; 60 static InetSocketAddress sSocketAddr; 61 62 static { 63 // built-in local address/port for ADB. 64 try { 65 sHostAddr = InetAddress.getByName(ADB_HOST); 66 sSocketAddr = new InetSocketAddress(sHostAddr, ADB_PORT); 67 } catch (UnknownHostException e) { 68 69 } 70 } 71 72 private static AndroidDebugBridge sThis; 73 private static boolean sClientSupport; 74 75 /** Full path to adb. */ 76 private String mAdbOsLocation = null; 77 78 private boolean mVersionCheck; 79 80 private boolean mStarted = false; 81 82 private DeviceMonitor mDeviceMonitor; 83 84 private final static ArrayList<IDebugBridgeChangeListener> sBridgeListeners = 85 new ArrayList<IDebugBridgeChangeListener>(); 86 private final static ArrayList<IDeviceChangeListener> sDeviceListeners = 87 new ArrayList<IDeviceChangeListener>(); 88 private final static ArrayList<IClientChangeListener> sClientListeners = 89 new ArrayList<IClientChangeListener>(); 90 91 // lock object for synchronization 92 private static final Object sLock = sBridgeListeners; 93 94 /** 95 * Classes which implement this interface provide a method that deals 96 * with {@link AndroidDebugBridge} changes. 97 */ 98 public interface IDebugBridgeChangeListener { 99 /** 100 * Sent when a new {@link AndroidDebugBridge} is connected. 101 * <p/> 102 * This is sent from a non UI thread. 103 * @param bridge the new {@link AndroidDebugBridge} object. 104 */ 105 public void bridgeChanged(AndroidDebugBridge bridge); 106 } 107 108 /** 109 * Classes which implement this interface provide methods that deal 110 * with {@link IDevice} addition, deletion, and changes. 111 */ 112 public interface IDeviceChangeListener { 113 /** 114 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 115 * <p/> 116 * This is sent from a non UI thread. 117 * @param device the new device. 118 */ 119 public void deviceConnected(IDevice device); 120 121 /** 122 * Sent when the a device is connected to the {@link AndroidDebugBridge}. 123 * <p/> 124 * This is sent from a non UI thread. 125 * @param device the new device. 126 */ 127 public void deviceDisconnected(IDevice device); 128 129 /** 130 * Sent when a device data changed, or when clients are started/terminated on the device. 131 * <p/> 132 * This is sent from a non UI thread. 133 * @param device the device that was updated. 134 * @param changeMask the mask describing what changed. It can contain any of the following 135 * values: {@link IDevice#CHANGE_BUILD_INFO}, {@link IDevice#CHANGE_STATE}, 136 * {@link IDevice#CHANGE_CLIENT_LIST} 137 */ 138 public void deviceChanged(IDevice device, int changeMask); 139 } 140 141 /** 142 * Classes which implement this interface provide methods that deal 143 * with {@link Client} changes. 144 */ 145 public interface IClientChangeListener { 146 /** 147 * Sent when an existing client information changed. 148 * <p/> 149 * This is sent from a non UI thread. 150 * @param client the updated client. 151 * @param changeMask the bit mask describing the changed properties. It can contain 152 * any of the following values: {@link Client#CHANGE_INFO}, 153 * {@link Client#CHANGE_DEBUGGER_STATUS}, {@link Client#CHANGE_THREAD_MODE}, 154 * {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, 155 * {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA} 156 */ 157 public void clientChanged(Client client, int changeMask); 158 } 159 160 /** 161 * Initializes the <code>ddm</code> library. 162 * <p/>This must be called once <b>before</b> any call to 163 * {@link #createBridge(String, boolean)}. 164 * <p>The library can be initialized in 2 ways: 165 * <ul> 166 * <li>Mode 1: <var>clientSupport</var> == <code>true</code>.<br>The library monitors the 167 * devices and the applications running on them. It will connect to each application, as a 168 * debugger of sort, to be able to interact with them through JDWP packets.</li> 169 * <li>Mode 2: <var>clientSupport</var> == <code>false</code>.<br>The library only monitors 170 * devices. The applications are left untouched, letting other tools built on 171 * <code>ddmlib</code> to connect a debugger to them.</li> 172 * </ul> 173 * <p/><b>Only one tool can run in mode 1 at the same time.</b> 174 * <p/>Note that mode 1 does not prevent debugging of applications running on devices. Mode 1 175 * lets debuggers connect to <code>ddmlib</code> which acts as a proxy between the debuggers and 176 * the applications to debug. See {@link Client#getDebuggerListenPort()}. 177 * <p/>The preferences of <code>ddmlib</code> should also be initialized with whatever default 178 * values were changed from the default values. 179 * <p/>When the application quits, {@link #terminate()} should be called. 180 * @param clientSupport Indicates whether the library should enable the monitoring and 181 * interaction with applications running on the devices. 182 * @see AndroidDebugBridge#createBridge(String, boolean) 183 * @see DdmPreferences 184 */ 185 public static void init(boolean clientSupport) { 186 sClientSupport = clientSupport; 187 188 MonitorThread monitorThread = MonitorThread.createInstance(); 189 monitorThread.start(); 190 191 HandleHello.register(monitorThread); 192 HandleAppName.register(monitorThread); 193 HandleTest.register(monitorThread); 194 HandleThread.register(monitorThread); 195 HandleHeap.register(monitorThread); 196 HandleWait.register(monitorThread); 197 HandleProfiling.register(monitorThread); 198 } 199 200 /** 201 * Terminates the ddm library. This must be called upon application termination. 202 */ 203 public static void terminate() { 204 // kill the monitoring services 205 if (sThis != null && sThis.mDeviceMonitor != null) { 206 sThis.mDeviceMonitor.stop(); 207 sThis.mDeviceMonitor = null; 208 } 209 210 MonitorThread monitorThread = MonitorThread.getInstance(); 211 if (monitorThread != null) { 212 monitorThread.quit(); 213 } 214 } 215 216 /** 217 * Returns whether the ddmlib is setup to support monitoring and interacting with 218 * {@link Client}s running on the {@link IDevice}s. 219 */ 220 static boolean getClientSupport() { 221 return sClientSupport; 222 } 223 224 /** 225 * Creates a {@link AndroidDebugBridge} that is not linked to any particular executable. 226 * <p/>This bridge will expect adb to be running. It will not be able to start/stop/restart 227 * adb. 228 * <p/>If a bridge has already been started, it is directly returned with no changes (similar 229 * to calling {@link #getBridge()}). 230 * @return a connected bridge. 231 */ 232 public static AndroidDebugBridge createBridge() { 233 synchronized (sLock) { 234 if (sThis != null) { 235 return sThis; 236 } 237 238 try { 239 sThis = new AndroidDebugBridge(); 240 sThis.start(); 241 } catch (InvalidParameterException e) { 242 sThis = null; 243 } 244 245 // because the listeners could remove themselves from the list while processing 246 // their event callback, we make a copy of the list and iterate on it instead of 247 // the main list. 248 // This mostly happens when the application quits. 249 IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( 250 new IDebugBridgeChangeListener[sBridgeListeners.size()]); 251 252 // notify the listeners of the change 253 for (IDebugBridgeChangeListener listener : listenersCopy) { 254 // we attempt to catch any exception so that a bad listener doesn't kill our 255 // thread 256 try { 257 listener.bridgeChanged(sThis); 258 } catch (Exception e) { 259 Log.e(DDMS, e); 260 } 261 } 262 263 return sThis; 264 } 265 } 266 267 268 /** 269 * Creates a new debug bridge from the location of the command line tool. 270 * <p/> 271 * Any existing server will be disconnected, unless the location is the same and 272 * <code>forceNewBridge</code> is set to false. 273 * @param osLocation the location of the command line tool 'adb' 274 * @param forceNewBridge force creation of a new bridge even if one with the same location 275 * already exists. 276 * @return a connected bridge. 277 */ 278 public static AndroidDebugBridge createBridge(String osLocation, boolean forceNewBridge) { 279 synchronized (sLock) { 280 if (sThis != null) { 281 if (sThis.mAdbOsLocation != null && sThis.mAdbOsLocation.equals(osLocation) && 282 forceNewBridge == false) { 283 return sThis; 284 } else { 285 // stop the current server 286 sThis.stop(); 287 } 288 } 289 290 try { 291 sThis = new AndroidDebugBridge(osLocation); 292 sThis.start(); 293 } catch (InvalidParameterException e) { 294 sThis = null; 295 } 296 297 // because the listeners could remove themselves from the list while processing 298 // their event callback, we make a copy of the list and iterate on it instead of 299 // the main list. 300 // This mostly happens when the application quits. 301 IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( 302 new IDebugBridgeChangeListener[sBridgeListeners.size()]); 303 304 // notify the listeners of the change 305 for (IDebugBridgeChangeListener listener : listenersCopy) { 306 // we attempt to catch any exception so that a bad listener doesn't kill our 307 // thread 308 try { 309 listener.bridgeChanged(sThis); 310 } catch (Exception e) { 311 Log.e(DDMS, e); 312 } 313 } 314 315 return sThis; 316 } 317 } 318 319 /** 320 * Returns the current debug bridge. Can be <code>null</code> if none were created. 321 */ 322 public static AndroidDebugBridge getBridge() { 323 return sThis; 324 } 325 326 /** 327 * Disconnects the current debug bridge, and destroy the object. 328 * <p/>This also stops the current adb host server. 329 * <p/> 330 * A new object will have to be created with {@link #createBridge(String, boolean)}. 331 */ 332 public static void disconnectBridge() { 333 synchronized (sLock) { 334 if (sThis != null) { 335 sThis.stop(); 336 sThis = null; 337 338 // because the listeners could remove themselves from the list while processing 339 // their event callback, we make a copy of the list and iterate on it instead of 340 // the main list. 341 // This mostly happens when the application quits. 342 IDebugBridgeChangeListener[] listenersCopy = sBridgeListeners.toArray( 343 new IDebugBridgeChangeListener[sBridgeListeners.size()]); 344 345 // notify the listeners. 346 for (IDebugBridgeChangeListener listener : listenersCopy) { 347 // we attempt to catch any exception so that a bad listener doesn't kill our 348 // thread 349 try { 350 listener.bridgeChanged(sThis); 351 } catch (Exception e) { 352 Log.e(DDMS, e); 353 } 354 } 355 } 356 } 357 } 358 359 /** 360 * Adds the listener to the collection of listeners who will be notified when a new 361 * {@link AndroidDebugBridge} is connected, by sending it one of the messages defined 362 * in the {@link IDebugBridgeChangeListener} interface. 363 * @param listener The listener which should be notified. 364 */ 365 public static void addDebugBridgeChangeListener(IDebugBridgeChangeListener listener) { 366 synchronized (sLock) { 367 if (sBridgeListeners.contains(listener) == false) { 368 sBridgeListeners.add(listener); 369 if (sThis != null) { 370 // we attempt to catch any exception so that a bad listener doesn't kill our 371 // thread 372 try { 373 listener.bridgeChanged(sThis); 374 } catch (Exception e) { 375 Log.e(DDMS, e); 376 } 377 } 378 } 379 } 380 } 381 382 /** 383 * Removes the listener from the collection of listeners who will be notified when a new 384 * {@link AndroidDebugBridge} is started. 385 * @param listener The listener which should no longer be notified. 386 */ 387 public static void removeDebugBridgeChangeListener(IDebugBridgeChangeListener listener) { 388 synchronized (sLock) { 389 sBridgeListeners.remove(listener); 390 } 391 } 392 393 /** 394 * Adds the listener to the collection of listeners who will be notified when a {@link IDevice} 395 * is connected, disconnected, or when its properties or its {@link Client} list changed, 396 * by sending it one of the messages defined in the {@link IDeviceChangeListener} interface. 397 * @param listener The listener which should be notified. 398 */ 399 public static void addDeviceChangeListener(IDeviceChangeListener listener) { 400 synchronized (sLock) { 401 if (sDeviceListeners.contains(listener) == false) { 402 sDeviceListeners.add(listener); 403 } 404 } 405 } 406 407 /** 408 * Removes the listener from the collection of listeners who will be notified when a 409 * {@link IDevice} is connected, disconnected, or when its properties or its {@link Client} 410 * list changed. 411 * @param listener The listener which should no longer be notified. 412 */ 413 public static void removeDeviceChangeListener(IDeviceChangeListener listener) { 414 synchronized (sLock) { 415 sDeviceListeners.remove(listener); 416 } 417 } 418 419 /** 420 * Adds the listener to the collection of listeners who will be notified when a {@link Client} 421 * property changed, by sending it one of the messages defined in the 422 * {@link IClientChangeListener} interface. 423 * @param listener The listener which should be notified. 424 */ 425 public static void addClientChangeListener(IClientChangeListener listener) { 426 synchronized (sLock) { 427 if (sClientListeners.contains(listener) == false) { 428 sClientListeners.add(listener); 429 } 430 } 431 } 432 433 /** 434 * Removes the listener from the collection of listeners who will be notified when a 435 * {@link Client} property changed. 436 * @param listener The listener which should no longer be notified. 437 */ 438 public static void removeClientChangeListener(IClientChangeListener listener) { 439 synchronized (sLock) { 440 sClientListeners.remove(listener); 441 } 442 } 443 444 445 /** 446 * Returns the devices. 447 * @see #hasInitialDeviceList() 448 */ 449 public IDevice[] getDevices() { 450 synchronized (sLock) { 451 if (mDeviceMonitor != null) { 452 return mDeviceMonitor.getDevices(); 453 } 454 } 455 456 return new IDevice[0]; 457 } 458 459 /** 460 * Returns whether the bridge has acquired the initial list from adb after being created. 461 * <p/>Calling {@link #getDevices()} right after {@link #createBridge(String, boolean)} will 462 * generally result in an empty list. This is due to the internal asynchronous communication 463 * mechanism with <code>adb</code> that does not guarantee that the {@link IDevice} list has been 464 * built before the call to {@link #getDevices()}. 465 * <p/>The recommended way to get the list of {@link IDevice} objects is to create a 466 * {@link IDeviceChangeListener} object. 467 */ 468 public boolean hasInitialDeviceList() { 469 if (mDeviceMonitor != null) { 470 return mDeviceMonitor.hasInitialDeviceList(); 471 } 472 473 return false; 474 } 475 476 /** 477 * Sets the client to accept debugger connection on the custom "Selected debug port". 478 * @param selectedClient the client. Can be null. 479 */ 480 public void setSelectedClient(Client selectedClient) { 481 MonitorThread monitorThread = MonitorThread.getInstance(); 482 if (monitorThread != null) { 483 monitorThread.setSelectedClient(selectedClient); 484 } 485 } 486 487 /** 488 * Returns whether the {@link AndroidDebugBridge} object is still connected to the adb daemon. 489 */ 490 public boolean isConnected() { 491 MonitorThread monitorThread = MonitorThread.getInstance(); 492 if (mDeviceMonitor != null && monitorThread != null) { 493 return mDeviceMonitor.isMonitoring() && monitorThread.getState() != State.TERMINATED; 494 } 495 return false; 496 } 497 498 /** 499 * Returns the number of times the {@link AndroidDebugBridge} object attempted to connect 500 * to the adb daemon. 501 */ 502 public int getConnectionAttemptCount() { 503 if (mDeviceMonitor != null) { 504 return mDeviceMonitor.getConnectionAttemptCount(); 505 } 506 return -1; 507 } 508 509 /** 510 * Returns the number of times the {@link AndroidDebugBridge} object attempted to restart 511 * the adb daemon. 512 */ 513 public int getRestartAttemptCount() { 514 if (mDeviceMonitor != null) { 515 return mDeviceMonitor.getRestartAttemptCount(); 516 } 517 return -1; 518 } 519 520 /** 521 * Creates a new bridge. 522 * @param osLocation the location of the command line tool 523 * @throws InvalidParameterException 524 */ 525 private AndroidDebugBridge(String osLocation) throws InvalidParameterException { 526 if (osLocation == null || osLocation.length() == 0) { 527 throw new InvalidParameterException(); 528 } 529 mAdbOsLocation = osLocation; 530 531 checkAdbVersion(); 532 } 533 534 /** 535 * Creates a new bridge not linked to any particular adb executable. 536 */ 537 private AndroidDebugBridge() { 538 } 539 540 /** 541 * Queries adb for its version number and checks it against {@link #MIN_VERSION_NUMBER} and 542 * {@link #MAX_VERSION_NUMBER} 543 */ 544 private void checkAdbVersion() { 545 // default is bad check 546 mVersionCheck = false; 547 548 if (mAdbOsLocation == null) { 549 return; 550 } 551 552 try { 553 String[] command = new String[2]; 554 command[0] = mAdbOsLocation; 555 command[1] = "version"; //$NON-NLS-1$ 556 Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); //$NON-NLS-1$ 557 Process process = Runtime.getRuntime().exec(command); 558 559 ArrayList<String> errorOutput = new ArrayList<String>(); 560 ArrayList<String> stdOutput = new ArrayList<String>(); 561 int status = grabProcessOutput(process, errorOutput, stdOutput, 562 true /* waitForReaders */); 563 564 if (status != 0) { 565 StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$ 566 for (String error : errorOutput) { 567 builder.append('\n'); 568 builder.append(error); 569 } 570 Log.logAndDisplay(LogLevel.ERROR, "adb", builder.toString()); 571 } 572 573 // check both stdout and stderr 574 boolean versionFound = false; 575 for (String line : stdOutput) { 576 versionFound = scanVersionLine(line); 577 if (versionFound) { 578 break; 579 } 580 } 581 if (!versionFound) { 582 for (String line : errorOutput) { 583 versionFound = scanVersionLine(line); 584 if (versionFound) { 585 break; 586 } 587 } 588 } 589 590 if (!versionFound) { 591 // if we get here, we failed to parse the output. 592 Log.logAndDisplay(LogLevel.ERROR, ADB, 593 "Failed to parse the output of 'adb version'"); //$NON-NLS-1$ 594 } 595 596 } catch (IOException e) { 597 Log.logAndDisplay(LogLevel.ERROR, ADB, 598 "Failed to get the adb version: " + e.getMessage()); //$NON-NLS-1$ 599 } catch (InterruptedException e) { 600 } finally { 601 602 } 603 } 604 605 /** 606 * Scans a line resulting from 'adb version' for a potential version number. 607 * <p/> 608 * If a version number is found, it checks the version number against what is expected 609 * by this version of ddms. 610 * <p/> 611 * Returns true when a version number has been found so that we can stop scanning, 612 * whether the version number is in the acceptable range or not. 613 * 614 * @param line The line to scan. 615 * @return True if a version number was found (whether it is acceptable or not). 616 */ 617 private boolean scanVersionLine(String line) { 618 if (line != null) { 619 Matcher matcher = sAdbVersion.matcher(line); 620 if (matcher.matches()) { 621 int majorVersion = Integer.parseInt(matcher.group(1)); 622 int minorVersion = Integer.parseInt(matcher.group(2)); 623 int microVersion = Integer.parseInt(matcher.group(3)); 624 625 // check only the micro version for now. 626 if (microVersion < ADB_VERSION_MICRO_MIN) { 627 String message = String.format( 628 "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ 629 + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ 630 majorVersion, minorVersion, ADB_VERSION_MICRO_MIN, 631 microVersion); 632 Log.logAndDisplay(LogLevel.ERROR, ADB, message); 633 } else if (ADB_VERSION_MICRO_MAX != -1 && 634 microVersion > ADB_VERSION_MICRO_MAX) { 635 String message = String.format( 636 "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ 637 + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ 638 majorVersion, minorVersion, ADB_VERSION_MICRO_MAX, 639 microVersion); 640 Log.logAndDisplay(LogLevel.ERROR, ADB, message); 641 } else { 642 mVersionCheck = true; 643 } 644 645 return true; 646 } 647 } 648 return false; 649 } 650 651 /** 652 * Starts the debug bridge. 653 * @return true if success. 654 */ 655 boolean start() { 656 if (mAdbOsLocation != null && (mVersionCheck == false || startAdb() == false)) { 657 return false; 658 } 659 660 mStarted = true; 661 662 // now that the bridge is connected, we start the underlying services. 663 mDeviceMonitor = new DeviceMonitor(this); 664 mDeviceMonitor.start(); 665 666 return true; 667 } 668 669 /** 670 * Kills the debug bridge, and the adb host server. 671 * @return true if success 672 */ 673 boolean stop() { 674 // if we haven't started we return false; 675 if (mStarted == false) { 676 return false; 677 } 678 679 // kill the monitoring services 680 mDeviceMonitor.stop(); 681 mDeviceMonitor = null; 682 683 if (stopAdb() == false) { 684 return false; 685 } 686 687 mStarted = false; 688 return true; 689 } 690 691 /** 692 * Restarts adb, but not the services around it. 693 * @return true if success. 694 */ 695 public boolean restart() { 696 if (mAdbOsLocation == null) { 697 Log.e(ADB, 698 "Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 699 return false; 700 } 701 702 if (mVersionCheck == false) { 703 Log.logAndDisplay(LogLevel.ERROR, ADB, 704 "Attempting to restart adb, but version check failed!"); //$NON-NLS-1$ 705 return false; 706 } 707 synchronized (this) { 708 stopAdb(); 709 710 boolean restart = startAdb(); 711 712 if (restart && mDeviceMonitor == null) { 713 mDeviceMonitor = new DeviceMonitor(this); 714 mDeviceMonitor.start(); 715 } 716 717 return restart; 718 } 719 } 720 721 /** 722 * Notify the listener of a new {@link IDevice}. 723 * <p/> 724 * The notification of the listeners is done in a synchronized block. It is important to 725 * expect the listeners to potentially access various methods of {@link IDevice} as well as 726 * {@link #getDevices()} which use internal locks. 727 * <p/> 728 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 729 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 730 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 731 * @param device the new <code>IDevice</code>. 732 * @see #getLock() 733 */ 734 void deviceConnected(IDevice device) { 735 // because the listeners could remove themselves from the list while processing 736 // their event callback, we make a copy of the list and iterate on it instead of 737 // the main list. 738 // This mostly happens when the application quits. 739 IDeviceChangeListener[] listenersCopy = null; 740 synchronized (sLock) { 741 listenersCopy = sDeviceListeners.toArray( 742 new IDeviceChangeListener[sDeviceListeners.size()]); 743 } 744 745 // Notify the listeners 746 for (IDeviceChangeListener listener : listenersCopy) { 747 // we attempt to catch any exception so that a bad listener doesn't kill our 748 // thread 749 try { 750 listener.deviceConnected(device); 751 } catch (Exception e) { 752 Log.e(DDMS, e); 753 } 754 } 755 } 756 757 /** 758 * Notify the listener of a disconnected {@link IDevice}. 759 * <p/> 760 * The notification of the listeners is done in a synchronized block. It is important to 761 * expect the listeners to potentially access various methods of {@link IDevice} as well as 762 * {@link #getDevices()} which use internal locks. 763 * <p/> 764 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 765 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 766 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 767 * @param device the disconnected <code>IDevice</code>. 768 * @see #getLock() 769 */ 770 void deviceDisconnected(IDevice device) { 771 // because the listeners could remove themselves from the list while processing 772 // their event callback, we make a copy of the list and iterate on it instead of 773 // the main list. 774 // This mostly happens when the application quits. 775 IDeviceChangeListener[] listenersCopy = null; 776 synchronized (sLock) { 777 listenersCopy = sDeviceListeners.toArray( 778 new IDeviceChangeListener[sDeviceListeners.size()]); 779 } 780 781 // Notify the listeners 782 for (IDeviceChangeListener listener : listenersCopy) { 783 // we attempt to catch any exception so that a bad listener doesn't kill our 784 // thread 785 try { 786 listener.deviceDisconnected(device); 787 } catch (Exception e) { 788 Log.e(DDMS, e); 789 } 790 } 791 } 792 793 /** 794 * Notify the listener of a modified {@link IDevice}. 795 * <p/> 796 * The notification of the listeners is done in a synchronized block. It is important to 797 * expect the listeners to potentially access various methods of {@link IDevice} as well as 798 * {@link #getDevices()} which use internal locks. 799 * <p/> 800 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 801 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 802 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 803 * @param device the modified <code>IDevice</code>. 804 * @see #getLock() 805 */ 806 void deviceChanged(IDevice device, int changeMask) { 807 // because the listeners could remove themselves from the list while processing 808 // their event callback, we make a copy of the list and iterate on it instead of 809 // the main list. 810 // This mostly happens when the application quits. 811 IDeviceChangeListener[] listenersCopy = null; 812 synchronized (sLock) { 813 listenersCopy = sDeviceListeners.toArray( 814 new IDeviceChangeListener[sDeviceListeners.size()]); 815 } 816 817 // Notify the listeners 818 for (IDeviceChangeListener listener : listenersCopy) { 819 // we attempt to catch any exception so that a bad listener doesn't kill our 820 // thread 821 try { 822 listener.deviceChanged(device, changeMask); 823 } catch (Exception e) { 824 Log.e(DDMS, e); 825 } 826 } 827 } 828 829 /** 830 * Notify the listener of a modified {@link Client}. 831 * <p/> 832 * The notification of the listeners is done in a synchronized block. It is important to 833 * expect the listeners to potentially access various methods of {@link IDevice} as well as 834 * {@link #getDevices()} which use internal locks. 835 * <p/> 836 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 837 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 838 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 839 * @param device the modified <code>Client</code>. 840 * @param changeMask the mask indicating what changed in the <code>Client</code> 841 * @see #getLock() 842 */ 843 void clientChanged(Client client, int changeMask) { 844 // because the listeners could remove themselves from the list while processing 845 // their event callback, we make a copy of the list and iterate on it instead of 846 // the main list. 847 // This mostly happens when the application quits. 848 IClientChangeListener[] listenersCopy = null; 849 synchronized (sLock) { 850 listenersCopy = sClientListeners.toArray( 851 new IClientChangeListener[sClientListeners.size()]); 852 853 } 854 855 // Notify the listeners 856 for (IClientChangeListener listener : listenersCopy) { 857 // we attempt to catch any exception so that a bad listener doesn't kill our 858 // thread 859 try { 860 listener.clientChanged(client, changeMask); 861 } catch (Exception e) { 862 Log.e(DDMS, e); 863 } 864 } 865 } 866 867 /** 868 * Returns the {@link DeviceMonitor} object. 869 */ 870 DeviceMonitor getDeviceMonitor() { 871 return mDeviceMonitor; 872 } 873 874 /** 875 * Starts the adb host side server. 876 * @return true if success 877 */ 878 synchronized boolean startAdb() { 879 if (mAdbOsLocation == null) { 880 Log.e(ADB, 881 "Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 882 return false; 883 } 884 885 Process proc; 886 int status = -1; 887 888 try { 889 String[] command = new String[2]; 890 command[0] = mAdbOsLocation; 891 command[1] = "start-server"; //$NON-NLS-1$ 892 Log.d(DDMS, 893 String.format("Launching '%1$s %2$s' to ensure ADB is running.", //$NON-NLS-1$ 894 mAdbOsLocation, command[1])); 895 proc = Runtime.getRuntime().exec(command); 896 897 ArrayList<String> errorOutput = new ArrayList<String>(); 898 ArrayList<String> stdOutput = new ArrayList<String>(); 899 status = grabProcessOutput(proc, errorOutput, stdOutput, 900 false /* waitForReaders */); 901 902 } catch (IOException ioe) { 903 Log.d(DDMS, "Unable to run 'adb': " + ioe.getMessage()); //$NON-NLS-1$ 904 // we'll return false; 905 } catch (InterruptedException ie) { 906 Log.d(DDMS, "Unable to run 'adb': " + ie.getMessage()); //$NON-NLS-1$ 907 // we'll return false; 908 } 909 910 if (status != 0) { 911 Log.w(DDMS, 912 "'adb start-server' failed -- run manually if necessary"); //$NON-NLS-1$ 913 return false; 914 } 915 916 Log.d(DDMS, "'adb start-server' succeeded"); //$NON-NLS-1$ 917 918 return true; 919 } 920 921 /** 922 * Stops the adb host side server. 923 * @return true if success 924 */ 925 private synchronized boolean stopAdb() { 926 if (mAdbOsLocation == null) { 927 Log.e(ADB, 928 "Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 929 return false; 930 } 931 932 Process proc; 933 int status = -1; 934 935 try { 936 String[] command = new String[2]; 937 command[0] = mAdbOsLocation; 938 command[1] = "kill-server"; //$NON-NLS-1$ 939 proc = Runtime.getRuntime().exec(command); 940 status = proc.waitFor(); 941 } 942 catch (IOException ioe) { 943 // we'll return false; 944 } 945 catch (InterruptedException ie) { 946 // we'll return false; 947 } 948 949 if (status != 0) { 950 Log.w(DDMS, 951 "'adb kill-server' failed -- run manually if necessary"); //$NON-NLS-1$ 952 return false; 953 } 954 955 Log.d(DDMS, "'adb kill-server' succeeded"); //$NON-NLS-1$ 956 return true; 957 } 958 959 /** 960 * Get the stderr/stdout outputs of a process and return when the process is done. 961 * Both <b>must</b> be read or the process will block on windows. 962 * @param process The process to get the ouput from 963 * @param errorOutput The array to store the stderr output. cannot be null. 964 * @param stdOutput The array to store the stdout output. cannot be null. 965 * @param displayStdOut If true this will display stdout as well 966 * @param waitforReaders if true, this will wait for the reader threads. 967 * @return the process return code. 968 * @throws InterruptedException 969 */ 970 private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput, 971 final ArrayList<String> stdOutput, boolean waitforReaders) 972 throws InterruptedException { 973 assert errorOutput != null; 974 assert stdOutput != null; 975 // read the lines as they come. if null is returned, it's 976 // because the process finished 977 Thread t1 = new Thread("") { //$NON-NLS-1$ 978 @Override 979 public void run() { 980 // create a buffer to read the stderr output 981 InputStreamReader is = new InputStreamReader(process.getErrorStream()); 982 BufferedReader errReader = new BufferedReader(is); 983 984 try { 985 while (true) { 986 String line = errReader.readLine(); 987 if (line != null) { 988 Log.e(ADB, line); 989 errorOutput.add(line); 990 } else { 991 break; 992 } 993 } 994 } catch (IOException e) { 995 // do nothing. 996 } 997 } 998 }; 999 1000 Thread t2 = new Thread("") { //$NON-NLS-1$ 1001 @Override 1002 public void run() { 1003 InputStreamReader is = new InputStreamReader(process.getInputStream()); 1004 BufferedReader outReader = new BufferedReader(is); 1005 1006 try { 1007 while (true) { 1008 String line = outReader.readLine(); 1009 if (line != null) { 1010 Log.d(ADB, line); 1011 stdOutput.add(line); 1012 } else { 1013 break; 1014 } 1015 } 1016 } catch (IOException e) { 1017 // do nothing. 1018 } 1019 } 1020 }; 1021 1022 t1.start(); 1023 t2.start(); 1024 1025 // it looks like on windows process#waitFor() can return 1026 // before the thread have filled the arrays, so we wait for both threads and the 1027 // process itself. 1028 if (waitforReaders) { 1029 try { 1030 t1.join(); 1031 } catch (InterruptedException e) { 1032 } 1033 try { 1034 t2.join(); 1035 } catch (InterruptedException e) { 1036 } 1037 } 1038 1039 // get the return code from the process 1040 return process.waitFor(); 1041 } 1042 1043 /** 1044 * Returns the singleton lock used by this class to protect any access to the listener. 1045 * <p/> 1046 * This includes adding/removing listeners, but also notifying listeners of new bridges, 1047 * devices, and clients. 1048 */ 1049 static Object getLock() { 1050 return sLock; 1051 } 1052 } 1053