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