AdbHelperpublic final class AdbHelper extends Object Helper class to handle requests and connections to adb.
{@link DebugBridgeServer} is the public API to connection to adb, while {@link AdbHelper}
does the low level stuff.
This currently uses spin-wait non-blocking I/O. A Selector would be more efficient,
but seems like overkill for what we're doing here. |
Fields Summary |
---|
static final int | WAIT_TIME | public static final int | STD_TIMEOUT | static final String | DEFAULT_ENCODING |
Constructors Summary |
---|
private AdbHelper()do not instantiate //$NON-NLS-1$
|
Methods Summary |
---|
private static byte[] | createAdbForwardRequest(java.lang.String addrStr, int port)Creates a port forwarding request for adb. This returns an array
containing "####tcp:{port}:{addStr}".
String reqStr;
if (addrStr == null)
reqStr = "tcp:" + port;
else
reqStr = "tcp:" + port + ":" + addrStr;
return formAdbRequest(reqStr);
| public static boolean | createForward(java.net.InetSocketAddress adbSockAddr, Device device, int localPort, int remotePort)Creates a port forwarding between a local and a remote port.
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:forward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the forward command.");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return true;
| private static byte[] | createJdwpForwardRequest(int pid)Creates a port forwarding request to a jdwp process. This returns an array
containing "####jwdp:{pid}".
String reqStr = String.format("jdwp:%1$d", pid); //$NON-NLS-1$
return formAdbRequest(reqStr);
| public static java.nio.channels.SocketChannel | createPassThroughConnection(java.net.InetSocketAddress adbSockAddr, Device device, int pid)Creates and connects a new pass-through socket, from the host to a port on
the device.
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
try {
adbChan.socket().setTcpNoDelay(true);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to
// talk to a specific device
setDevice(adbChan, device);
byte[] req = createJdwpForwardRequest(pid);
// Log.hexDump(req);
if (write(adbChan, req) == false)
throw new IOException("failed submitting request to ADB"); //$NON-NLS-1$
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.okay)
throw new IOException("connection request rejected: " + resp.message); //$NON-NLS-1$
adbChan.configureBlocking(true);
} catch (IOException ioe) {
adbChan.close();
throw ioe;
}
return adbChan;
| public static void | executeRemoteCommand(java.net.InetSocketAddress adbSockAddr, java.lang.String command, Device device, IShellOutputReceiver rcvr)Execute a command on the device and retrieve the output. The output is
handed to "rcvr" as it arrives.
Log.v("ddms", "execute: running " + command);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to
// talk
// to a specific device
setDevice(adbChan, device);
byte[] request = formAdbRequest("shell:" + command); //$NON-NLS-1$
if (write(adbChan, request) == false)
throw new IOException("failed submitting shell command");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);
throw new IOException("sad result from adb: " + resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
Log.v("ddms", "execute: cancelled");
break;
}
count = adbChan.read(buf);
if (count < 0) {
// we're at the end, we flush the output
rcvr.flush();
Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: "
+ count);
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
}
} else {
if (rcvr != null) {
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());
}
buf.rewind();
}
}
} finally {
if (adbChan != null) {
adbChan.close();
}
Log.v("ddms", "execute: returning");
}
| static byte[] | formAdbRequest(java.lang.String req)Create an ASCII string preceeded by four hex digits. The opening "####"
is the length of the rest of the string, encoded as ASCII hex (case
doesn't matter). "port" and "host" are what we want to forward to. If
we're on the host side connecting into the device, "addrStr" should be
null.
String resultStr = String.format("%04X%s", req.length(), req); //$NON-NLS-1$
byte[] result;
try {
result = resultStr.getBytes(DEFAULT_ENCODING);
} catch (UnsupportedEncodingException uee) {
uee.printStackTrace(); // not expected
return null;
}
assert result.length == req.length() + 4;
return result;
| public static RawImage | getFrameBuffer(java.net.InetSocketAddress adbSockAddr, Device device)Retrieve the frame buffer from the device.
RawImage imageParams = new RawImage();
byte[] request = formAdbRequest("framebuffer:"); //$NON-NLS-1$
byte[] nudge = {
0
};
byte[] reply;
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
if (write(adbChan, request) == false)
throw new IOException("failed asking for frame buffer");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.w("ddms", "Got timeout or unhappy response from ADB fb req: "
+ resp.message);
adbChan.close();
return null;
}
reply = new byte[16];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got partial reply from ADB fb:");
Log.hexDump("ddms", LogLevel.WARN, reply, 0, reply.length);
adbChan.close();
return null;
}
ByteBuffer buf = ByteBuffer.wrap(reply);
buf.order(ByteOrder.LITTLE_ENDIAN);
imageParams.bpp = buf.getInt();
imageParams.size = buf.getInt();
imageParams.width = buf.getInt();
imageParams.height = buf.getInt();
Log.d("ddms", "image params: bpp=" + imageParams.bpp + ", size="
+ imageParams.size + ", width=" + imageParams.width
+ ", height=" + imageParams.height);
if (write(adbChan, nudge) == false)
throw new IOException("failed nudging");
reply = new byte[imageParams.size];
if (read(adbChan, reply) == false) {
Log.w("ddms", "got truncated reply from ADB fb data");
adbChan.close();
return null;
}
imageParams.data = reply;
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return imageParams;
| static boolean | isOkay(byte[] reply)Checks to see if the first four bytes in "reply" are OKAY.
return reply[0] == (byte)'O" && reply[1] == (byte)'K"
&& reply[2] == (byte)'A" && reply[3] == (byte)'Y";
| public static java.nio.channels.SocketChannel | open(java.net.InetSocketAddress adbSockAddr, Device device, int devicePort)Create and connect a new pass-through socket, from the host to a port on
the device.
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
try {
adbChan.socket().setTcpNoDelay(true);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to
// talk to a specific device
setDevice(adbChan, device);
byte[] req = createAdbForwardRequest(null, devicePort);
// Log.hexDump(req);
if (write(adbChan, req) == false)
throw new IOException("failed submitting request to ADB"); //$NON-NLS-1$
AdbResponse resp = readAdbResponse(adbChan, false);
if (!resp.okay)
throw new IOException("connection request rejected"); //$NON-NLS-1$
adbChan.configureBlocking(true);
} catch (IOException ioe) {
adbChan.close();
throw ioe;
}
return adbChan;
| static boolean | read(java.nio.channels.SocketChannel chan, byte[] data)Reads from the socket until the array is filled, or no more data is coming (because
the socket closed or the timeout expired).
try {
read(chan, data, -1, STD_TIMEOUT);
} catch (IOException e) {
Log.d("ddms", "readAll: IOException: " + e.getMessage());
return false;
}
return true;
| static void | read(java.nio.channels.SocketChannel chan, byte[] data, int length, int timeout)Reads from the socket until the array is filled, the optional length
is reached, or no more data is coming (because the socket closed or the
timeout expired). After "timeout" milliseconds since the
previous successful read, this will return whether or not new data has
been found.
ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length);
int numWaits = 0;
while (buf.position() != buf.limit()) {
int count;
count = chan.read(buf);
if (count < 0) {
Log.d("ddms", "read: channel EOF");
throw new IOException("EOF");
} else if (count == 0) {
// TODO: need more accurate timeout?
if (timeout != 0 && numWaits * WAIT_TIME > timeout) {
Log.i("ddms", "read: timeout");
throw new IOException("timeout");
}
// non-blocking spin
try {
Thread.sleep(WAIT_TIME);
} catch (InterruptedException ie) {
}
numWaits++;
} else {
numWaits = 0;
}
}
| static com.android.ddmlib.AdbHelper$AdbResponse | readAdbResponse(java.nio.channels.SocketChannel chan, boolean readDiagString)Reads the response from ADB after a command.
AdbResponse resp = new AdbResponse();
byte[] reply = new byte[4];
if (read(chan, reply) == false) {
return resp;
}
resp.ioSuccess = true;
if (isOkay(reply)) {
resp.okay = true;
} else {
readDiagString = true; // look for a reason after the FAIL
resp.okay = false;
}
// not a loop -- use "while" so we can use "break"
while (readDiagString) {
// length string is in next 4 bytes
byte[] lenBuf = new byte[4];
if (read(chan, lenBuf) == false) {
Log.w("ddms", "Expected diagnostic string not found");
break;
}
String lenStr = replyToString(lenBuf);
int len;
try {
len = Integer.parseInt(lenStr, 16);
} catch (NumberFormatException nfe) {
Log.w("ddms", "Expected digits, got '" + lenStr + "': "
+ lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " "
+ lenBuf[3]);
Log.w("ddms", "reply was " + replyToString(reply));
break;
}
byte[] msg = new byte[len];
if (read(chan, msg) == false) {
Log.w("ddms", "Failed reading diagnostic string, len=" + len);
break;
}
resp.message = replyToString(msg);
Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='"
+ resp.message + "'");
break;
}
return resp;
| public static boolean | removeForward(java.net.InetSocketAddress adbSockAddr, Device device, int localPort, int remotePort)Remove a port forwarding between a local and a remote port.
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
"host-serial:%1$s:killforward:tcp:%2$d;tcp:%3$d", //$NON-NLS-1$
device.serialNumber, localPort, remotePort));
if (!write(adbChan, request)) {
throw new IOException("failed to submit the remove forward command.");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return true;
| static java.lang.String | replyToString(byte[] reply)Converts an ADB reply to a string.
String result;
try {
result = new String(reply, DEFAULT_ENCODING);
} catch (UnsupportedEncodingException uee) {
uee.printStackTrace(); // not expected
result = "";
}
return result;
| public static void | runEventLogService(java.net.InetSocketAddress adbSockAddr, Device device, com.android.ddmlib.log.LogReceiver rcvr)Runs the Event log service on the {@link Device}, and provides its output to the
{@link LogReceiver}.
runLogService(adbSockAddr, device, "events", rcvr); //$NON-NLS-1$
| public static void | runLogService(java.net.InetSocketAddress adbSockAddr, Device device, java.lang.String logName, com.android.ddmlib.log.LogReceiver rcvr)Runs a log service on the {@link Device}, and provides its output to the {@link LogReceiver}.
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
setDevice(adbChan, device);
byte[] request = formAdbRequest("log:" + logName);
if (write(adbChan, request) == false) {
throw new IOException("failed to submit the log command");
}
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected log command: " + resp.message);
}
byte[] data = new byte[16384];
ByteBuffer buf = ByteBuffer.wrap(data);
while (true) {
int count;
if (rcvr != null && rcvr.isCancelled()) {
break;
}
count = adbChan.read(buf);
if (count < 0) {
break;
} else if (count == 0) {
try {
Thread.sleep(WAIT_TIME * 5);
} catch (InterruptedException ie) {
}
} else {
if (rcvr != null) {
rcvr.parseNewData(buf.array(), buf.arrayOffset(), buf.position());
}
buf.rewind();
}
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
| static void | setDevice(java.nio.channels.SocketChannel adbChan, Device device)tells adb to talk to a specific device
// if the device is not -1, then we first tell adb we're looking to talk
// to a specific device
if (device != null) {
String msg = "host:transport:" + device.serialNumber; //$NON-NLS-1$
byte[] device_query = formAdbRequest(msg);
if (write(adbChan, device_query) == false)
throw new IOException("failed submitting device (" + device +
") request to ADB");
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.okay)
throw new IOException("device (" + device +
") request rejected: " + resp.message);
}
| static boolean | write(java.nio.channels.SocketChannel chan, byte[] data)Write until all data in "data" is written or the connection fails.
try {
write(chan, data, -1, STD_TIMEOUT);
} catch (IOException e) {
Log.e("ddms", e);
return false;
}
return true;
| static void | write(java.nio.channels.SocketChannel chan, byte[] data, int length, int timeout)Write until all data in "data" is written, the optional length is reached,
the timeout expires, or the connection fails. Returns "true" if all
data was written.
ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.length);
int numWaits = 0;
while (buf.position() != buf.limit()) {
int count;
count = chan.write(buf);
if (count < 0) {
Log.d("ddms", "write: channel EOF");
throw new IOException("channel EOF");
} else if (count == 0) {
// TODO: need more accurate timeout?
if (timeout != 0 && numWaits * WAIT_TIME > timeout) {
Log.i("ddms", "write: timeout");
throw new IOException("timeout");
}
// non-blocking spin
try {
Thread.sleep(WAIT_TIME);
} catch (InterruptedException ie) {
}
numWaits++;
} else {
numWaits = 0;
}
}
|
|