OSDN Git Service

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