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