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