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.File; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.lang.Thread.State; 26 import java.net.InetAddress; 27 import java.net.InetSocketAddress; 28 import java.net.UnknownHostException; 29 import java.security.InvalidParameterException; 30 import java.util.ArrayList; 31 import java.util.Map; 32 import java.util.regex.Matcher; 33 import 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 */ 41 public 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 */ bridgeChanged(AndroidDebugBridge bridge)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 */ deviceConnected(IDevice device)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 */ deviceDisconnected(IDevice device)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 */ deviceChanged(IDevice device, int changeMask)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 */ clientChanged(Client client, int changeMask)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 */ init(boolean clientSupport)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 */ terminate()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 */ getClientSupport()224 static boolean getClientSupport() { 225 return sClientSupport; 226 } 227 228 /** 229 * Returns the socket address of the ADB server on the host. 230 */ getSocketAddress()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 */ createBridge()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 */ createBridge(String osLocation, boolean forceNewBridge)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 */ getBridge()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 */ disconnectBridge()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 */ addDebugBridgeChangeListener(IDebugBridgeChangeListener listener)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 */ removeDebugBridgeChangeListener(IDebugBridgeChangeListener listener)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 */ addDeviceChangeListener(IDeviceChangeListener listener)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 */ removeDeviceChangeListener(IDeviceChangeListener listener)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 */ addClientChangeListener(IClientChangeListener listener)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 */ removeClientChangeListener(IClientChangeListener listener)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 */ getDevices()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 */ hasInitialDeviceList()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 */ setSelectedClient(Client selectedClient)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 */ isConnected()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 */ getConnectionAttemptCount()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 */ getRestartAttemptCount()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 */ AndroidDebugBridge(String osLocation)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 */ AndroidDebugBridge()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 */ checkAdbVersion()555 private void checkAdbVersion() { 556 // default is bad check 557 mVersionCheck = false; 558 559 if (mAdbOsLocation == null) { 560 return; 561 } 562 563 try { 564 String[] command = new String[2]; 565 command[0] = mAdbOsLocation; 566 command[1] = "version"; //$NON-NLS-1$ 567 Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); //$NON-NLS-1$ 568 Process process = Runtime.getRuntime().exec(command); 569 570 ArrayList<String> errorOutput = new ArrayList<String>(); 571 ArrayList<String> stdOutput = new ArrayList<String>(); 572 int status = grabProcessOutput(process, errorOutput, stdOutput, 573 true /* waitForReaders */); 574 575 if (status != 0) { 576 StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$ 577 for (String error : errorOutput) { 578 builder.append('\n'); 579 builder.append(error); 580 } 581 Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString()); 582 } 583 584 // check both stdout and stderr 585 boolean versionFound = false; 586 for (String line : stdOutput) { 587 versionFound = scanVersionLine(line); 588 if (versionFound) { 589 break; 590 } 591 } 592 if (!versionFound) { 593 for (String line : errorOutput) { 594 versionFound = scanVersionLine(line); 595 if (versionFound) { 596 break; 597 } 598 } 599 } 600 601 if (!versionFound) { 602 // if we get here, we failed to parse the output. 603 StringBuilder builder = new StringBuilder( 604 "Failed to parse the output of 'adb version':\n"); //$NON-NLS-1$ 605 builder.append("Standard Output was:\n"); //$NON-NLS-1$ 606 for (String line : stdOutput) { 607 builder.append(line); 608 builder.append('\n'); 609 } 610 builder.append("\nError Output was:\n"); //$NON-NLS-1$ 611 for (String line : errorOutput) { 612 builder.append(line); 613 builder.append('\n'); 614 } 615 Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString()); 616 } 617 } catch (IOException e) { 618 Log.logAndDisplay(LogLevel.ERROR, ADB, 619 "Failed to get the adb version: " + e.getMessage() //$NON-NLS-1$ 620 + " from '" + mAdbOsLocation + "' - exists=" //$NON-NLS-1$ 621 + (new File(mAdbOsLocation).exists())); 622 } catch (InterruptedException e) { 623 } finally { 624 625 } 626 } 627 628 /** 629 * Scans a line resulting from 'adb version' for a potential version number. 630 * <p/> 631 * If a version number is found, it checks the version number against what is expected 632 * by this version of ddms. 633 * <p/> 634 * Returns true when a version number has been found so that we can stop scanning, 635 * whether the version number is in the acceptable range or not. 636 * 637 * @param line The line to scan. 638 * @return True if a version number was found (whether it is acceptable or not). 639 */ 640 @SuppressWarnings("all") // With Eclipse 3.6, replace by @SuppressWarnings("unused") scanVersionLine(String line)641 private boolean scanVersionLine(String line) { 642 if (line != null) { 643 Matcher matcher = sAdbVersion.matcher(line); 644 if (matcher.matches()) { 645 int majorVersion = Integer.parseInt(matcher.group(1)); 646 int minorVersion = Integer.parseInt(matcher.group(2)); 647 int microVersion = Integer.parseInt(matcher.group(3)); 648 649 // check only the micro version for now. 650 if (microVersion < ADB_VERSION_MICRO_MIN) { 651 String message = String.format( 652 "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ 653 + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ 654 majorVersion, minorVersion, ADB_VERSION_MICRO_MIN, 655 microVersion); 656 Log.logAndDisplay(LogLevel.ERROR, ADB, message); 657 } else if (ADB_VERSION_MICRO_MAX != -1 && 658 microVersion > ADB_VERSION_MICRO_MAX) { 659 String message = String.format( 660 "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$ 661 + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$ 662 majorVersion, minorVersion, ADB_VERSION_MICRO_MAX, 663 microVersion); 664 Log.logAndDisplay(LogLevel.ERROR, ADB, message); 665 } else { 666 mVersionCheck = true; 667 } 668 669 return true; 670 } 671 } 672 return false; 673 } 674 675 /** 676 * Starts the debug bridge. 677 * @return true if success. 678 */ start()679 boolean start() { 680 if (mAdbOsLocation != null && (mVersionCheck == false || startAdb() == false)) { 681 return false; 682 } 683 684 mStarted = true; 685 686 // now that the bridge is connected, we start the underlying services. 687 mDeviceMonitor = new DeviceMonitor(this); 688 mDeviceMonitor.start(); 689 690 return true; 691 } 692 693 /** 694 * Kills the debug bridge, and the adb host server. 695 * @return true if success 696 */ stop()697 boolean stop() { 698 // if we haven't started we return false; 699 if (mStarted == false) { 700 return false; 701 } 702 703 // kill the monitoring services 704 mDeviceMonitor.stop(); 705 mDeviceMonitor = null; 706 707 if (stopAdb() == false) { 708 return false; 709 } 710 711 mStarted = false; 712 return true; 713 } 714 715 /** 716 * Restarts adb, but not the services around it. 717 * @return true if success. 718 */ restart()719 public boolean restart() { 720 if (mAdbOsLocation == null) { 721 Log.e(ADB, 722 "Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 723 return false; 724 } 725 726 if (mVersionCheck == false) { 727 Log.logAndDisplay(LogLevel.ERROR, ADB, 728 "Attempting to restart adb, but version check failed!"); //$NON-NLS-1$ 729 return false; 730 } 731 synchronized (this) { 732 stopAdb(); 733 734 boolean restart = startAdb(); 735 736 if (restart && mDeviceMonitor == null) { 737 mDeviceMonitor = new DeviceMonitor(this); 738 mDeviceMonitor.start(); 739 } 740 741 return restart; 742 } 743 } 744 745 /** 746 * Notify the listener of a new {@link IDevice}. 747 * <p/> 748 * The notification of the listeners is done in a synchronized block. It is important to 749 * expect the listeners to potentially access various methods of {@link IDevice} as well as 750 * {@link #getDevices()} which use internal locks. 751 * <p/> 752 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 753 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 754 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 755 * @param device the new <code>IDevice</code>. 756 * @see #getLock() 757 */ deviceConnected(IDevice device)758 void deviceConnected(IDevice device) { 759 // because the listeners could remove themselves from the list while processing 760 // their event callback, we make a copy of the list and iterate on it instead of 761 // the main list. 762 // This mostly happens when the application quits. 763 IDeviceChangeListener[] listenersCopy = null; 764 synchronized (sLock) { 765 listenersCopy = sDeviceListeners.toArray( 766 new IDeviceChangeListener[sDeviceListeners.size()]); 767 } 768 769 // Notify the listeners 770 for (IDeviceChangeListener listener : listenersCopy) { 771 // we attempt to catch any exception so that a bad listener doesn't kill our 772 // thread 773 try { 774 listener.deviceConnected(device); 775 } catch (Exception e) { 776 Log.e(DDMS, e); 777 } 778 } 779 } 780 781 /** 782 * Notify the listener of a disconnected {@link IDevice}. 783 * <p/> 784 * The notification of the listeners is done in a synchronized block. It is important to 785 * expect the listeners to potentially access various methods of {@link IDevice} as well as 786 * {@link #getDevices()} which use internal locks. 787 * <p/> 788 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 789 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 790 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 791 * @param device the disconnected <code>IDevice</code>. 792 * @see #getLock() 793 */ deviceDisconnected(IDevice device)794 void deviceDisconnected(IDevice device) { 795 // because the listeners could remove themselves from the list while processing 796 // their event callback, we make a copy of the list and iterate on it instead of 797 // the main list. 798 // This mostly happens when the application quits. 799 IDeviceChangeListener[] listenersCopy = null; 800 synchronized (sLock) { 801 listenersCopy = sDeviceListeners.toArray( 802 new IDeviceChangeListener[sDeviceListeners.size()]); 803 } 804 805 // Notify the listeners 806 for (IDeviceChangeListener listener : listenersCopy) { 807 // we attempt to catch any exception so that a bad listener doesn't kill our 808 // thread 809 try { 810 listener.deviceDisconnected(device); 811 } catch (Exception e) { 812 Log.e(DDMS, e); 813 } 814 } 815 } 816 817 /** 818 * Notify the listener of a modified {@link IDevice}. 819 * <p/> 820 * The notification of the listeners is done in a synchronized block. It is important to 821 * expect the listeners to potentially access various methods of {@link IDevice} as well as 822 * {@link #getDevices()} which use internal locks. 823 * <p/> 824 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 825 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 826 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 827 * @param device the modified <code>IDevice</code>. 828 * @see #getLock() 829 */ deviceChanged(IDevice device, int changeMask)830 void deviceChanged(IDevice device, int changeMask) { 831 // because the listeners could remove themselves from the list while processing 832 // their event callback, we make a copy of the list and iterate on it instead of 833 // the main list. 834 // This mostly happens when the application quits. 835 IDeviceChangeListener[] listenersCopy = null; 836 synchronized (sLock) { 837 listenersCopy = sDeviceListeners.toArray( 838 new IDeviceChangeListener[sDeviceListeners.size()]); 839 } 840 841 // Notify the listeners 842 for (IDeviceChangeListener listener : listenersCopy) { 843 // we attempt to catch any exception so that a bad listener doesn't kill our 844 // thread 845 try { 846 listener.deviceChanged(device, changeMask); 847 } catch (Exception e) { 848 Log.e(DDMS, e); 849 } 850 } 851 } 852 853 /** 854 * Notify the listener of a modified {@link Client}. 855 * <p/> 856 * The notification of the listeners is done in a synchronized block. It is important to 857 * expect the listeners to potentially access various methods of {@link IDevice} as well as 858 * {@link #getDevices()} which use internal locks. 859 * <p/> 860 * For this reason, any call to this method from a method of {@link DeviceMonitor}, 861 * {@link IDevice} which is also inside a synchronized block, should first synchronize on 862 * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}. 863 * @param device the modified <code>Client</code>. 864 * @param changeMask the mask indicating what changed in the <code>Client</code> 865 * @see #getLock() 866 */ clientChanged(Client client, int changeMask)867 void clientChanged(Client client, int changeMask) { 868 // because the listeners could remove themselves from the list while processing 869 // their event callback, we make a copy of the list and iterate on it instead of 870 // the main list. 871 // This mostly happens when the application quits. 872 IClientChangeListener[] listenersCopy = null; 873 synchronized (sLock) { 874 listenersCopy = sClientListeners.toArray( 875 new IClientChangeListener[sClientListeners.size()]); 876 877 } 878 879 // Notify the listeners 880 for (IClientChangeListener listener : listenersCopy) { 881 // we attempt to catch any exception so that a bad listener doesn't kill our 882 // thread 883 try { 884 listener.clientChanged(client, changeMask); 885 } catch (Exception e) { 886 Log.e(DDMS, e); 887 } 888 } 889 } 890 891 /** 892 * Returns the {@link DeviceMonitor} object. 893 */ getDeviceMonitor()894 DeviceMonitor getDeviceMonitor() { 895 return mDeviceMonitor; 896 } 897 898 /** 899 * Starts the adb host side server. 900 * @return true if success 901 */ startAdb()902 synchronized boolean startAdb() { 903 if (mAdbOsLocation == null) { 904 Log.e(ADB, 905 "Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 906 return false; 907 } 908 909 Process proc; 910 int status = -1; 911 912 try { 913 String[] command = new String[2]; 914 command[0] = mAdbOsLocation; 915 command[1] = "start-server"; //$NON-NLS-1$ 916 Log.d(DDMS, 917 String.format("Launching '%1$s %2$s' to ensure ADB is running.", //$NON-NLS-1$ 918 mAdbOsLocation, command[1])); 919 ProcessBuilder processBuilder = new ProcessBuilder(command); 920 if (DdmPreferences.getUseAdbHost()) { 921 String adbHostValue = DdmPreferences.getAdbHostValue(); 922 if (adbHostValue != null && adbHostValue.length() > 0) { 923 //TODO : check that the String is a valid IP address 924 Map<String, String> env = processBuilder.environment(); 925 env.put("ADBHOST", adbHostValue); 926 } 927 } 928 proc = processBuilder.start(); 929 930 ArrayList<String> errorOutput = new ArrayList<String>(); 931 ArrayList<String> stdOutput = new ArrayList<String>(); 932 status = grabProcessOutput(proc, errorOutput, stdOutput, 933 false /* waitForReaders */); 934 935 } catch (IOException ioe) { 936 Log.d(DDMS, "Unable to run 'adb': " + ioe.getMessage()); //$NON-NLS-1$ 937 // we'll return false; 938 } catch (InterruptedException ie) { 939 Log.d(DDMS, "Unable to run 'adb': " + ie.getMessage()); //$NON-NLS-1$ 940 // we'll return false; 941 } 942 943 if (status != 0) { 944 Log.w(DDMS, 945 "'adb start-server' failed -- run manually if necessary"); //$NON-NLS-1$ 946 return false; 947 } 948 949 Log.d(DDMS, "'adb start-server' succeeded"); //$NON-NLS-1$ 950 951 return true; 952 } 953 954 /** 955 * Stops the adb host side server. 956 * @return true if success 957 */ stopAdb()958 private synchronized boolean stopAdb() { 959 if (mAdbOsLocation == null) { 960 Log.e(ADB, 961 "Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$ 962 return false; 963 } 964 965 Process proc; 966 int status = -1; 967 968 try { 969 String[] command = new String[2]; 970 command[0] = mAdbOsLocation; 971 command[1] = "kill-server"; //$NON-NLS-1$ 972 proc = Runtime.getRuntime().exec(command); 973 status = proc.waitFor(); 974 } 975 catch (IOException ioe) { 976 // we'll return false; 977 } 978 catch (InterruptedException ie) { 979 // we'll return false; 980 } 981 982 if (status != 0) { 983 Log.w(DDMS, 984 "'adb kill-server' failed -- run manually if necessary"); //$NON-NLS-1$ 985 return false; 986 } 987 988 Log.d(DDMS, "'adb kill-server' succeeded"); //$NON-NLS-1$ 989 return true; 990 } 991 992 /** 993 * Get the stderr/stdout outputs of a process and return when the process is done. 994 * Both <b>must</b> be read or the process will block on windows. 995 * @param process The process to get the ouput from 996 * @param errorOutput The array to store the stderr output. cannot be null. 997 * @param stdOutput The array to store the stdout output. cannot be null. 998 * @param displayStdOut If true this will display stdout as well 999 * @param waitforReaders if true, this will wait for the reader threads. 1000 * @return the process return code. 1001 * @throws InterruptedException 1002 */ grabProcessOutput(final Process process, final ArrayList<String> errorOutput, final ArrayList<String> stdOutput, boolean waitforReaders)1003 private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput, 1004 final ArrayList<String> stdOutput, boolean waitforReaders) 1005 throws InterruptedException { 1006 assert errorOutput != null; 1007 assert stdOutput != null; 1008 // read the lines as they come. if null is returned, it's 1009 // because the process finished 1010 Thread t1 = new Thread("") { //$NON-NLS-1$ 1011 @Override 1012 public void run() { 1013 // create a buffer to read the stderr output 1014 InputStreamReader is = new InputStreamReader(process.getErrorStream()); 1015 BufferedReader errReader = new BufferedReader(is); 1016 1017 try { 1018 while (true) { 1019 String line = errReader.readLine(); 1020 if (line != null) { 1021 Log.e(ADB, line); 1022 errorOutput.add(line); 1023 } else { 1024 break; 1025 } 1026 } 1027 } catch (IOException e) { 1028 // do nothing. 1029 } 1030 } 1031 }; 1032 1033 Thread t2 = new Thread("") { //$NON-NLS-1$ 1034 @Override 1035 public void run() { 1036 InputStreamReader is = new InputStreamReader(process.getInputStream()); 1037 BufferedReader outReader = new BufferedReader(is); 1038 1039 try { 1040 while (true) { 1041 String line = outReader.readLine(); 1042 if (line != null) { 1043 Log.d(ADB, line); 1044 stdOutput.add(line); 1045 } else { 1046 break; 1047 } 1048 } 1049 } catch (IOException e) { 1050 // do nothing. 1051 } 1052 } 1053 }; 1054 1055 t1.start(); 1056 t2.start(); 1057 1058 // it looks like on windows process#waitFor() can return 1059 // before the thread have filled the arrays, so we wait for both threads and the 1060 // process itself. 1061 if (waitforReaders) { 1062 try { 1063 t1.join(); 1064 } catch (InterruptedException e) { 1065 } 1066 try { 1067 t2.join(); 1068 } catch (InterruptedException e) { 1069 } 1070 } 1071 1072 // get the return code from the process 1073 return process.waitFor(); 1074 } 1075 1076 /** 1077 * Returns the singleton lock used by this class to protect any access to the listener. 1078 * <p/> 1079 * This includes adding/removing listeners, but also notifying listeners of new bridges, 1080 * devices, and clients. 1081 */ getLock()1082 static Object getLock() { 1083 return sLock; 1084 } 1085 1086 /** 1087 * Instantiates sSocketAddr with the address of the host's adb process. 1088 */ initAdbSocketAddr()1089 private static void initAdbSocketAddr() { 1090 try { 1091 int adb_port = determineAndValidateAdbPort(); 1092 sHostAddr = InetAddress.getByName(ADB_HOST); 1093 sSocketAddr = new InetSocketAddress(sHostAddr, adb_port); 1094 } catch (UnknownHostException e) { 1095 // localhost should always be known. 1096 } 1097 } 1098 1099 /** 1100 * Determines port where ADB is expected by looking at an env variable. 1101 * <p/> 1102 * The value for the environment variable ANDROID_ADB_SERVER_PORT is validated, 1103 * IllegalArgumentException is thrown on illegal values. 1104 * <p/> 1105 * @return The port number where the host's adb should be expected or started. 1106 * @throws IllegalArgumentException if ANDROID_ADB_SERVER_PORT has a non-numeric value. 1107 */ determineAndValidateAdbPort()1108 private static int determineAndValidateAdbPort() { 1109 String adb_env_var; 1110 int result = ADB_PORT; 1111 try { 1112 adb_env_var = System.getenv(SERVER_PORT_ENV_VAR); 1113 1114 if (adb_env_var != null) { 1115 adb_env_var = adb_env_var.trim(); 1116 } 1117 1118 if (adb_env_var != null && adb_env_var.length() > 0) { 1119 // C tools (adb, emulator) accept hex and octal port numbers, so need to accept 1120 // them too. 1121 result = Integer.decode(adb_env_var); 1122 1123 if (result <= 0) { 1124 String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$ 1125 + ": must be >=0, got " //$NON-NLS-1$ 1126 + System.getenv(SERVER_PORT_ENV_VAR); 1127 throw new IllegalArgumentException(errMsg); 1128 } 1129 } 1130 } catch (NumberFormatException nfEx) { 1131 String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$ 1132 + ": illegal value '" //$NON-NLS-1$ 1133 + System.getenv(SERVER_PORT_ENV_VAR) + "'"; //$NON-NLS-1$ 1134 throw new IllegalArgumentException(errMsg); 1135 } catch (SecurityException secEx) { 1136 // A security manager has been installed that doesn't allow access to env vars. 1137 // So an environment variable might have been set, but we can't tell. 1138 // Let's log a warning and continue with ADB's default port. 1139 // The issue is that adb would be started (by the forked process having access 1140 // to the env vars) on the desired port, but within this process, we can't figure out 1141 // what that port is. However, a security manager not granting access to env vars 1142 // but allowing to fork is a rare and interesting configuration, so the right 1143 // thing seems to be to continue using the default port, as forking is likely to 1144 // fail later on in the scenario of the security manager. 1145 Log.w(DDMS, 1146 "No access to env variables allowed by current security manager. " //$NON-NLS-1$ 1147 + "If you've set ANDROID_ADB_SERVER_PORT: it's being ignored."); //$NON-NLS-1$ 1148 } 1149 return result; 1150 } 1151 1152 } 1153