• 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.AdbHelper.AdbResponse;
20 import com.android.ddmlib.ClientData.DebuggerStatus;
21 import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
22 import com.android.ddmlib.IDevice.DeviceState;
23 
24 import java.io.IOException;
25 import java.io.UnsupportedEncodingException;
26 import java.net.UnknownHostException;
27 import java.nio.ByteBuffer;
28 import java.nio.channels.AsynchronousCloseException;
29 import java.nio.channels.SelectionKey;
30 import java.nio.channels.Selector;
31 import java.nio.channels.SocketChannel;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Set;
37 
38 /**
39  * A Device monitor. This connects to the Android Debug Bridge and get device and
40  * debuggable process information from it.
41  */
42 final class DeviceMonitor {
43     private byte[] mLengthBuffer = new byte[4];
44     private byte[] mLengthBuffer2 = new byte[4];
45 
46     private boolean mQuit = false;
47 
48     private AndroidDebugBridge mServer;
49 
50     private SocketChannel mMainAdbConnection = null;
51     private boolean mMonitoring = false;
52     private int mConnectionAttempt = 0;
53     private int mRestartAttemptCount = 0;
54     private boolean mInitialDeviceListDone = false;
55 
56     private Selector mSelector;
57 
58     private final ArrayList<Device> mDevices = new ArrayList<Device>();
59 
60     private final ArrayList<Integer> mDebuggerPorts = new ArrayList<Integer>();
61 
62     private final HashMap<Client, Integer> mClientsToReopen = new HashMap<Client, Integer>();
63 
64     /**
65      * Creates a new {@link DeviceMonitor} object and links it to the running
66      * {@link AndroidDebugBridge} object.
67      * @param server the running {@link AndroidDebugBridge}.
68      */
DeviceMonitor(AndroidDebugBridge server)69     DeviceMonitor(AndroidDebugBridge server) {
70         mServer = server;
71 
72         mDebuggerPorts.add(DdmPreferences.getDebugPortBase());
73     }
74 
75     /**
76      * Starts the monitoring.
77      */
start()78     void start() {
79         new Thread("Device List Monitor") { //$NON-NLS-1$
80             @Override
81             public void run() {
82                 deviceMonitorLoop();
83             }
84         }.start();
85     }
86 
87     /**
88      * Stops the monitoring.
89      */
stop()90     void stop() {
91         mQuit = true;
92 
93         // wakeup the main loop thread by closing the main connection to adb.
94         try {
95             if (mMainAdbConnection != null) {
96                 mMainAdbConnection.close();
97             }
98         } catch (IOException e1) {
99         }
100 
101         // wake up the secondary loop by closing the selector.
102         if (mSelector != null) {
103             mSelector.wakeup();
104         }
105     }
106 
107 
108 
109     /**
110      * Returns if the monitor is currently connected to the debug bridge server.
111      * @return
112      */
isMonitoring()113     boolean isMonitoring() {
114         return mMonitoring;
115     }
116 
getConnectionAttemptCount()117     int getConnectionAttemptCount() {
118         return mConnectionAttempt;
119     }
120 
getRestartAttemptCount()121     int getRestartAttemptCount() {
122         return mRestartAttemptCount;
123     }
124 
125     /**
126      * Returns the devices.
127      */
getDevices()128     Device[] getDevices() {
129         synchronized (mDevices) {
130             return mDevices.toArray(new Device[mDevices.size()]);
131         }
132     }
133 
hasInitialDeviceList()134     boolean hasInitialDeviceList() {
135         return mInitialDeviceListDone;
136     }
137 
getServer()138     AndroidDebugBridge getServer() {
139         return mServer;
140     }
141 
addClientToDropAndReopen(Client client, int port)142     void addClientToDropAndReopen(Client client, int port) {
143         synchronized (mClientsToReopen) {
144             Log.d("DeviceMonitor",
145                     "Adding " + client + " to list of client to reopen (" + port +").");
146             if (mClientsToReopen.get(client) == null) {
147                 mClientsToReopen.put(client, port);
148             }
149         }
150         mSelector.wakeup();
151     }
152 
153     /**
154      * Monitors the devices. This connects to the Debug Bridge
155      */
deviceMonitorLoop()156     private void deviceMonitorLoop() {
157         do {
158             try {
159                 if (mMainAdbConnection == null) {
160                     Log.d("DeviceMonitor", "Opening adb connection");
161                     mMainAdbConnection = openAdbConnection();
162                     if (mMainAdbConnection == null) {
163                         mConnectionAttempt++;
164                         Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt);
165                         if (mConnectionAttempt > 10) {
166                             if (mServer.startAdb() == false) {
167                                 mRestartAttemptCount++;
168                                 Log.e("DeviceMonitor",
169                                         "adb restart attempts: " + mRestartAttemptCount);
170                             } else {
171                                 mRestartAttemptCount = 0;
172                             }
173                         }
174                         waitABit();
175                     } else {
176                         Log.d("DeviceMonitor", "Connected to adb for device monitoring");
177                         mConnectionAttempt = 0;
178                     }
179                 }
180 
181                 if (mMainAdbConnection != null && mMonitoring == false) {
182                     mMonitoring = sendDeviceListMonitoringRequest();
183                 }
184 
185                 if (mMonitoring) {
186                     // read the length of the incoming message
187                     int length = readLength(mMainAdbConnection, mLengthBuffer);
188 
189                     if (length >= 0) {
190                         // read the incoming message
191                         processIncomingDeviceData(length);
192 
193                         // flag the fact that we have build the list at least once.
194                         mInitialDeviceListDone = true;
195                     }
196                 }
197             } catch (AsynchronousCloseException ace) {
198                 // this happens because of a call to Quit. We do nothing, and the loop will break.
199             } catch (IOException ioe) {
200                 if (mQuit == false) {
201                     Log.e("DeviceMonitor", "Adb connection Error:" + ioe.getMessage());
202                     mMonitoring = false;
203                     if (mMainAdbConnection != null) {
204                         try {
205                             mMainAdbConnection.close();
206                         } catch (IOException ioe2) {
207                             // we can safely ignore that one.
208                         }
209                         mMainAdbConnection = null;
210                     }
211                 }
212             }
213         } while (mQuit == false);
214     }
215 
216     /**
217      * Sleeps for a little bit.
218      */
waitABit()219     private void waitABit() {
220         try {
221             Thread.sleep(1000);
222         } catch (InterruptedException e1) {
223         }
224     }
225 
226     /**
227      * Attempts to connect to the debug bridge server.
228      * @return a connect socket if success, null otherwise
229      */
openAdbConnection()230     private SocketChannel openAdbConnection() {
231         Log.d("DeviceMonitor", "Connecting to adb for Device List Monitoring...");
232 
233         SocketChannel adbChannel = null;
234         try {
235             adbChannel = SocketChannel.open(AndroidDebugBridge.sSocketAddr);
236             adbChannel.socket().setTcpNoDelay(true);
237         } catch (IOException e) {
238         }
239 
240         return adbChannel;
241     }
242 
243     /**
244      *
245      * @return
246      * @throws IOException
247      */
sendDeviceListMonitoringRequest()248     private boolean sendDeviceListMonitoringRequest() throws IOException {
249         byte[] request = AdbHelper.formAdbRequest("host:track-devices"); //$NON-NLS-1$
250 
251         if (AdbHelper.write(mMainAdbConnection, request) == false) {
252             Log.e("DeviceMonitor", "Sending Tracking request failed!");
253             mMainAdbConnection.close();
254             throw new IOException("Sending Tracking request failed!");
255         }
256 
257         AdbResponse resp = AdbHelper.readAdbResponse(mMainAdbConnection,
258                 false /* readDiagString */);
259 
260         if (resp.ioSuccess == false) {
261             Log.e("DeviceMonitor", "Failed to read the adb response!");
262             mMainAdbConnection.close();
263             throw new IOException("Failed to read the adb response!");
264         }
265 
266         if (resp.okay == false) {
267             // request was refused by adb!
268             Log.e("DeviceMonitor", "adb refused request: " + resp.message);
269         }
270 
271         return resp.okay;
272     }
273 
274     /**
275      * Processes an incoming device message from the socket
276      * @param socket
277      * @param length
278      * @throws IOException
279      */
processIncomingDeviceData(int length)280     private void processIncomingDeviceData(int length) throws IOException {
281         ArrayList<Device> list = new ArrayList<Device>();
282 
283         if (length > 0) {
284             byte[] buffer = new byte[length];
285             String result = read(mMainAdbConnection, buffer);
286 
287             String[] devices = result.split("\n"); // $NON-NLS-1$
288 
289             for (String d : devices) {
290                 String[] param = d.split("\t"); // $NON-NLS-1$
291                 if (param.length == 2) {
292                     // new adb uses only serial numbers to identify devices
293                     Device device = new Device(this, param[0] /*serialnumber*/,
294                             DeviceState.getState(param[1]));
295 
296                     //add the device to the list
297                     list.add(device);
298                 }
299             }
300         }
301 
302         // now merge the new devices with the old ones.
303         updateDevices(list);
304     }
305 
306     /**
307      *  Updates the device list with the new items received from the monitoring service.
308      */
updateDevices(ArrayList<Device> newList)309     private void updateDevices(ArrayList<Device> newList) {
310         // because we are going to call mServer.deviceDisconnected which will acquire this lock
311         // we lock it first, so that the AndroidDebugBridge lock is always locked first.
312         synchronized (AndroidDebugBridge.getLock()) {
313             synchronized (mDevices) {
314                 // For each device in the current list, we look for a matching the new list.
315                 // * if we find it, we update the current object with whatever new information
316                 //   there is
317                 //   (mostly state change, if the device becomes ready, we query for build info).
318                 //   We also remove the device from the new list to mark it as "processed"
319                 // * if we do not find it, we remove it from the current list.
320                 // Once this is done, the new list contains device we aren't monitoring yet, so we
321                 // add them to the list, and start monitoring them.
322 
323                 for (int d = 0 ; d < mDevices.size() ;) {
324                     Device device = mDevices.get(d);
325 
326                     // look for a similar device in the new list.
327                     int count = newList.size();
328                     boolean foundMatch = false;
329                     for (int dd = 0 ; dd < count ; dd++) {
330                         Device newDevice = newList.get(dd);
331                         // see if it matches in id and serial number.
332                         if (newDevice.getSerialNumber().equals(device.getSerialNumber())) {
333                             foundMatch = true;
334 
335                             // update the state if needed.
336                             if (device.getState() != newDevice.getState()) {
337                                 device.setState(newDevice.getState());
338                                 device.update(Device.CHANGE_STATE);
339 
340                                 // if the device just got ready/online, we need to start
341                                 // monitoring it.
342                                 if (device.isOnline()) {
343                                     if (AndroidDebugBridge.getClientSupport() == true) {
344                                         if (startMonitoringDevice(device) == false) {
345                                             Log.e("DeviceMonitor",
346                                                     "Failed to start monitoring "
347                                                     + device.getSerialNumber());
348                                         }
349                                     }
350 
351                                     if (device.getPropertyCount() == 0) {
352                                         queryNewDeviceForInfo(device);
353                                     }
354                                 }
355                             }
356 
357                             // remove the new device from the list since it's been used
358                             newList.remove(dd);
359                             break;
360                         }
361                     }
362 
363                     if (foundMatch == false) {
364                         // the device is gone, we need to remove it, and keep current index
365                         // to process the next one.
366                         removeDevice(device);
367                         mServer.deviceDisconnected(device);
368                     } else {
369                         // process the next one
370                         d++;
371                     }
372                 }
373 
374                 // at this point we should still have some new devices in newList, so we
375                 // process them.
376                 for (Device newDevice : newList) {
377                     // add them to the list
378                     mDevices.add(newDevice);
379                     mServer.deviceConnected(newDevice);
380 
381                     // start monitoring them.
382                     if (AndroidDebugBridge.getClientSupport() == true) {
383                         if (newDevice.isOnline()) {
384                             startMonitoringDevice(newDevice);
385                         }
386                     }
387 
388                     // look for their build info.
389                     if (newDevice.isOnline()) {
390                         queryNewDeviceForInfo(newDevice);
391                     }
392                 }
393             }
394         }
395         newList.clear();
396     }
397 
removeDevice(Device device)398     private void removeDevice(Device device) {
399         device.clearClientList();
400         mDevices.remove(device);
401 
402         SocketChannel channel = device.getClientMonitoringSocket();
403         if (channel != null) {
404             try {
405                 channel.close();
406             } catch (IOException e) {
407                 // doesn't really matter if the close fails.
408             }
409         }
410     }
411 
412     /**
413      * Queries a device for its build info.
414      * @param device the device to query.
415      */
queryNewDeviceForInfo(Device device)416     private void queryNewDeviceForInfo(Device device) {
417         // TODO: do this in a separate thread.
418         try {
419             // first get the list of properties.
420             device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
421                     new GetPropReceiver(device));
422 
423             // now get the emulator Virtual Device name (if applicable).
424             if (device.isEmulator()) {
425                 EmulatorConsole console = EmulatorConsole.getConsole(device);
426                 if (console != null) {
427                     device.setAvdName(console.getAvdName());
428                 }
429             }
430         } catch (IOException e) {
431             // if we can't get the build info, it doesn't matter too much
432         }
433     }
434 
435     /**
436      * Starts a monitoring service for a device.
437      * @param device the device to monitor.
438      * @return true if success.
439      */
startMonitoringDevice(Device device)440     private boolean startMonitoringDevice(Device device) {
441         SocketChannel socketChannel = openAdbConnection();
442 
443         if (socketChannel != null) {
444             try {
445                 boolean result = sendDeviceMonitoringRequest(socketChannel, device);
446                 if (result) {
447 
448                     if (mSelector == null) {
449                         startDeviceMonitorThread();
450                     }
451 
452                     device.setClientMonitoringSocket(socketChannel);
453 
454                     synchronized (mDevices) {
455                         // always wakeup before doing the register. The synchronized block
456                         // ensure that the selector won't select() before the end of this block.
457                         // @see deviceClientMonitorLoop
458                         mSelector.wakeup();
459 
460                         socketChannel.configureBlocking(false);
461                         socketChannel.register(mSelector, SelectionKey.OP_READ, device);
462                     }
463 
464                     return true;
465                 }
466             } catch (IOException e) {
467                 try {
468                     // attempt to close the socket if needed.
469                     socketChannel.close();
470                 } catch (IOException e1) {
471                     // we can ignore that one. It may already have been closed.
472                 }
473                 Log.d("DeviceMonitor",
474                         "Connection Failure when starting to monitor device '"
475                         + device + "' : " + e.getMessage());
476             }
477         }
478 
479         return false;
480     }
481 
startDeviceMonitorThread()482     private void startDeviceMonitorThread() throws IOException {
483         mSelector = Selector.open();
484         new Thread("Device Client Monitor") { //$NON-NLS-1$
485             @Override
486             public void run() {
487                 deviceClientMonitorLoop();
488             }
489         }.start();
490     }
491 
deviceClientMonitorLoop()492     private void deviceClientMonitorLoop() {
493         do {
494             try {
495                 // This synchronized block stops us from doing the select() if a new
496                 // Device is being added.
497                 // @see startMonitoringDevice()
498                 synchronized (mDevices) {
499                 }
500 
501                 int count = mSelector.select();
502 
503                 if (mQuit) {
504                     return;
505                 }
506 
507                 synchronized (mClientsToReopen) {
508                     if (mClientsToReopen.size() > 0) {
509                         Set<Client> clients = mClientsToReopen.keySet();
510                         MonitorThread monitorThread = MonitorThread.getInstance();
511 
512                         for (Client client : clients) {
513                             Device device = client.getDeviceImpl();
514                             int pid = client.getClientData().getPid();
515 
516                             monitorThread.dropClient(client, false /* notify */);
517 
518                             // This is kinda bad, but if we don't wait a bit, the client
519                             // will never answer the second handshake!
520                             waitABit();
521 
522                             int port = mClientsToReopen.get(client);
523 
524                             if (port == IDebugPortProvider.NO_STATIC_PORT) {
525                                 port = getNextDebuggerPort();
526                             }
527                             Log.d("DeviceMonitor", "Reopening " + client);
528                             openClient(device, pid, port, monitorThread);
529                             device.update(Device.CHANGE_CLIENT_LIST);
530                         }
531 
532                         mClientsToReopen.clear();
533                     }
534                 }
535 
536                 if (count == 0) {
537                     continue;
538                 }
539 
540                 Set<SelectionKey> keys = mSelector.selectedKeys();
541                 Iterator<SelectionKey> iter = keys.iterator();
542 
543                 while (iter.hasNext()) {
544                     SelectionKey key = iter.next();
545                     iter.remove();
546 
547                     if (key.isValid() && key.isReadable()) {
548                         Object attachment = key.attachment();
549 
550                         if (attachment instanceof Device) {
551                             Device device = (Device)attachment;
552 
553                             SocketChannel socket = device.getClientMonitoringSocket();
554 
555                             if (socket != null) {
556                                 try {
557                                     int length = readLength(socket, mLengthBuffer2);
558 
559                                     processIncomingJdwpData(device, socket, length);
560                                 } catch (IOException ioe) {
561                                     Log.d("DeviceMonitor",
562                                             "Error reading jdwp list: " + ioe.getMessage());
563                                     socket.close();
564 
565                                     // restart the monitoring of that device
566                                     synchronized (mDevices) {
567                                         if (mDevices.contains(device)) {
568                                             Log.d("DeviceMonitor",
569                                                     "Restarting monitoring service for " + device);
570                                             startMonitoringDevice(device);
571                                         }
572                                     }
573                                 }
574                             }
575                         }
576                     }
577                 }
578             } catch (IOException e) {
579                 if (mQuit == false) {
580 
581                 }
582             }
583 
584         } while (mQuit == false);
585     }
586 
sendDeviceMonitoringRequest(SocketChannel socket, Device device)587     private boolean sendDeviceMonitoringRequest(SocketChannel socket, Device device)
588             throws IOException {
589 
590         AdbHelper.setDevice(socket, device);
591 
592         byte[] request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$
593 
594         if (AdbHelper.write(socket, request) == false) {
595             Log.e("DeviceMonitor", "Sending jdwp tracking request failed!");
596             socket.close();
597             throw new IOException();
598         }
599 
600         AdbResponse resp = AdbHelper.readAdbResponse(socket, false /* readDiagString */);
601 
602         if (resp.ioSuccess == false) {
603             Log.e("DeviceMonitor", "Failed to read the adb response!");
604             socket.close();
605             throw new IOException();
606         }
607 
608         if (resp.okay == false) {
609             // request was refused by adb!
610             Log.e("DeviceMonitor", "adb refused request: " + resp.message);
611         }
612 
613         return resp.okay;
614     }
615 
processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length)616     private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length)
617             throws IOException {
618         if (length >= 0) {
619             // array for the current pids.
620             ArrayList<Integer> pidList = new ArrayList<Integer>();
621 
622             // get the string data if there are any
623             if (length > 0) {
624                 byte[] buffer = new byte[length];
625                 String result = read(monitorSocket, buffer);
626 
627                 // split each line in its own list and create an array of integer pid
628                 String[] pids = result.split("\n"); //$NON-NLS-1$
629 
630                 for (String pid : pids) {
631                     try {
632                         pidList.add(Integer.valueOf(pid));
633                     } catch (NumberFormatException nfe) {
634                         // looks like this pid is not really a number. Lets ignore it.
635                         continue;
636                     }
637                 }
638             }
639 
640             MonitorThread monitorThread = MonitorThread.getInstance();
641 
642             // Now we merge the current list with the old one.
643             // this is the same mechanism as the merging of the device list.
644 
645             // For each client in the current list, we look for a matching the pid in the new list.
646             // * if we find it, we do nothing, except removing the pid from its list,
647             //   to mark it as "processed"
648             // * if we do not find any match, we remove the client from the current list.
649             // Once this is done, the new list contains pids for which we don't have clients yet,
650             // so we create clients for them, add them to the list, and start monitoring them.
651 
652             List<Client> clients = device.getClientList();
653 
654             boolean changed = false;
655 
656             // because MonitorThread#dropClient acquires first the monitorThread lock and then the
657             // Device client list lock (when removing the Client from the list), we have to make
658             // sure we acquire the locks in the same order, since another thread (MonitorThread),
659             // could call dropClient itself.
660             synchronized (monitorThread) {
661                 synchronized (clients) {
662                     for (int c = 0 ; c < clients.size() ;) {
663                         Client client = clients.get(c);
664                         int pid = client.getClientData().getPid();
665 
666                         // look for a matching pid
667                         Integer match = null;
668                         for (Integer matchingPid : pidList) {
669                             if (pid == matchingPid.intValue()) {
670                                 match = matchingPid;
671                                 break;
672                             }
673                         }
674 
675                         if (match != null) {
676                             pidList.remove(match);
677                             c++; // move on to the next client.
678                         } else {
679                             // we need to drop the client. the client will remove itself from the
680                             // list of its device which is 'clients', so there's no need to
681                             // increment c.
682                             // We ask the monitor thread to not send notification, as we'll do
683                             // it once at the end.
684                             monitorThread.dropClient(client, false /* notify */);
685                             changed = true;
686                         }
687                     }
688                 }
689             }
690 
691             // at this point whatever pid is left in the list needs to be converted into Clients.
692             for (int newPid : pidList) {
693                 openClient(device, newPid, getNextDebuggerPort(), monitorThread);
694                 changed = true;
695             }
696 
697             if (changed) {
698                 mServer.deviceChanged(device, Device.CHANGE_CLIENT_LIST);
699             }
700         }
701     }
702 
703     /**
704      * Opens and creates a new client.
705      * @return
706      */
openClient(Device device, int pid, int port, MonitorThread monitorThread)707     private void openClient(Device device, int pid, int port, MonitorThread monitorThread) {
708 
709         SocketChannel clientSocket;
710         try {
711             clientSocket = AdbHelper.createPassThroughConnection(
712                     AndroidDebugBridge.sSocketAddr, device, pid);
713 
714             // required for Selector
715             clientSocket.configureBlocking(false);
716         } catch (UnknownHostException uhe) {
717             Log.d("DeviceMonitor", "Unknown Jdwp pid: " + pid);
718             return;
719         } catch (IOException ioe) {
720             Log.w("DeviceMonitor",
721                     "Failed to connect to client '" + pid + "': " + ioe.getMessage());
722             return ;
723         }
724 
725         createClient(device, pid, clientSocket, port, monitorThread);
726     }
727 
728     /**
729      * Creates a client and register it to the monitor thread
730      * @param device
731      * @param pid
732      * @param socket
733      * @param debuggerPort the debugger port.
734      * @param monitorThread the {@link MonitorThread} object.
735      */
createClient(Device device, int pid, SocketChannel socket, int debuggerPort, MonitorThread monitorThread)736     private void createClient(Device device, int pid, SocketChannel socket, int debuggerPort,
737             MonitorThread monitorThread) {
738 
739         /*
740          * Successfully connected to something. Create a Client object, add
741          * it to the list, and initiate the JDWP handshake.
742          */
743 
744         Client client = new Client(device, socket, pid);
745 
746         if (client.sendHandshake()) {
747             try {
748                 if (AndroidDebugBridge.getClientSupport()) {
749                     client.listenForDebugger(debuggerPort);
750                 }
751             } catch (IOException ioe) {
752                 client.getClientData().setDebuggerConnectionStatus(DebuggerStatus.ERROR);
753                 Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger");
754                 // oh well
755             }
756 
757             client.requestAllocationStatus();
758         } else {
759             Log.e("ddms", "Handshake with " + client + " failed!");
760             /*
761              * The handshake send failed. We could remove it now, but if the
762              * failure is "permanent" we'll just keep banging on it and
763              * getting the same result. Keep it in the list with its "error"
764              * state so we don't try to reopen it.
765              */
766         }
767 
768         if (client.isValid()) {
769             device.addClient(client);
770             monitorThread.addClient(client);
771         } else {
772             client = null;
773         }
774     }
775 
getNextDebuggerPort()776     private int getNextDebuggerPort() {
777         // get the first port and remove it
778         synchronized (mDebuggerPorts) {
779             if (mDebuggerPorts.size() > 0) {
780                 int port = mDebuggerPorts.get(0);
781 
782                 // remove it.
783                 mDebuggerPorts.remove(0);
784 
785                 // if there's nothing left, add the next port to the list
786                 if (mDebuggerPorts.size() == 0) {
787                     mDebuggerPorts.add(port+1);
788                 }
789 
790                 return port;
791             }
792         }
793 
794         return -1;
795     }
796 
addPortToAvailableList(int port)797     void addPortToAvailableList(int port) {
798         if (port > 0) {
799             synchronized (mDebuggerPorts) {
800                 // because there could be case where clients are closed twice, we have to make
801                 // sure the port number is not already in the list.
802                 if (mDebuggerPorts.indexOf(port) == -1) {
803                     // add the port to the list while keeping it sorted. It's not like there's
804                     // going to be tons of objects so we do it linearly.
805                     int count = mDebuggerPorts.size();
806                     for (int i = 0 ; i < count ; i++) {
807                         if (port < mDebuggerPorts.get(i)) {
808                             mDebuggerPorts.add(i, port);
809                             break;
810                         }
811                     }
812                     // TODO: check if we can compact the end of the list.
813                 }
814             }
815         }
816     }
817 
818     /**
819      * Reads the length of the next message from a socket.
820      * @param socket The {@link SocketChannel} to read from.
821      * @return the length, or 0 (zero) if no data is available from the socket.
822      * @throws IOException if the connection failed.
823      */
readLength(SocketChannel socket, byte[] buffer)824     private int readLength(SocketChannel socket, byte[] buffer) throws IOException {
825         String msg = read(socket, buffer);
826 
827         if (msg != null) {
828             try {
829                 return Integer.parseInt(msg, 16);
830             } catch (NumberFormatException nfe) {
831                 // we'll throw an exception below.
832             }
833        }
834 
835         // we receive something we can't read. It's better to reset the connection at this point.
836         throw new IOException("Unable to read length");
837     }
838 
839     /**
840      * Fills a buffer from a socket.
841      * @param socket
842      * @param buffer
843      * @return the content of the buffer as a string, or null if it failed to convert the buffer.
844      * @throws IOException
845      */
read(SocketChannel socket, byte[] buffer)846     private String read(SocketChannel socket, byte[] buffer) throws IOException {
847         ByteBuffer buf = ByteBuffer.wrap(buffer, 0, buffer.length);
848 
849         while (buf.position() != buf.limit()) {
850             int count;
851 
852             count = socket.read(buf);
853             if (count < 0) {
854                 throw new IOException("EOF");
855             }
856         }
857 
858         try {
859             return new String(buffer, 0, buf.position(), AdbHelper.DEFAULT_ENCODING);
860         } catch (UnsupportedEncodingException e) {
861             // we'll return null below.
862         }
863 
864         return null;
865     }
866 
867 }
868