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