• 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         try {
564             String[] command = new String[2];
565             command[0] = mAdbOsLocation;
566             command[1] = "version"; //$NON-NLS-1$
567             Log.d(DDMS, String.format("Checking '%1$s version'", mAdbOsLocation)); //$NON-NLS-1$
568             Process process = Runtime.getRuntime().exec(command);
569 
570             ArrayList<String> errorOutput = new ArrayList<String>();
571             ArrayList<String> stdOutput = new ArrayList<String>();
572             int status = grabProcessOutput(process, errorOutput, stdOutput,
573                     true /* waitForReaders */);
574 
575             if (status != 0) {
576                 StringBuilder builder = new StringBuilder("'adb version' failed!"); //$NON-NLS-1$
577                 for (String error : errorOutput) {
578                     builder.append('\n');
579                     builder.append(error);
580                 }
581                 Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
582             }
583 
584             // check both stdout and stderr
585             boolean versionFound = false;
586             for (String line : stdOutput) {
587                 versionFound = scanVersionLine(line);
588                 if (versionFound) {
589                     break;
590                 }
591             }
592             if (!versionFound) {
593                 for (String line : errorOutput) {
594                     versionFound = scanVersionLine(line);
595                     if (versionFound) {
596                         break;
597                     }
598                 }
599             }
600 
601             if (!versionFound) {
602                 // if we get here, we failed to parse the output.
603                 StringBuilder builder = new StringBuilder(
604                         "Failed to parse the output of 'adb version':\n"); //$NON-NLS-1$
605                 builder.append("Standard Output was:\n"); //$NON-NLS-1$
606                 for (String line : stdOutput) {
607                     builder.append(line);
608                     builder.append('\n');
609                 }
610                 builder.append("\nError Output was:\n"); //$NON-NLS-1$
611                 for (String line : errorOutput) {
612                     builder.append(line);
613                     builder.append('\n');
614                 }
615                 Log.logAndDisplay(LogLevel.ERROR, ADB, builder.toString());
616             }
617         } catch (IOException e) {
618             Log.logAndDisplay(LogLevel.ERROR, ADB,
619                     "Failed to get the adb version: " + e.getMessage()   //$NON-NLS-1$
620                     + " from '" + mAdbOsLocation + "' - exists="         //$NON-NLS-1$
621                             + (new File(mAdbOsLocation).exists()));
622         } catch (InterruptedException e) {
623         } finally {
624 
625         }
626     }
627 
628     /**
629      * Scans a line resulting from 'adb version' for a potential version number.
630      * <p/>
631      * If a version number is found, it checks the version number against what is expected
632      * by this version of ddms.
633      * <p/>
634      * Returns true when a version number has been found so that we can stop scanning,
635      * whether the version number is in the acceptable range or not.
636      *
637      * @param line The line to scan.
638      * @return True if a version number was found (whether it is acceptable or not).
639      */
640     @SuppressWarnings("all") // With Eclipse 3.6, replace by @SuppressWarnings("unused")
scanVersionLine(String line)641     private boolean scanVersionLine(String line) {
642         if (line != null) {
643             Matcher matcher = sAdbVersion.matcher(line);
644             if (matcher.matches()) {
645                 int majorVersion = Integer.parseInt(matcher.group(1));
646                 int minorVersion = Integer.parseInt(matcher.group(2));
647                 int microVersion = Integer.parseInt(matcher.group(3));
648 
649                 // check only the micro version for now.
650                 if (microVersion < ADB_VERSION_MICRO_MIN) {
651                     String message = String.format(
652                             "Required minimum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
653                             + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
654                             majorVersion, minorVersion, ADB_VERSION_MICRO_MIN,
655                             microVersion);
656                     Log.logAndDisplay(LogLevel.ERROR, ADB, message);
657                 } else if (ADB_VERSION_MICRO_MAX != -1 &&
658                         microVersion > ADB_VERSION_MICRO_MAX) {
659                     String message = String.format(
660                             "Required maximum version of adb: %1$d.%2$d.%3$d." //$NON-NLS-1$
661                             + "Current version is %1$d.%2$d.%4$d", //$NON-NLS-1$
662                             majorVersion, minorVersion, ADB_VERSION_MICRO_MAX,
663                             microVersion);
664                     Log.logAndDisplay(LogLevel.ERROR, ADB, message);
665                 } else {
666                     mVersionCheck = true;
667                 }
668 
669                 return true;
670             }
671         }
672         return false;
673     }
674 
675     /**
676      * Starts the debug bridge.
677      * @return true if success.
678      */
start()679     boolean start() {
680         if (mAdbOsLocation != null && (mVersionCheck == false || startAdb() == false)) {
681             return false;
682         }
683 
684         mStarted = true;
685 
686         // now that the bridge is connected, we start the underlying services.
687         mDeviceMonitor = new DeviceMonitor(this);
688         mDeviceMonitor.start();
689 
690         return true;
691     }
692 
693    /**
694      * Kills the debug bridge, and the adb host server.
695      * @return true if success
696      */
stop()697     boolean stop() {
698         // if we haven't started we return false;
699         if (mStarted == false) {
700             return false;
701         }
702 
703         // kill the monitoring services
704         mDeviceMonitor.stop();
705         mDeviceMonitor = null;
706 
707         if (stopAdb() == false) {
708             return false;
709         }
710 
711         mStarted = false;
712         return true;
713     }
714 
715     /**
716      * Restarts adb, but not the services around it.
717      * @return true if success.
718      */
restart()719     public boolean restart() {
720         if (mAdbOsLocation == null) {
721             Log.e(ADB,
722                     "Cannot restart adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
723             return false;
724         }
725 
726         if (mVersionCheck == false) {
727             Log.logAndDisplay(LogLevel.ERROR, ADB,
728                     "Attempting to restart adb, but version check failed!"); //$NON-NLS-1$
729             return false;
730         }
731         synchronized (this) {
732             stopAdb();
733 
734             boolean restart = startAdb();
735 
736             if (restart && mDeviceMonitor == null) {
737                 mDeviceMonitor = new DeviceMonitor(this);
738                 mDeviceMonitor.start();
739             }
740 
741             return restart;
742         }
743     }
744 
745     /**
746      * Notify the listener of a new {@link IDevice}.
747      * <p/>
748      * The notification of the listeners is done in a synchronized block. It is important to
749      * expect the listeners to potentially access various methods of {@link IDevice} as well as
750      * {@link #getDevices()} which use internal locks.
751      * <p/>
752      * For this reason, any call to this method from a method of {@link DeviceMonitor},
753      * {@link IDevice} which is also inside a synchronized block, should first synchronize on
754      * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
755      * @param device the new <code>IDevice</code>.
756      * @see #getLock()
757      */
deviceConnected(IDevice device)758     void deviceConnected(IDevice device) {
759         // because the listeners could remove themselves from the list while processing
760         // their event callback, we make a copy of the list and iterate on it instead of
761         // the main list.
762         // This mostly happens when the application quits.
763         IDeviceChangeListener[] listenersCopy = null;
764         synchronized (sLock) {
765             listenersCopy = sDeviceListeners.toArray(
766                     new IDeviceChangeListener[sDeviceListeners.size()]);
767         }
768 
769         // Notify the listeners
770         for (IDeviceChangeListener listener : listenersCopy) {
771             // we attempt to catch any exception so that a bad listener doesn't kill our
772             // thread
773             try {
774                 listener.deviceConnected(device);
775             } catch (Exception e) {
776                 Log.e(DDMS, e);
777             }
778         }
779     }
780 
781     /**
782      * Notify the listener of a disconnected {@link IDevice}.
783      * <p/>
784      * The notification of the listeners is done in a synchronized block. It is important to
785      * expect the listeners to potentially access various methods of {@link IDevice} as well as
786      * {@link #getDevices()} which use internal locks.
787      * <p/>
788      * For this reason, any call to this method from a method of {@link DeviceMonitor},
789      * {@link IDevice} which is also inside a synchronized block, should first synchronize on
790      * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
791      * @param device the disconnected <code>IDevice</code>.
792      * @see #getLock()
793      */
deviceDisconnected(IDevice device)794     void deviceDisconnected(IDevice device) {
795         // because the listeners could remove themselves from the list while processing
796         // their event callback, we make a copy of the list and iterate on it instead of
797         // the main list.
798         // This mostly happens when the application quits.
799         IDeviceChangeListener[] listenersCopy = null;
800         synchronized (sLock) {
801             listenersCopy = sDeviceListeners.toArray(
802                     new IDeviceChangeListener[sDeviceListeners.size()]);
803         }
804 
805         // Notify the listeners
806         for (IDeviceChangeListener listener : listenersCopy) {
807             // we attempt to catch any exception so that a bad listener doesn't kill our
808             // thread
809             try {
810                 listener.deviceDisconnected(device);
811             } catch (Exception e) {
812                 Log.e(DDMS, e);
813             }
814         }
815     }
816 
817     /**
818      * Notify the listener of a modified {@link IDevice}.
819      * <p/>
820      * The notification of the listeners is done in a synchronized block. It is important to
821      * expect the listeners to potentially access various methods of {@link IDevice} as well as
822      * {@link #getDevices()} which use internal locks.
823      * <p/>
824      * For this reason, any call to this method from a method of {@link DeviceMonitor},
825      * {@link IDevice} which is also inside a synchronized block, should first synchronize on
826      * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
827      * @param device the modified <code>IDevice</code>.
828      * @see #getLock()
829      */
deviceChanged(IDevice device, int changeMask)830     void deviceChanged(IDevice device, int changeMask) {
831         // because the listeners could remove themselves from the list while processing
832         // their event callback, we make a copy of the list and iterate on it instead of
833         // the main list.
834         // This mostly happens when the application quits.
835         IDeviceChangeListener[] listenersCopy = null;
836         synchronized (sLock) {
837             listenersCopy = sDeviceListeners.toArray(
838                     new IDeviceChangeListener[sDeviceListeners.size()]);
839         }
840 
841         // Notify the listeners
842         for (IDeviceChangeListener listener : listenersCopy) {
843             // we attempt to catch any exception so that a bad listener doesn't kill our
844             // thread
845             try {
846                 listener.deviceChanged(device, changeMask);
847             } catch (Exception e) {
848                 Log.e(DDMS, e);
849             }
850         }
851     }
852 
853     /**
854      * Notify the listener of a modified {@link Client}.
855      * <p/>
856      * The notification of the listeners is done in a synchronized block. It is important to
857      * expect the listeners to potentially access various methods of {@link IDevice} as well as
858      * {@link #getDevices()} which use internal locks.
859      * <p/>
860      * For this reason, any call to this method from a method of {@link DeviceMonitor},
861      * {@link IDevice} which is also inside a synchronized block, should first synchronize on
862      * the {@link AndroidDebugBridge} lock. Access to this lock is done through {@link #getLock()}.
863      * @param device the modified <code>Client</code>.
864      * @param changeMask the mask indicating what changed in the <code>Client</code>
865      * @see #getLock()
866      */
clientChanged(Client client, int changeMask)867     void clientChanged(Client client, int changeMask) {
868         // because the listeners could remove themselves from the list while processing
869         // their event callback, we make a copy of the list and iterate on it instead of
870         // the main list.
871         // This mostly happens when the application quits.
872         IClientChangeListener[] listenersCopy = null;
873         synchronized (sLock) {
874             listenersCopy = sClientListeners.toArray(
875                     new IClientChangeListener[sClientListeners.size()]);
876 
877         }
878 
879         // Notify the listeners
880         for (IClientChangeListener listener : listenersCopy) {
881             // we attempt to catch any exception so that a bad listener doesn't kill our
882             // thread
883             try {
884                 listener.clientChanged(client, changeMask);
885             } catch (Exception e) {
886                 Log.e(DDMS, e);
887             }
888         }
889     }
890 
891     /**
892      * Returns the {@link DeviceMonitor} object.
893      */
getDeviceMonitor()894     DeviceMonitor getDeviceMonitor() {
895         return mDeviceMonitor;
896     }
897 
898     /**
899      * Starts the adb host side server.
900      * @return true if success
901      */
startAdb()902     synchronized boolean startAdb() {
903         if (mAdbOsLocation == null) {
904             Log.e(ADB,
905                 "Cannot start adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
906             return false;
907         }
908 
909         Process proc;
910         int status = -1;
911 
912         try {
913             String[] command = new String[2];
914             command[0] = mAdbOsLocation;
915             command[1] = "start-server"; //$NON-NLS-1$
916             Log.d(DDMS,
917                     String.format("Launching '%1$s %2$s' to ensure ADB is running.", //$NON-NLS-1$
918                     mAdbOsLocation, command[1]));
919             ProcessBuilder processBuilder = new ProcessBuilder(command);
920             if (DdmPreferences.getUseAdbHost()) {
921                 String adbHostValue = DdmPreferences.getAdbHostValue();
922                 if (adbHostValue != null && adbHostValue.length() > 0) {
923                     //TODO : check that the String is a valid IP address
924                     Map<String, String> env = processBuilder.environment();
925                     env.put("ADBHOST", adbHostValue);
926                 }
927             }
928             proc = processBuilder.start();
929 
930             ArrayList<String> errorOutput = new ArrayList<String>();
931             ArrayList<String> stdOutput = new ArrayList<String>();
932             status = grabProcessOutput(proc, errorOutput, stdOutput,
933                     false /* waitForReaders */);
934 
935         } catch (IOException ioe) {
936             Log.d(DDMS, "Unable to run 'adb': " + ioe.getMessage()); //$NON-NLS-1$
937             // we'll return false;
938         } catch (InterruptedException ie) {
939             Log.d(DDMS, "Unable to run 'adb': " + ie.getMessage()); //$NON-NLS-1$
940             // we'll return false;
941         }
942 
943         if (status != 0) {
944             Log.w(DDMS,
945                     "'adb start-server' failed -- run manually if necessary"); //$NON-NLS-1$
946             return false;
947         }
948 
949         Log.d(DDMS, "'adb start-server' succeeded"); //$NON-NLS-1$
950 
951         return true;
952     }
953 
954     /**
955      * Stops the adb host side server.
956      * @return true if success
957      */
stopAdb()958     private synchronized boolean stopAdb() {
959         if (mAdbOsLocation == null) {
960             Log.e(ADB,
961                 "Cannot stop adb when AndroidDebugBridge is created without the location of adb."); //$NON-NLS-1$
962             return false;
963         }
964 
965         Process proc;
966         int status = -1;
967 
968         try {
969             String[] command = new String[2];
970             command[0] = mAdbOsLocation;
971             command[1] = "kill-server"; //$NON-NLS-1$
972             proc = Runtime.getRuntime().exec(command);
973             status = proc.waitFor();
974         }
975         catch (IOException ioe) {
976             // we'll return false;
977         }
978         catch (InterruptedException ie) {
979             // we'll return false;
980         }
981 
982         if (status != 0) {
983             Log.w(DDMS,
984                     "'adb kill-server' failed -- run manually if necessary"); //$NON-NLS-1$
985             return false;
986         }
987 
988         Log.d(DDMS, "'adb kill-server' succeeded"); //$NON-NLS-1$
989         return true;
990     }
991 
992     /**
993      * Get the stderr/stdout outputs of a process and return when the process is done.
994      * Both <b>must</b> be read or the process will block on windows.
995      * @param process The process to get the ouput from
996      * @param errorOutput The array to store the stderr output. cannot be null.
997      * @param stdOutput The array to store the stdout output. cannot be null.
998      * @param displayStdOut If true this will display stdout as well
999      * @param waitforReaders if true, this will wait for the reader threads.
1000      * @return the process return code.
1001      * @throws InterruptedException
1002      */
grabProcessOutput(final Process process, final ArrayList<String> errorOutput, final ArrayList<String> stdOutput, boolean waitforReaders)1003     private int grabProcessOutput(final Process process, final ArrayList<String> errorOutput,
1004             final ArrayList<String> stdOutput, boolean waitforReaders)
1005             throws InterruptedException {
1006         assert errorOutput != null;
1007         assert stdOutput != null;
1008         // read the lines as they come. if null is returned, it's
1009         // because the process finished
1010         Thread t1 = new Thread("") { //$NON-NLS-1$
1011             @Override
1012             public void run() {
1013                 // create a buffer to read the stderr output
1014                 InputStreamReader is = new InputStreamReader(process.getErrorStream());
1015                 BufferedReader errReader = new BufferedReader(is);
1016 
1017                 try {
1018                     while (true) {
1019                         String line = errReader.readLine();
1020                         if (line != null) {
1021                             Log.e(ADB, line);
1022                             errorOutput.add(line);
1023                         } else {
1024                             break;
1025                         }
1026                     }
1027                 } catch (IOException e) {
1028                     // do nothing.
1029                 }
1030             }
1031         };
1032 
1033         Thread t2 = new Thread("") { //$NON-NLS-1$
1034             @Override
1035             public void run() {
1036                 InputStreamReader is = new InputStreamReader(process.getInputStream());
1037                 BufferedReader outReader = new BufferedReader(is);
1038 
1039                 try {
1040                     while (true) {
1041                         String line = outReader.readLine();
1042                         if (line != null) {
1043                             Log.d(ADB, line);
1044                             stdOutput.add(line);
1045                         } else {
1046                             break;
1047                         }
1048                     }
1049                 } catch (IOException e) {
1050                     // do nothing.
1051                 }
1052             }
1053         };
1054 
1055         t1.start();
1056         t2.start();
1057 
1058         // it looks like on windows process#waitFor() can return
1059         // before the thread have filled the arrays, so we wait for both threads and the
1060         // process itself.
1061         if (waitforReaders) {
1062             try {
1063                 t1.join();
1064             } catch (InterruptedException e) {
1065             }
1066             try {
1067                 t2.join();
1068             } catch (InterruptedException e) {
1069             }
1070         }
1071 
1072         // get the return code from the process
1073         return process.waitFor();
1074     }
1075 
1076     /**
1077      * Returns the singleton lock used by this class to protect any access to the listener.
1078      * <p/>
1079      * This includes adding/removing listeners, but also notifying listeners of new bridges,
1080      * devices, and clients.
1081      */
getLock()1082     static Object getLock() {
1083         return sLock;
1084     }
1085 
1086     /**
1087      * Instantiates sSocketAddr with the address of the host's adb process.
1088      */
initAdbSocketAddr()1089     private static void initAdbSocketAddr() {
1090         try {
1091             int adb_port = determineAndValidateAdbPort();
1092             sHostAddr = InetAddress.getByName(ADB_HOST);
1093             sSocketAddr = new InetSocketAddress(sHostAddr, adb_port);
1094         } catch (UnknownHostException e) {
1095             // localhost should always be known.
1096         }
1097     }
1098 
1099     /**
1100      * Determines port where ADB is expected by looking at an env variable.
1101      * <p/>
1102      * The value for the environment variable ANDROID_ADB_SERVER_PORT is validated,
1103      * IllegalArgumentException is thrown on illegal values.
1104      * <p/>
1105      * @return The port number where the host's adb should be expected or started.
1106      * @throws IllegalArgumentException if ANDROID_ADB_SERVER_PORT has a non-numeric value.
1107      */
determineAndValidateAdbPort()1108     private static int determineAndValidateAdbPort() {
1109         String adb_env_var;
1110         int result = ADB_PORT;
1111         try {
1112             adb_env_var = System.getenv(SERVER_PORT_ENV_VAR);
1113 
1114             if (adb_env_var != null) {
1115                 adb_env_var = adb_env_var.trim();
1116             }
1117 
1118             if (adb_env_var != null && adb_env_var.length() > 0) {
1119                 // C tools (adb, emulator) accept hex and octal port numbers, so need to accept
1120                 // them too.
1121                 result = Integer.decode(adb_env_var);
1122 
1123                 if (result <= 0) {
1124                     String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$
1125                             + ": must be >=0, got " //$NON-NLS-1$
1126                             + System.getenv(SERVER_PORT_ENV_VAR);
1127                     throw new IllegalArgumentException(errMsg);
1128                 }
1129             }
1130         } catch (NumberFormatException nfEx) {
1131             String errMsg = "env var " + SERVER_PORT_ENV_VAR //$NON-NLS-1$
1132                     + ": illegal value '" //$NON-NLS-1$
1133                     + System.getenv(SERVER_PORT_ENV_VAR) + "'"; //$NON-NLS-1$
1134             throw new IllegalArgumentException(errMsg);
1135         } catch (SecurityException secEx) {
1136             // A security manager has been installed that doesn't allow access to env vars.
1137             // So an environment variable might have been set, but we can't tell.
1138             // Let's log a warning and continue with ADB's default port.
1139             // The issue is that adb would be started (by the forked process having access
1140             // to the env vars) on the desired port, but within this process, we can't figure out
1141             // what that port is. However, a security manager not granting access to env vars
1142             // but allowing to fork is a rare and interesting configuration, so the right
1143             // thing seems to be to continue using the default port, as forking is likely to
1144             // fail later on in the scenario of the security manager.
1145             Log.w(DDMS,
1146                     "No access to env variables allowed by current security manager. " //$NON-NLS-1$
1147                     + "If you've set ANDROID_ADB_SERVER_PORT: it's being ignored."); //$NON-NLS-1$
1148         }
1149         return result;
1150     }
1151 
1152 }
1153