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