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