diff --git a/src/se/vidstige/jadb/FrameBuffer.java b/src/se/vidstige/jadb/FrameBuffer.java
new file mode 100644
index 0000000..47275aa
--- /dev/null
+++ b/src/se/vidstige/jadb/FrameBuffer.java
@@ -0,0 +1,109 @@
+package se.vidstige.jadb;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class FrameBuffer {
+ public int version;
+ public int bpp;
+ public int colorSpace;
+ public int size;
+ public int width;
+ public int height;
+ public int red_offset;
+ public int red_length;
+ public int blue_offset;
+ public int blue_length;
+ public int green_offset;
+ public int green_length;
+ public int alpha_offset;
+ public int alpha_length;
+ public byte[] data;
+
+ /**
+ * get bitmap format data as byte array
+ *
+ * this data can be saved as a BMP format file
+ *
+ * if you want a JPEG format file ,you can convert it like this:
+ *
+ * ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageData);
+ *
+ * BufferedImage image = ImageIO.read(byteArrayInputStream);
+ *
+ * ImageIO.write(image, "jpeg", outputStream);
+ */
+ public byte[] getBitmapData() {
+ if (data == null) {
+ return null;
+ }
+ byte[] fileHeader = new byte[14];
+ byte[] infoHeader = new byte[40];
+ int imagePixelSize = width * height;
+ int pixelByteCount = size / imagePixelSize;
+ int bitmapFileSize = fileHeader.length + infoHeader.length + size;
+ ByteBuffer buffer = ByteBuffer.wrap(fileHeader);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put("BM".getBytes());//bfType
+ buffer.putInt(bitmapFileSize);//bfSize
+ buffer.putInt(0);//bfReserved
+ buffer.putInt(fileHeader.length + infoHeader.length);//bfOffBits
+ buffer = ByteBuffer.wrap(infoHeader);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putInt(infoHeader.length);//biSize
+ buffer.putInt(width);//biWidth
+ buffer.putInt(height);//biHeight
+ buffer.putShort((short) 1);//biPlanes
+ buffer.putShort((short) 32);//biBitCount
+ buffer.putInt(0);//biCompression
+ buffer.putInt(0);//biSizeImage
+ buffer.putInt(0);//biXPelsPerMeter
+ buffer.putInt(0);//biYPelsPerMeter
+ buffer.putInt(0);//biClrUsed
+ buffer.putInt(0);//biClrImportant
+ //reverse row data in vertical direction and for each pixel convert RGBA to BGRA
+ for (int i = 0; i < height / 2; i++) {
+ for (int j = 0; j < width; j++) {
+ int id1 = (i * width + j) * pixelByteCount;
+ int id2 = ((height - i - 1) * width + j) * pixelByteCount;
+ byte r = data[id1];
+ byte g = data[id1 + 1];
+ byte b = data[id1 + 2];
+ data[id1] = data[id2 + 2];
+ data[id1 + 1] = data[id2 + 1];
+ data[id1 + 2] = data[id2];
+ data[id2] = b;
+ data[id2 + 1] = g;
+ data[id2 + 2] = r;
+ }
+ }
+
+ byte[] imageData = new byte[bitmapFileSize];
+ System.arraycopy(fileHeader, 0, imageData, 0, fileHeader.length);
+ System.arraycopy(infoHeader, 0, imageData, fileHeader.length, infoHeader.length);
+ System.arraycopy(data, 0, imageData, fileHeader.length + infoHeader.length, data.length);
+ return imageData;
+ }
+
+ @Override
+ public String toString() {
+ return "FrameBuffer{" +
+ "version=" + version +
+ ", bpp=" + bpp +
+ ", colorSpace=" + colorSpace +
+ ", size=" + size +
+ ", width=" + width +
+ ", height=" + height +
+ ", red_offset=" + red_offset +
+ ", red_length=" + red_length +
+ ", blue_offset=" + blue_offset +
+ ", blue_length=" + blue_length +
+ ", green_offset=" + green_offset +
+ ", green_length=" + green_length +
+ ", alpha_offset=" + alpha_offset +
+ ", alpha_length=" + alpha_length +
+ '}';
+ }
+
+
+}
diff --git a/src/se/vidstige/jadb/JadbConnection.java b/src/se/vidstige/jadb/JadbConnection.java
index c8509e1..1cb9625 100644
--- a/src/se/vidstige/jadb/JadbConnection.java
+++ b/src/se/vidstige/jadb/JadbConnection.java
@@ -58,6 +58,15 @@ public List getDevices() throws IOException, JadbException {
}
}
+ public List getDevicesList() throws IOException, JadbException {
+ try (Transport transport = createTransport()) {
+ transport.send("host:devices-l");
+ transport.verifyResponse();
+ String body = transport.readString();
+ return parseDevicesList(body);
+ }
+ }
+
public DeviceWatcher createDeviceWatcher(DeviceDetectionListener listener) throws IOException, JadbException {
Transport transport = createTransport();
transport.send("host:track-devices");
@@ -77,6 +86,37 @@ public List parseDevices(String body) {
return devices;
}
+ public List parseDevicesList(String body) {
+ String[] lines = body.split("\n");
+ ArrayList devices = new ArrayList<>(lines.length);
+ for (String line : lines) {
+ String[] parts = line.split(" ");
+ if (parts.length > 1) {
+ String serial = parts[0];
+ String usb = null;
+ String product = null;
+ String model = null;
+ String device = null;
+ String transport_id = null;
+ for (String info : parts) {
+ if (info.startsWith("usb:")) {
+ usb = info.substring("usb:".length());
+ } else if (info.startsWith("product:")) {
+ product = info.substring("product:".length());
+ } else if (info.startsWith("model:")) {
+ model = info.substring("model:".length());
+ } else if (info.startsWith("device:")) {
+ device = info.substring("device:".length());
+ } else if (info.startsWith("transport_id:")) {
+ transport_id = info.substring("transport_id:".length());
+ }
+ }
+ devices.add(new JadbDevice(serial, usb, product, model, device, transport_id, this)); // parts[1] is type
+ }
+ }
+ return devices;
+ }
+
public JadbDevice getAnyDevice() {
return JadbDevice.createAny(this);
}
diff --git a/src/se/vidstige/jadb/JadbDevice.java b/src/se/vidstige/jadb/JadbDevice.java
index f72d4b5..b5966d7 100644
--- a/src/se/vidstige/jadb/JadbDevice.java
+++ b/src/se/vidstige/jadb/JadbDevice.java
@@ -24,11 +24,30 @@ public enum State {
//noinspection OctalInteger
private static final int DEFAULT_MODE = 0664;
private final String serial;
+ private final String usb;
+ private final String product;
+ private final String model;
+ private final String device;
+ private final String transport_id;
private final ITransportFactory transportFactory;
private static final int DEFAULT_TCPIP_PORT = 5555;
+
+ private JadbDevice(ITransportFactory tFactory) {
+ this(null, null, null, null, null, null, tFactory);
+ }
+
JadbDevice(String serial, ITransportFactory tFactory) {
+ this(serial, null, null, null, null, null, tFactory);
+ }
+
+ JadbDevice(String serial, String usb, String product, String model, String device, String transport_id, ITransportFactory tFactory) {
this.serial = serial;
+ this.usb = usb;
+ this.product = product;
+ this.model = model;
+ this.device = device;
+ this.transport_id = transport_id;
this.transportFactory = tFactory;
}
@@ -36,23 +55,28 @@ static JadbDevice createAny(JadbConnection connection) {
return new JadbDevice(connection);
}
- private JadbDevice(ITransportFactory tFactory) {
- serial = null;
- this.transportFactory = tFactory;
- }
-
private State convertState(String type) {
switch (type) {
- case "device": return State.Device;
- case "offline": return State.Offline;
- case "bootloader": return State.BootLoader;
- case "recovery": return State.Recovery;
- case "unauthorized": return State.Unauthorized;
- case "authorizing" : return State.Authorizing;
- case "connecting": return State.Connecting;
- case "sideload": return State.Sideload;
- case "rescue" : return State.Rescue;
- default: return State.Unknown;
+ case "device":
+ return State.Device;
+ case "offline":
+ return State.Offline;
+ case "bootloader":
+ return State.BootLoader;
+ case "recovery":
+ return State.Recovery;
+ case "unauthorized":
+ return State.Unauthorized;
+ case "authorizing":
+ return State.Authorizing;
+ case "connecting":
+ return State.Connecting;
+ case "sideload":
+ return State.Sideload;
+ case "rescue":
+ return State.Rescue;
+ default:
+ return State.Unknown;
}
}
@@ -61,8 +85,8 @@ private Transport getTransport() throws IOException, JadbException {
// Do not use try-with-resources here. We want to return unclosed Transport and it is up to caller
// to close it. Here we close it only in case of exception.
try {
- send(transport, serial == null ? "host:transport-any" : "host:transport:" + serial );
- } catch (IOException|JadbException e) {
+ send(transport, serial == null ? "host:transport-any" : "host:transport:" + serial);
+ } catch (IOException | JadbException e) {
transport.close();
throw e;
}
@@ -73,6 +97,26 @@ public String getSerial() {
return serial;
}
+ public String getUsb() {
+ return usb;
+ }
+
+ public String getProduct() {
+ return product;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public String getDevice() {
+ return device;
+ }
+
+ public String getTransport_id() {
+ return transport_id;
+ }
+
public State getState() throws IOException, JadbException {
try (Transport transport = transportFactory.createTransport()) {
send(transport, serial == null ? "host:get-state" : "host-serial:" + serial + ":get-state");
@@ -80,12 +124,13 @@ public State getState() throws IOException, JadbException {
}
}
- /** Execute a shell command.
+ /**
+ * Execute a shell command.
*
* For Lollipop and later see: {@link #execute(String, String...)}
*
* @param command main command to run. E.g. "ls"
- * @param args arguments to the command.
+ * @param args arguments to the command.
* @return combined stdout/stderr stream.
* @throws IOException
* @throws JadbException
@@ -98,7 +143,6 @@ public InputStream executeShell(String command, String... args) throws IOExcepti
}
/**
- *
* @deprecated Use InputStream executeShell(String command, String... args) method instead. Together with
* Stream.copy(in, out), it is possible to achieve the same effect.
*/
@@ -115,14 +159,15 @@ public void executeShell(OutputStream output, String command, String... args) th
}
}
- /** Execute a command with raw binary output.
+ /**
+ * Execute a command with raw binary output.
*
* Support for this command was added in Lollipop (Android 5.0), and is the recommended way to transmit binary
* data with that version or later. For earlier versions of Android, use
* {@link #executeShell(String, String...)}.
*
* @param command main command to run, e.g. "screencap"
- * @param args arguments to the command, e.g. "-p".
+ * @param args arguments to the command, e.g. "-p".
* @return combined stdout/stderr stream.
* @throws IOException
* @throws JadbException
@@ -138,7 +183,7 @@ public InputStream execute(String command, String... args) throws IOException, J
* Builds a command line string from the command and its arguments.
*
* @param command the command.
- * @param args the list of arguments.
+ * @param args the list of arguments.
* @return the command line.
*/
private StringBuilder buildCmdLine(String command, String... args) {
@@ -163,7 +208,6 @@ public void enableAdbOverTCP() throws IOException, JadbException {
* Enable tcpip on a specific port
*
* @param port for the device to bind on
- *
* @return success or failure
*/
public void enableAdbOverTCP(int port) throws IOException, JadbException {
@@ -218,6 +262,32 @@ public void pull(RemoteFile remote, File local) throws IOException, JadbExceptio
}
}
+ public FrameBuffer screenshot() throws IOException, JadbException {
+ Transport transport = getTransport();
+ send(transport, "framebuffer:");
+ FrameBuffer frameBuffer = new FrameBuffer();
+ frameBuffer.version = transport.readInt();
+ frameBuffer.bpp = transport.readInt();
+ frameBuffer.colorSpace = transport.readInt();
+ frameBuffer.size = transport.readInt();
+ frameBuffer.width = transport.readInt();
+ frameBuffer.height = transport.readInt();
+ frameBuffer.red_offset = transport.readInt();
+ frameBuffer.red_length = transport.readInt();
+ frameBuffer.blue_offset = transport.readInt();
+ frameBuffer.blue_length = transport.readInt();
+ frameBuffer.green_offset = transport.readInt();
+ frameBuffer.green_length = transport.readInt();
+ frameBuffer.alpha_offset = transport.readInt();
+ frameBuffer.alpha_length = transport.readInt();
+ byte[] imageData = new byte[frameBuffer.size];
+ DataInputStream inputStream = transport.getDataInputStream();
+ inputStream.readFully(imageData);
+ inputStream.close();
+ frameBuffer.data = imageData;
+ return frameBuffer;
+ }
+
private void send(Transport transport, String command) throws IOException, JadbException {
transport.send(command);
transport.verifyResponse();
diff --git a/src/se/vidstige/jadb/RemoteFile.java b/src/se/vidstige/jadb/RemoteFile.java
index 48ef23c..0d1914a 100644
--- a/src/se/vidstige/jadb/RemoteFile.java
+++ b/src/se/vidstige/jadb/RemoteFile.java
@@ -1,19 +1,37 @@
package se.vidstige.jadb;
+import java.io.File;
+
/**
* Created by vidstige on 2014-03-20
*/
public class RemoteFile {
private final String path;
- public RemoteFile(String path) { this.path = path; }
+ public RemoteFile(String path) {
+ this.path = path;
+ }
+
+ public String getName() {
+ int var1 = path.lastIndexOf(File.separatorChar);
+ return var1 < 0 ? path : path.substring(var1 + 1);
+ }
+
+ public int getSize() {
+ throw new UnsupportedOperationException();
+ }
- public String getName() { throw new UnsupportedOperationException(); }
- public int getSize() { throw new UnsupportedOperationException(); }
- public int getLastModified() { throw new UnsupportedOperationException(); }
- public boolean isDirectory() { throw new UnsupportedOperationException(); }
+ public int getLastModified() {
+ throw new UnsupportedOperationException();
+ }
- public String getPath() { return path;}
+ public boolean isDirectory() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPath() {
+ return path;
+ }
@Override
public boolean equals(Object o) {
diff --git a/src/se/vidstige/jadb/Transport.java b/src/se/vidstige/jadb/Transport.java
index 72a828c..006b76d 100644
--- a/src/se/vidstige/jadb/Transport.java
+++ b/src/se/vidstige/jadb/Transport.java
@@ -2,6 +2,8 @@
import java.io.*;
import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
class Transport implements Closeable {
@@ -36,6 +38,10 @@ public InputStream getInputStream() {
return inputStream;
}
+ public DataInputStream getDataInputStream() {
+ return dataInput;
+ }
+
public void verifyResponse() throws IOException, JadbException {
String response = readString(4);
if (!"OKAY".equals(response)) {
@@ -50,6 +56,14 @@ public String readString(int length) throws IOException {
return new String(responseBuffer, StandardCharsets.UTF_8);
}
+ public int readInt() throws IOException {
+ byte[] responseBuffer = new byte[4];
+ dataInput.readFully(responseBuffer);
+ ByteBuffer buffer = ByteBuffer.wrap(responseBuffer);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ return buffer.getInt();
+ }
+
private String getCommandLength(String command) {
return String.format("%04x", command.getBytes().length);
}
@@ -61,6 +75,11 @@ public void send(String command) throws IOException {
writer.flush();
}
+ public void sendBytes(byte[] bytes) throws IOException {
+ outputStream.write(bytes);
+ outputStream.flush();
+ }
+
public SyncTransport startSync() throws IOException, JadbException {
send("sync:");
verifyResponse();