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