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