• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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