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