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.log.LogReceiver; 20 21 import java.io.IOException; 22 import java.io.UnsupportedEncodingException; 23 import java.net.InetSocketAddress; 24 import java.nio.ByteBuffer; 25 import java.nio.ByteOrder; 26 import java.nio.channels.SocketChannel; 27 28 /** 29 * Helper class to handle requests and connections to adb. 30 * <p/>{@link DebugBridgeServer} is the public API to connection to adb, while {@link AdbHelper} 31 * does the low level stuff. 32 * <p/>This currently uses spin-wait non-blocking I/O. A Selector would be more efficient, 33 * but seems like overkill for what we're doing here. 34 */ 35 final class AdbHelper { 36 37 // public static final long kOkay = 0x59414b4fL; 38 // public static final long kFail = 0x4c494146L; 39 40 static final int WAIT_TIME = 5; // spin-wait sleep, in ms 41 42 static final String DEFAULT_ENCODING = "ISO-8859-1"; //$NON-NLS-1$ 43 44 /** do not instantiate */ AdbHelper()45 private AdbHelper() { 46 } 47 48 /** 49 * Response from ADB. 50 */ 51 static class AdbResponse { AdbResponse()52 public AdbResponse() { 53 message = ""; 54 } 55 56 public boolean okay; // first 4 bytes in response were "OKAY"? 57 58 public String message; // diagnostic string if #okay is false 59 } 60 61 /** 62 * Create and connect a new pass-through socket, from the host to a port on 63 * the device. 64 * 65 * @param adbSockAddr 66 * @param device the device to connect to. Can be null in which case the connection will be 67 * to the first available device. 68 * @param devicePort the port we're opening 69 * @throws TimeoutException in case of timeout on the connection. 70 * @throws IOException in case of I/O error on the connection. 71 * @throws AdbCommandRejectedException if adb rejects the command 72 */ open(InetSocketAddress adbSockAddr, Device device, int devicePort)73 public static SocketChannel open(InetSocketAddress adbSockAddr, 74 Device device, int devicePort) 75 throws IOException, TimeoutException, AdbCommandRejectedException { 76 77 SocketChannel adbChan = SocketChannel.open(adbSockAddr); 78 try { 79 adbChan.socket().setTcpNoDelay(true); 80 adbChan.configureBlocking(false); 81 82 // if the device is not -1, then we first tell adb we're looking to 83 // talk to a specific device 84 setDevice(adbChan, device); 85 86 byte[] req = createAdbForwardRequest(null, devicePort); 87 // Log.hexDump(req); 88 89 write(adbChan, req); 90 91 AdbResponse resp = readAdbResponse(adbChan, false); 92 if (resp.okay == false) { 93 throw new AdbCommandRejectedException(resp.message); 94 } 95 96 adbChan.configureBlocking(true); 97 } catch (TimeoutException e) { 98 adbChan.close(); 99 throw e; 100 } catch (IOException e) { 101 adbChan.close(); 102 throw e; 103 } 104 105 return adbChan; 106 } 107 108 /** 109 * Creates and connects a new pass-through socket, from the host to a port on 110 * the device. 111 * 112 * @param adbSockAddr 113 * @param device the device to connect to. Can be null in which case the connection will be 114 * to the first available device. 115 * @param pid the process pid to connect to. 116 * @throws TimeoutException in case of timeout on the connection. 117 * @throws AdbCommandRejectedException if adb rejects the command 118 * @throws IOException in case of I/O error on the connection. 119 */ createPassThroughConnection(InetSocketAddress adbSockAddr, Device device, int pid)120 public static SocketChannel createPassThroughConnection(InetSocketAddress adbSockAddr, 121 Device device, int pid) 122 throws TimeoutException, AdbCommandRejectedException, IOException { 123 124 SocketChannel adbChan = SocketChannel.open(adbSockAddr); 125 try { 126 adbChan.socket().setTcpNoDelay(true); 127 adbChan.configureBlocking(false); 128 129 // if the device is not -1, then we first tell adb we're looking to 130 // talk to a specific device 131 setDevice(adbChan, device); 132 133 byte[] req = createJdwpForwardRequest(pid); 134 // Log.hexDump(req); 135 136 write(adbChan, req); 137 138 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 139 if (resp.okay == false) { 140 throw new AdbCommandRejectedException(resp.message); 141 } 142 143 adbChan.configureBlocking(true); 144 } catch (TimeoutException e) { 145 adbChan.close(); 146 throw e; 147 } catch (IOException e) { 148 adbChan.close(); 149 throw e; 150 } 151 152 return adbChan; 153 } 154 155 /** 156 * Creates a port forwarding request for adb. This returns an array 157 * containing "####tcp:{port}:{addStr}". 158 * @param addrStr the host. Can be null. 159 * @param port the port on the device. This does not need to be numeric. 160 */ createAdbForwardRequest(String addrStr, int port)161 private static byte[] createAdbForwardRequest(String addrStr, int port) { 162 String reqStr; 163 164 if (addrStr == null) 165 reqStr = "tcp:" + port; 166 else 167 reqStr = "tcp:" + port + ":" + addrStr; 168 return formAdbRequest(reqStr); 169 } 170 171 /** 172 * Creates a port forwarding request to a jdwp process. This returns an array 173 * containing "####jwdp:{pid}". 174 * @param pid the jdwp process pid on the device. 175 */ createJdwpForwardRequest(int pid)176 private static byte[] createJdwpForwardRequest(int pid) { 177 String reqStr = String.format("jdwp:%1$d", pid); //$NON-NLS-1$ 178 return formAdbRequest(reqStr); 179 } 180 181 /** 182 * Create an ASCII string preceeded by four hex digits. The opening "####" 183 * is the length of the rest of the string, encoded as ASCII hex (case 184 * doesn't matter). "port" and "host" are what we want to forward to. If 185 * we're on the host side connecting into the device, "addrStr" should be 186 * null. 187 */ formAdbRequest(String req)188 static byte[] formAdbRequest(String req) { 189 String resultStr = String.format("%04X%s", req.length(), req); //$NON-NLS-1$ 190 byte[] result; 191 try { 192 result = resultStr.getBytes(DEFAULT_ENCODING); 193 } catch (UnsupportedEncodingException uee) { 194 uee.printStackTrace(); // not expected 195 return null; 196 } 197 assert result.length == req.length() + 4; 198 return result; 199 } 200 201 /** 202 * Reads the response from ADB after a command. 203 * @param chan The socket channel that is connected to adb. 204 * @param readDiagString If true, we're expecting an OKAY response to be 205 * followed by a diagnostic string. Otherwise, we only expect the 206 * diagnostic string to follow a FAIL. 207 * @throws TimeoutException in case of timeout on the connection. 208 * @throws IOException in case of I/O error on the connection. 209 */ readAdbResponse(SocketChannel chan, boolean readDiagString)210 static AdbResponse readAdbResponse(SocketChannel chan, boolean readDiagString) 211 throws TimeoutException, IOException { 212 213 AdbResponse resp = new AdbResponse(); 214 215 byte[] reply = new byte[4]; 216 read(chan, reply); 217 218 if (isOkay(reply)) { 219 resp.okay = true; 220 } else { 221 readDiagString = true; // look for a reason after the FAIL 222 resp.okay = false; 223 } 224 225 // not a loop -- use "while" so we can use "break" 226 try { 227 while (readDiagString) { 228 // length string is in next 4 bytes 229 byte[] lenBuf = new byte[4]; 230 read(chan, lenBuf); 231 232 String lenStr = replyToString(lenBuf); 233 234 int len; 235 try { 236 len = Integer.parseInt(lenStr, 16); 237 } catch (NumberFormatException nfe) { 238 Log.w("ddms", "Expected digits, got '" + lenStr + "': " 239 + lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " " 240 + lenBuf[3]); 241 Log.w("ddms", "reply was " + replyToString(reply)); 242 break; 243 } 244 245 byte[] msg = new byte[len]; 246 read(chan, msg); 247 248 resp.message = replyToString(msg); 249 Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='" 250 + resp.message + "'"); 251 252 break; 253 } 254 } catch (Exception e) { 255 // ignore those, since it's just reading the diagnose string, the response will 256 // contain okay==false anyway. 257 } 258 259 return resp; 260 } 261 262 /** 263 * Retrieve the frame buffer from the device. 264 * @throws TimeoutException in case of timeout on the connection. 265 * @throws AdbCommandRejectedException if adb rejects the command 266 * @throws IOException in case of I/O error on the connection. 267 */ getFrameBuffer(InetSocketAddress adbSockAddr, Device device)268 static RawImage getFrameBuffer(InetSocketAddress adbSockAddr, Device device) 269 throws TimeoutException, AdbCommandRejectedException, IOException { 270 271 RawImage imageParams = new RawImage(); 272 byte[] request = formAdbRequest("framebuffer:"); //$NON-NLS-1$ 273 byte[] nudge = { 274 0 275 }; 276 byte[] reply; 277 278 SocketChannel adbChan = null; 279 try { 280 adbChan = SocketChannel.open(adbSockAddr); 281 adbChan.configureBlocking(false); 282 283 // if the device is not -1, then we first tell adb we're looking to talk 284 // to a specific device 285 setDevice(adbChan, device); 286 287 write(adbChan, request); 288 289 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 290 if (resp.okay == false) { 291 throw new AdbCommandRejectedException(resp.message); 292 } 293 294 // first the protocol version. 295 reply = new byte[4]; 296 read(adbChan, reply); 297 298 ByteBuffer buf = ByteBuffer.wrap(reply); 299 buf.order(ByteOrder.LITTLE_ENDIAN); 300 301 int version = buf.getInt(); 302 303 // get the header size (this is a count of int) 304 int headerSize = RawImage.getHeaderSize(version); 305 306 // read the header 307 reply = new byte[headerSize * 4]; 308 read(adbChan, reply); 309 310 buf = ByteBuffer.wrap(reply); 311 buf.order(ByteOrder.LITTLE_ENDIAN); 312 313 // fill the RawImage with the header 314 if (imageParams.readHeader(version, buf) == false) { 315 Log.e("Screenshot", "Unsupported protocol: " + version); 316 return null; 317 } 318 319 Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size=" 320 + imageParams.size + ", width=" + imageParams.width 321 + ", height=" + imageParams.height); 322 323 write(adbChan, nudge); 324 325 reply = new byte[imageParams.size]; 326 read(adbChan, reply); 327 328 imageParams.data = reply; 329 } finally { 330 if (adbChan != null) { 331 adbChan.close(); 332 } 333 } 334 335 return imageParams; 336 } 337 338 /** 339 * Executes a shell command on the device and retrieve the output. The output is 340 * handed to <var>rcvr</var> as it arrives. 341 * 342 * @param adbSockAddr the {@link InetSocketAddress} to adb. 343 * @param command the shell command to execute 344 * @param device the {@link IDevice} on which to execute the command. 345 * @param rcvr the {@link IShellOutputReceiver} that will receives the output of the shell 346 * command 347 * @param maxTimeToOutputResponse max time between command output. If more time passes 348 * between command output, the method will throw 349 * {@link ShellCommandUnresponsiveException}. A value of 0 means the method will 350 * wait forever for command output and never throw. 351 * @throws TimeoutException in case of timeout on the connection when sending the command. 352 * @throws AdbCommandRejectedException if adb rejects the command 353 * @throws ShellCommandUnresponsiveException in case the shell command doesn't send any output 354 * for a period longer than <var>maxTimeToOutputResponse</var>. 355 * @throws IOException in case of I/O error on the connection. 356 * 357 * @see DdmPreferences#getTimeOut() 358 */ executeRemoteCommand(InetSocketAddress adbSockAddr, String command, IDevice device, IShellOutputReceiver rcvr, int maxTimeToOutputResponse)359 static void executeRemoteCommand(InetSocketAddress adbSockAddr, 360 String command, IDevice device, IShellOutputReceiver rcvr, int maxTimeToOutputResponse) 361 throws TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException, 362 IOException { 363 Log.v("ddms", "execute: running " + command); 364 365 SocketChannel adbChan = null; 366 try { 367 adbChan = SocketChannel.open(adbSockAddr); 368 adbChan.configureBlocking(false); 369 370 // if the device is not -1, then we first tell adb we're looking to 371 // talk 372 // to a specific device 373 setDevice(adbChan, device); 374 375 byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$ 376 write(adbChan, request); 377 378 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 379 if (resp.okay == false) { 380 Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message); 381 throw new AdbCommandRejectedException(resp.message); 382 } 383 384 byte[] data = new byte[16384]; 385 ByteBuffer buf = ByteBuffer.wrap(data); 386 int timeToResponseCount = 0; 387 while (true) { 388 int count; 389 390 if (rcvr != null && rcvr.isCancelled()) { 391 Log.v("ddms", "execute: cancelled"); 392 break; 393 } 394 395 count = adbChan.read(buf); 396 if (count < 0) { 397 // we're at the end, we flush the output 398 rcvr.flush(); 399 Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: " 400 + count); 401 break; 402 } else if (count == 0) { 403 try { 404 int wait = WAIT_TIME * 5; 405 timeToResponseCount += wait; 406 if (maxTimeToOutputResponse > 0 && 407 timeToResponseCount > maxTimeToOutputResponse) { 408 throw new ShellCommandUnresponsiveException(); 409 } 410 Thread.sleep(wait); 411 } catch (InterruptedException ie) { 412 } 413 } else { 414 // reset timeout 415 timeToResponseCount = 0; 416 417 // send data to receiver if present 418 if (rcvr != null) { 419 rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position()); 420 } 421 buf.rewind(); 422 } 423 } 424 } finally { 425 if (adbChan != null) { 426 adbChan.close(); 427 } 428 Log.v("ddms", "execute: returning"); 429 } 430 } 431 432 /** 433 * Runs the Event log service on the {@link Device}, and provides its output to the 434 * {@link LogReceiver}. 435 * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true. 436 * @param adbSockAddr the socket address to connect to adb 437 * @param device the Device on which to run the service 438 * @param rcvr the {@link LogReceiver} to receive the log output 439 * @throws TimeoutException in case of timeout on the connection. 440 * @throws AdbCommandRejectedException if adb rejects the command 441 * @throws IOException in case of I/O error on the connection. 442 */ runEventLogService(InetSocketAddress adbSockAddr, Device device, LogReceiver rcvr)443 public static void runEventLogService(InetSocketAddress adbSockAddr, Device device, 444 LogReceiver rcvr) throws TimeoutException, AdbCommandRejectedException, IOException { 445 runLogService(adbSockAddr, device, "events", rcvr); //$NON-NLS-1$ 446 } 447 448 /** 449 * Runs a log service on the {@link Device}, and provides its output to the {@link LogReceiver}. 450 * <p/>This call is blocking until {@link LogReceiver#isCancelled()} returns true. 451 * @param adbSockAddr the socket address to connect to adb 452 * @param device the Device on which to run the service 453 * @param logName the name of the log file to output 454 * @param rcvr the {@link LogReceiver} to receive the log output 455 * @throws TimeoutException in case of timeout on the connection. 456 * @throws AdbCommandRejectedException if adb rejects the command 457 * @throws IOException in case of I/O error on the connection. 458 */ runLogService(InetSocketAddress adbSockAddr, Device device, String logName, LogReceiver rcvr)459 public static void runLogService(InetSocketAddress adbSockAddr, Device device, String logName, 460 LogReceiver rcvr) throws TimeoutException, AdbCommandRejectedException, IOException { 461 SocketChannel adbChan = null; 462 463 try { 464 adbChan = SocketChannel.open(adbSockAddr); 465 adbChan.configureBlocking(false); 466 467 // if the device is not -1, then we first tell adb we're looking to talk 468 // to a specific device 469 setDevice(adbChan, device); 470 471 byte[] request = formAdbRequest("log:" + logName); 472 write(adbChan, request); 473 474 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 475 if (resp.okay == false) { 476 throw new AdbCommandRejectedException(resp.message); 477 } 478 479 byte[] data = new byte[16384]; 480 ByteBuffer buf = ByteBuffer.wrap(data); 481 while (true) { 482 int count; 483 484 if (rcvr != null && rcvr.isCancelled()) { 485 break; 486 } 487 488 count = adbChan.read(buf); 489 if (count < 0) { 490 break; 491 } else if (count == 0) { 492 try { 493 Thread.sleep(WAIT_TIME * 5); 494 } catch (InterruptedException ie) { 495 } 496 } else { 497 if (rcvr != null) { 498 rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position()); 499 } 500 buf.rewind(); 501 } 502 } 503 } finally { 504 if (adbChan != null) { 505 adbChan.close(); 506 } 507 } 508 } 509 510 /** 511 * Creates a port forwarding between a local and a remote port. 512 * @param adbSockAddr the socket address to connect to adb 513 * @param device the device on which to do the port fowarding 514 * @param localPort the local port to forward 515 * @param remotePort the remote port. 516 * @throws TimeoutException in case of timeout on the connection. 517 * @throws AdbCommandRejectedException if adb rejects the command 518 * @throws IOException in case of I/O error on the connection. 519 */ createForward(InetSocketAddress adbSockAddr, Device device, int localPort, int remotePort)520 public static void createForward(InetSocketAddress adbSockAddr, Device device, int localPort, 521 int remotePort) throws TimeoutException, AdbCommandRejectedException, IOException { 522 523 SocketChannel adbChan = null; 524 try { 525 adbChan = SocketChannel.open(adbSockAddr); 526 adbChan.configureBlocking(false); 527 528 byte[] request = formAdbRequest(String.format( 529 "host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$ 530 device.getSerialNumber(), localPort, remotePort)); 531 532 write(adbChan, request); 533 534 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 535 if (resp.okay == false) { 536 Log.w("create-forward", "Error creating forward: " + resp.message); 537 throw new AdbCommandRejectedException(resp.message); 538 } 539 } finally { 540 if (adbChan != null) { 541 adbChan.close(); 542 } 543 } 544 } 545 546 /** 547 * Remove a port forwarding between a local and a remote port. 548 * @param adbSockAddr the socket address to connect to adb 549 * @param device the device on which to remove the port fowarding 550 * @param localPort the local port of the forward 551 * @param remotePort the remote port. 552 * @throws TimeoutException in case of timeout on the connection. 553 * @throws AdbCommandRejectedException if adb rejects the command 554 * @throws IOException in case of I/O error on the connection. 555 */ removeForward(InetSocketAddress adbSockAddr, Device device, int localPort, int remotePort)556 public static void removeForward(InetSocketAddress adbSockAddr, Device device, int localPort, 557 int remotePort) throws TimeoutException, AdbCommandRejectedException, IOException { 558 559 SocketChannel adbChan = null; 560 try { 561 adbChan = SocketChannel.open(adbSockAddr); 562 adbChan.configureBlocking(false); 563 564 byte[] request = formAdbRequest(String.format( 565 "host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$ 566 device.getSerialNumber(), localPort, remotePort)); 567 568 write(adbChan, request); 569 570 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 571 if (resp.okay == false) { 572 Log.w("remove-forward", "Error creating forward: " + resp.message); 573 throw new AdbCommandRejectedException(resp.message); 574 } 575 } finally { 576 if (adbChan != null) { 577 adbChan.close(); 578 } 579 } 580 } 581 582 /** 583 * Checks to see if the first four bytes in "reply" are OKAY. 584 */ isOkay(byte[] reply)585 static boolean isOkay(byte[] reply) { 586 return reply[0] == (byte)'O' && reply[1] == (byte)'K' 587 && reply[2] == (byte)'A' && reply[3] == (byte)'Y'; 588 } 589 590 /** 591 * Converts an ADB reply to a string. 592 */ replyToString(byte[] reply)593 static String replyToString(byte[] reply) { 594 String result; 595 try { 596 result = new String(reply, DEFAULT_ENCODING); 597 } catch (UnsupportedEncodingException uee) { 598 uee.printStackTrace(); // not expected 599 result = ""; 600 } 601 return result; 602 } 603 604 /** 605 * Reads from the socket until the array is filled, or no more data is coming (because 606 * the socket closed or the timeout expired). 607 * <p/>This uses the default time out value. 608 * 609 * @param chan the opened socket to read from. It must be in non-blocking 610 * mode for timeouts to work 611 * @param data the buffer to store the read data into. 612 * @throws TimeoutException in case of timeout on the connection. 613 * @throws IOException in case of I/O error on the connection. 614 */ read(SocketChannel chan, byte[] data)615 static void read(SocketChannel chan, byte[] data) throws TimeoutException, IOException { 616 read(chan, data, -1, DdmPreferences.getTimeOut()); 617 } 618 619 /** 620 * Reads from the socket until the array is filled, the optional length 621 * is reached, or no more data is coming (because the socket closed or the 622 * timeout expired). After "timeout" milliseconds since the 623 * previous successful read, this will return whether or not new data has 624 * been found. 625 * 626 * @param chan the opened socket to read from. It must be in non-blocking 627 * mode for timeouts to work 628 * @param data the buffer to store the read data into. 629 * @param length the length to read or -1 to fill the data buffer completely 630 * @param timeout The timeout value. A timeout of zero means "wait forever". 631 */ read(SocketChannel chan, byte[] data, int length, int timeout)632 static void read(SocketChannel chan, byte[] data, int length, int timeout) 633 throws TimeoutException, IOException { 634 ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); 635 int numWaits = 0; 636 637 while (buf.position() != buf.limit()) { 638 int count; 639 640 count = chan.read(buf); 641 if (count < 0) { 642 Log.d("ddms", "read: channel EOF"); 643 throw new IOException("EOF"); 644 } else if (count == 0) { 645 // TODO: need more accurate timeout? 646 if (timeout != 0 && numWaits * WAIT_TIME > timeout) { 647 Log.d("ddms", "read: timeout"); 648 throw new TimeoutException(); 649 } 650 // non-blocking spin 651 try { 652 Thread.sleep(WAIT_TIME); 653 } catch (InterruptedException ie) { 654 } 655 numWaits++; 656 } else { 657 numWaits = 0; 658 } 659 } 660 } 661 662 /** 663 * Write until all data in "data" is written or the connection fails or times out. 664 * <p/>This uses the default time out value. 665 * @param chan the opened socket to write to. 666 * @param data the buffer to send. 667 * @throws TimeoutException in case of timeout on the connection. 668 * @throws IOException in case of I/O error on the connection. 669 */ write(SocketChannel chan, byte[] data)670 static void write(SocketChannel chan, byte[] data) throws TimeoutException, IOException { 671 write(chan, data, -1, DdmPreferences.getTimeOut()); 672 } 673 674 /** 675 * Write until all data in "data" is written, the optional length is reached, 676 * the timeout expires, or the connection fails. Returns "true" if all 677 * data was written. 678 * @param chan the opened socket to write to. 679 * @param data the buffer to send. 680 * @param length the length to write or -1 to send the whole buffer. 681 * @param timeout The timeout value. A timeout of zero means "wait forever". 682 * @throws TimeoutException in case of timeout on the connection. 683 * @throws IOException in case of I/O error on the connection. 684 */ write(SocketChannel chan, byte[] data, int length, int timeout)685 static void write(SocketChannel chan, byte[] data, int length, int timeout) 686 throws TimeoutException, IOException { 687 ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); 688 int numWaits = 0; 689 690 while (buf.position() != buf.limit()) { 691 int count; 692 693 count = chan.write(buf); 694 if (count < 0) { 695 Log.d("ddms", "write: channel EOF"); 696 throw new IOException("channel EOF"); 697 } else if (count == 0) { 698 // TODO: need more accurate timeout? 699 if (timeout != 0 && numWaits * WAIT_TIME > timeout) { 700 Log.d("ddms", "write: timeout"); 701 throw new TimeoutException(); 702 } 703 // non-blocking spin 704 try { 705 Thread.sleep(WAIT_TIME); 706 } catch (InterruptedException ie) { 707 } 708 numWaits++; 709 } else { 710 numWaits = 0; 711 } 712 } 713 } 714 715 /** 716 * tells adb to talk to a specific device 717 * 718 * @param adbChan the socket connection to adb 719 * @param device The device to talk to. 720 * @throws TimeoutException in case of timeout on the connection. 721 * @throws AdbCommandRejectedException if adb rejects the command 722 * @throws IOException in case of I/O error on the connection. 723 */ setDevice(SocketChannel adbChan, IDevice device)724 static void setDevice(SocketChannel adbChan, IDevice device) 725 throws TimeoutException, AdbCommandRejectedException, IOException { 726 // if the device is not -1, then we first tell adb we're looking to talk 727 // to a specific device 728 if (device != null) { 729 String msg = "host:transport:" + device.getSerialNumber(); //$NON-NLS-1$ 730 byte[] device_query = formAdbRequest(msg); 731 732 write(adbChan, device_query); 733 734 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 735 if (resp.okay == false) { 736 throw new AdbCommandRejectedException(resp.message, 737 true/*errorDuringDeviceSelection*/); 738 } 739 } 740 } 741 742 /** 743 * Reboot the device. 744 * 745 * @param into what to reboot into (recovery, bootloader). Or null to just reboot. 746 * @throws TimeoutException in case of timeout on the connection. 747 * @throws AdbCommandRejectedException if adb rejects the command 748 * @throws IOException in case of I/O error on the connection. 749 */ reboot(String into, InetSocketAddress adbSockAddr, Device device)750 public static void reboot(String into, InetSocketAddress adbSockAddr, 751 Device device) throws TimeoutException, AdbCommandRejectedException, IOException { 752 byte[] request; 753 if (into == null) { 754 request = formAdbRequest("reboot:"); //$NON-NLS-1$ 755 } else { 756 request = formAdbRequest("reboot:" + into); //$NON-NLS-1$ 757 } 758 759 SocketChannel adbChan = null; 760 try { 761 adbChan = SocketChannel.open(adbSockAddr); 762 adbChan.configureBlocking(false); 763 764 // if the device is not -1, then we first tell adb we're looking to talk 765 // to a specific device 766 setDevice(adbChan, device); 767 768 write(adbChan, request); 769 } finally { 770 if (adbChan != null) { 771 adbChan.close(); 772 } 773 } 774 } 775 } 776