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