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 localPortSpec specification of the local port to forward, should be of format 515 * tcp:<port number> 516 * @param remotePortSpec specification of the remote port to forward to, one of: 517 * tcp:<port> 518 * localabstract:<unix domain socket name> 519 * localreserved:<unix domain socket name> 520 * localfilesystem:<unix domain socket name> 521 * dev:<character device name> 522 * jdwp:<process pid> (remote only) 523 * @throws TimeoutException in case of timeout on the connection. 524 * @throws AdbCommandRejectedException if adb rejects the command 525 * @throws IOException in case of I/O error on the connection. 526 */ createForward(InetSocketAddress adbSockAddr, Device device, String localPortSpec, String remotePortSpec)527 public static void createForward(InetSocketAddress adbSockAddr, Device device, 528 String localPortSpec, String remotePortSpec) 529 throws TimeoutException, AdbCommandRejectedException, IOException { 530 531 SocketChannel adbChan = null; 532 try { 533 adbChan = SocketChannel.open(adbSockAddr); 534 adbChan.configureBlocking(false); 535 536 byte[] request = formAdbRequest(String.format( 537 "host-serial:%1$s:forward:%2$s;%3$s", //$NON-NLS-1$ 538 device.getSerialNumber(), localPortSpec, remotePortSpec)); 539 540 write(adbChan, request); 541 542 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 543 if (resp.okay == false) { 544 Log.w("create-forward", "Error creating forward: " + resp.message); 545 throw new AdbCommandRejectedException(resp.message); 546 } 547 } finally { 548 if (adbChan != null) { 549 adbChan.close(); 550 } 551 } 552 } 553 554 /** 555 * Remove a port forwarding between a local and a remote port. 556 * @param adbSockAddr the socket address to connect to adb 557 * @param device the device on which to remove the port fowarding 558 * @param localPortSpec specification of the local port that was forwarded, should be of format 559 * tcp:<port number> 560 * @param remotePortSpec specification of the remote port forwarded to, one of: 561 * tcp:<port> 562 * localabstract:<unix domain socket name> 563 * localreserved:<unix domain socket name> 564 * localfilesystem:<unix domain socket name> 565 * dev:<character device name> 566 * jdwp:<process pid> (remote only) 567 * @throws TimeoutException in case of timeout on the connection. 568 * @throws AdbCommandRejectedException if adb rejects the command 569 * @throws IOException in case of I/O error on the connection. 570 */ removeForward(InetSocketAddress adbSockAddr, Device device, String localPortSpec, String remotePortSpec)571 public static void removeForward(InetSocketAddress adbSockAddr, Device device, 572 String localPortSpec, String remotePortSpec) 573 throws TimeoutException, AdbCommandRejectedException, IOException { 574 575 SocketChannel adbChan = null; 576 try { 577 adbChan = SocketChannel.open(adbSockAddr); 578 adbChan.configureBlocking(false); 579 580 byte[] request = formAdbRequest(String.format( 581 "host-serial:%1$s:killforward:%2$s;%3$s", //$NON-NLS-1$ 582 device.getSerialNumber(), localPortSpec, remotePortSpec)); 583 584 write(adbChan, request); 585 586 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 587 if (resp.okay == false) { 588 Log.w("remove-forward", "Error creating forward: " + resp.message); 589 throw new AdbCommandRejectedException(resp.message); 590 } 591 } finally { 592 if (adbChan != null) { 593 adbChan.close(); 594 } 595 } 596 } 597 598 /** 599 * Checks to see if the first four bytes in "reply" are OKAY. 600 */ isOkay(byte[] reply)601 static boolean isOkay(byte[] reply) { 602 return reply[0] == (byte)'O' && reply[1] == (byte)'K' 603 && reply[2] == (byte)'A' && reply[3] == (byte)'Y'; 604 } 605 606 /** 607 * Converts an ADB reply to a string. 608 */ replyToString(byte[] reply)609 static String replyToString(byte[] reply) { 610 String result; 611 try { 612 result = new String(reply, DEFAULT_ENCODING); 613 } catch (UnsupportedEncodingException uee) { 614 uee.printStackTrace(); // not expected 615 result = ""; 616 } 617 return result; 618 } 619 620 /** 621 * Reads from the socket until the array is filled, or no more data is coming (because 622 * the socket closed or the timeout expired). 623 * <p/>This uses the default time out value. 624 * 625 * @param chan the opened socket to read from. It must be in non-blocking 626 * mode for timeouts to work 627 * @param data the buffer to store the read data into. 628 * @throws TimeoutException in case of timeout on the connection. 629 * @throws IOException in case of I/O error on the connection. 630 */ read(SocketChannel chan, byte[] data)631 static void read(SocketChannel chan, byte[] data) throws TimeoutException, IOException { 632 read(chan, data, -1, DdmPreferences.getTimeOut()); 633 } 634 635 /** 636 * Reads from the socket until the array is filled, the optional length 637 * is reached, or no more data is coming (because the socket closed or the 638 * timeout expired). After "timeout" milliseconds since the 639 * previous successful read, this will return whether or not new data has 640 * been found. 641 * 642 * @param chan the opened socket to read from. It must be in non-blocking 643 * mode for timeouts to work 644 * @param data the buffer to store the read data into. 645 * @param length the length to read or -1 to fill the data buffer completely 646 * @param timeout The timeout value. A timeout of zero means "wait forever". 647 */ read(SocketChannel chan, byte[] data, int length, int timeout)648 static void read(SocketChannel chan, byte[] data, int length, int timeout) 649 throws TimeoutException, IOException { 650 ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); 651 int numWaits = 0; 652 653 while (buf.position() != buf.limit()) { 654 int count; 655 656 count = chan.read(buf); 657 if (count < 0) { 658 Log.d("ddms", "read: channel EOF"); 659 throw new IOException("EOF"); 660 } else if (count == 0) { 661 // TODO: need more accurate timeout? 662 if (timeout != 0 && numWaits * WAIT_TIME > timeout) { 663 Log.d("ddms", "read: timeout"); 664 throw new TimeoutException(); 665 } 666 // non-blocking spin 667 try { 668 Thread.sleep(WAIT_TIME); 669 } catch (InterruptedException ie) { 670 } 671 numWaits++; 672 } else { 673 numWaits = 0; 674 } 675 } 676 } 677 678 /** 679 * Write until all data in "data" is written or the connection fails or times out. 680 * <p/>This uses the default time out value. 681 * @param chan the opened socket to write to. 682 * @param data the buffer to send. 683 * @throws TimeoutException in case of timeout on the connection. 684 * @throws IOException in case of I/O error on the connection. 685 */ write(SocketChannel chan, byte[] data)686 static void write(SocketChannel chan, byte[] data) throws TimeoutException, IOException { 687 write(chan, data, -1, DdmPreferences.getTimeOut()); 688 } 689 690 /** 691 * Write until all data in "data" is written, the optional length is reached, 692 * the timeout expires, or the connection fails. Returns "true" if all 693 * data was written. 694 * @param chan the opened socket to write to. 695 * @param data the buffer to send. 696 * @param length the length to write or -1 to send the whole buffer. 697 * @param timeout The timeout value. A timeout of zero means "wait forever". 698 * @throws TimeoutException in case of timeout on the connection. 699 * @throws IOException in case of I/O error on the connection. 700 */ write(SocketChannel chan, byte[] data, int length, int timeout)701 static void write(SocketChannel chan, byte[] data, int length, int timeout) 702 throws TimeoutException, IOException { 703 ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length); 704 int numWaits = 0; 705 706 while (buf.position() != buf.limit()) { 707 int count; 708 709 count = chan.write(buf); 710 if (count < 0) { 711 Log.d("ddms", "write: channel EOF"); 712 throw new IOException("channel EOF"); 713 } else if (count == 0) { 714 // TODO: need more accurate timeout? 715 if (timeout != 0 && numWaits * WAIT_TIME > timeout) { 716 Log.d("ddms", "write: timeout"); 717 throw new TimeoutException(); 718 } 719 // non-blocking spin 720 try { 721 Thread.sleep(WAIT_TIME); 722 } catch (InterruptedException ie) { 723 } 724 numWaits++; 725 } else { 726 numWaits = 0; 727 } 728 } 729 } 730 731 /** 732 * tells adb to talk to a specific device 733 * 734 * @param adbChan the socket connection to adb 735 * @param device The device to talk to. 736 * @throws TimeoutException in case of timeout on the connection. 737 * @throws AdbCommandRejectedException if adb rejects the command 738 * @throws IOException in case of I/O error on the connection. 739 */ setDevice(SocketChannel adbChan, IDevice device)740 static void setDevice(SocketChannel adbChan, IDevice device) 741 throws TimeoutException, AdbCommandRejectedException, IOException { 742 // if the device is not -1, then we first tell adb we're looking to talk 743 // to a specific device 744 if (device != null) { 745 String msg = "host:transport:" + device.getSerialNumber(); //$NON-NLS-1$ 746 byte[] device_query = formAdbRequest(msg); 747 748 write(adbChan, device_query); 749 750 AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */); 751 if (resp.okay == false) { 752 throw new AdbCommandRejectedException(resp.message, 753 true/*errorDuringDeviceSelection*/); 754 } 755 } 756 } 757 758 /** 759 * Reboot the device. 760 * 761 * @param into what to reboot into (recovery, bootloader). Or null to just reboot. 762 * @throws TimeoutException in case of timeout on the connection. 763 * @throws AdbCommandRejectedException if adb rejects the command 764 * @throws IOException in case of I/O error on the connection. 765 */ reboot(String into, InetSocketAddress adbSockAddr, Device device)766 public static void reboot(String into, InetSocketAddress adbSockAddr, 767 Device device) throws TimeoutException, AdbCommandRejectedException, IOException { 768 byte[] request; 769 if (into == null) { 770 request = formAdbRequest("reboot:"); //$NON-NLS-1$ 771 } else { 772 request = formAdbRequest("reboot:" + into); //$NON-NLS-1$ 773 } 774 775 SocketChannel adbChan = null; 776 try { 777 adbChan = SocketChannel.open(adbSockAddr); 778 adbChan.configureBlocking(false); 779 780 // if the device is not -1, then we first tell adb we're looking to talk 781 // to a specific device 782 setDevice(adbChan, device); 783 784 write(adbChan, request); 785 } finally { 786 if (adbChan != null) { 787 adbChan.close(); 788 } 789 } 790 } 791 } 792