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