• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
bridgeChanged(AndroidDebugBridge bridge)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          */
deviceConnected(IDevice device)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          */
deviceDisconnected(IDevice device)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          */
deviceChanged(IDevice device, int changeMask)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          */
clientChanged(Client client, int changeMask)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      */
init(boolean clientSupport)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      */
terminate()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      */
getClientSupport()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      */
createBridge()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      */
createBridge(String osLocation, boolean forceNewBridge)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      */
getBridge()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      */
disconnectBridge()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      */
addDebugBridgeChangeListener(IDebugBridgeChangeListener listener)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      */
removeDebugBridgeChangeListener(IDebugBridgeChangeListener listener)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      */
addDeviceChangeListener(IDeviceChangeListener listener)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      */
removeDeviceChangeListener(IDeviceChangeListener listener)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      */
addClientChangeListener(IClientChangeListener listener)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      */
removeClientChangeListener(IClientChangeListener listener)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      */
getDevices()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      */
hasInitialDeviceList()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      */
setSelectedClient(Client selectedClient)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      */
isConnected()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      */
getConnectionAttemptCount()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      */
getRestartAttemptCount()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      */
AndroidDebugBridge(String osLocation)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      */
AndroidDebugBridge()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      */
checkAdbVersion()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      */
scanVersionLine(String line)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      */
start()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      */
stop()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      */
restart()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      */
deviceConnected(IDevice device)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      */
deviceDisconnected(IDevice device)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      */
deviceChanged(IDevice device, int changeMask)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      */
clientChanged(Client client, int changeMask)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      */
getDeviceMonitor()870     DeviceMonitor getDeviceMonitor() {
871         return mDeviceMonitor;
872     }
873 
874     /**
875      * Starts the adb host side server.
876      * @return true if success
877      */
startAdb()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      */
stopAdb()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      */
grabProcessOutput(final Process process, final ArrayList<String> errorOutput, final ArrayList<String> stdOutput, boolean waitforReaders)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      */
getLock()1049     static Object getLock() {
1050         return sLock;
1051     }
1052 }
1053