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