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