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