diff --git a/src/se/vidstige/jadb/server/AdbProtocolHandler.java b/src/se/vidstige/jadb/server/AdbProtocolHandler.java index fffbed4..884112b 100644 --- a/src/se/vidstige/jadb/server/AdbProtocolHandler.java +++ b/src/se/vidstige/jadb/server/AdbProtocolHandler.java @@ -37,91 +37,111 @@ class AdbProtocolHandler implements Runnable { } private void runServer() throws IOException { - DataInput input = new DataInputStream(socket.getInputStream()); - DataOutputStream output = new DataOutputStream(socket.getOutputStream()); - - while (true) { - byte[] buffer = new byte[4]; - input.readFully(buffer); - String encodedLength = new String(buffer, StandardCharsets.UTF_8); - int length = Integer.parseInt(encodedLength, 16); - - buffer = new byte[length]; - input.readFully(buffer); - String command = new String(buffer, StandardCharsets.UTF_8); - - responder.onCommand(command); - - try { - if ("host:version".equals(command)) { - output.writeBytes("OKAY"); - send(output, String.format("%04x", responder.getVersion())); - } else if ("host:transport-any".equals(command)) { - // TODO: Check so that exactly one device is selected. - selected = responder.getDevices().get(0); - output.writeBytes("OKAY"); - } else if ("host:devices".equals(command)) { - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - DataOutputStream writer = new DataOutputStream(tmp); - for (AdbDeviceResponder d : responder.getDevices()) { - writer.writeBytes(d.getSerial() + "\t" + d.getType() + "\n"); - } - output.writeBytes("OKAY"); - send(output, new String(tmp.toByteArray(), StandardCharsets.UTF_8)); - } else if (command.startsWith("host:transport:")) { - String serial = command.substring("host:transport:".length()); - selected = findDevice(serial); - output.writeBytes("OKAY"); - } else if ("sync:".equals(command)) { - output.writeBytes("OKAY"); - try { - sync(output, input); - } catch (JadbException e) { // sync response with a different type of fail message - SyncTransport sync = new SyncTransport(output, input); - sync.send("FAIL", e.getMessage()); - } - } else if (command.startsWith("shell:")) { - String shellCommand = command.substring("shell:".length()); - output.writeBytes("OKAY"); - shell(shellCommand, output, input); - output.close(); - return; - } else if ("host:get-state".equals(command)) { - // TODO: Check so that exactly one device is selected. - AdbDeviceResponder device = responder.getDevices().get(0); - output.writeBytes("OKAY"); - send(output, device.getType()); - } else if (command.startsWith("host-serial:")) { - String[] strs = command.split(":",0); - if (strs.length != 3) { - throw new ProtocolException("Invalid command: " + command); - } - - String serial = strs[1]; - boolean found = false; - output.writeBytes("OKAY"); - for (AdbDeviceResponder d : responder.getDevices()) { - if (d.getSerial().equals(serial)) { - send(output, d.getType()); - found = true; - break; - } - } - - if (!found) { - send(output, "unknown"); - } - } else { - throw new ProtocolException("Unknown command: " + command); - } - } catch (ProtocolException e) { - output.writeBytes("FAIL"); - send(output, e.getMessage()); + try ( + DataInputStream input = new DataInputStream(socket.getInputStream()); + DataOutputStream output = new DataOutputStream(socket.getOutputStream()) + ) { + while (processCommand(input, output)) { + // nothing to do here } - output.flush(); } } + private boolean processCommand(DataInput input, DataOutputStream output) throws IOException { + String command = readCommand(input); + responder.onCommand(command); + + try { + if ("host:version".equals(command)) { + hostVersion(output); + } else if ("host:transport-any".equals(command)) { + hostTransportAny(output); + } else if ("host:devices".equals(command)) { + hostDevices(output); + } else if (command.startsWith("host:transport:")) { + hostTransport(output, command); + } else if ("sync:".equals(command)) { + sync(output, input); + } else if (command.startsWith("shell:")) { + shell(input, output, command); + return false; + } else if ("host:get-state".equals(command)) { + hostGetState(output); + } else if (command.startsWith("host-serial:")) { + hostSerial(output, command); + } else { + throw new ProtocolException("Unknown command: " + command); + } + } catch (ProtocolException e) { + output.writeBytes("FAIL"); + send(output, e.getMessage()); + } + output.flush(); + return true; + } + + private void hostSerial(DataOutputStream output, String command) throws IOException { + String[] strs = command.split(":",0); + if (strs.length != 3) { + throw new ProtocolException("Invalid command: " + command); + } + + String serial = strs[1]; + boolean found = false; + output.writeBytes("OKAY"); + for (AdbDeviceResponder d : responder.getDevices()) { + if (d.getSerial().equals(serial)) { + send(output, d.getType()); + found = true; + break; + } + } + + if (!found) { + send(output, "unknown"); + } + } + + private void hostGetState(DataOutputStream output) throws IOException { + // TODO: Check so that exactly one device is selected. + AdbDeviceResponder device = responder.getDevices().get(0); + output.writeBytes("OKAY"); + send(output, device.getType()); + } + + private void shell(DataInput input, DataOutputStream output, String command) throws IOException { + String shellCommand = command.substring("shell:".length()); + output.writeBytes("OKAY"); + shell(shellCommand, output, input); + } + + private void hostTransport(DataOutputStream output, String command) throws IOException { + String serial = command.substring("host:transport:".length()); + selected = findDevice(serial); + output.writeBytes("OKAY"); + } + + private void hostDevices(DataOutputStream output) throws IOException { + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + DataOutputStream writer = new DataOutputStream(tmp); + for (AdbDeviceResponder d : responder.getDevices()) { + writer.writeBytes(d.getSerial() + "\t" + d.getType() + "\n"); + } + output.writeBytes("OKAY"); + send(output, new String(tmp.toByteArray(), StandardCharsets.UTF_8)); + } + + private void hostTransportAny(DataOutputStream output) throws IOException { + // TODO: Check so that exactly one device is selected. + selected = responder.getDevices().get(0); + output.writeBytes("OKAY"); + } + + private void hostVersion(DataOutputStream output) throws IOException { + output.writeBytes("OKAY"); + send(output, String.format("%04x", responder.getVersion())); + } + private void shell(String command, DataOutputStream stdout, DataInput stdin) throws IOException { selected.shell(command, stdout, stdin); } @@ -130,37 +150,60 @@ class AdbProtocolHandler implements Runnable { return Integer.reverseBytes(input.readInt()); } + private int readHexInt(DataInput input) throws IOException { + return Integer.parseInt(readString(input, 4), 16); + } + private String readString(DataInput input, int length) throws IOException { byte[] responseBuffer = new byte[length]; input.readFully(responseBuffer); return new String(responseBuffer, StandardCharsets.UTF_8); } - private void sync(DataOutput output, DataInput input) throws IOException, JadbException { - String id = readString(input, 4); - int length = readInt(input); - if ("SEND".equals(id)) { - String remotePath = readString(input, length); - int idx = remotePath.lastIndexOf(','); - String path = remotePath; - int mode = 0666; - if (idx > 0) { - path = remotePath.substring(0, idx); - mode = Integer.parseInt(remotePath.substring(idx + 1)); - } - SyncTransport transport = new SyncTransport(output, input); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - transport.readChunksTo(buffer); - selected.filePushed(new RemoteFile(path), mode, buffer); - transport.sendStatus("OKAY", 0); // 0 = ignored - } else if ("RECV".equals(id)) { - String remotePath = readString(input, length); - SyncTransport transport = new SyncTransport(output, input); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - selected.filePulled(new RemoteFile(remotePath), buffer); - transport.sendStream(new ByteArrayInputStream(buffer.toByteArray())); - transport.sendStatus("DONE", 0); // ignored - } else throw new JadbException("Unknown sync id " + id); + private String readCommand(DataInput input) throws IOException { + int length = readHexInt(input); + return readString(input, length); + } + + private void sync(DataOutput output, DataInput input) throws IOException { + output.writeBytes("OKAY"); + try { + String id = readString(input, 4); + int length = readInt(input); + if ("SEND".equals(id)) { + syncSend(output, input, length); + } else if ("RECV".equals(id)) { + syncRecv(output, input, length); + } else throw new JadbException("Unknown sync id " + id); + } catch (JadbException e) { // sync response with a different type of fail message + SyncTransport sync = getSyncTransport(output, input); + sync.send("FAIL", e.getMessage()); + } + } + + private void syncRecv(DataOutput output, DataInput input, int length) throws IOException, JadbException { + String remotePath = readString(input, length); + SyncTransport transport = getSyncTransport(output, input); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + selected.filePulled(new RemoteFile(remotePath), buffer); + transport.sendStream(new ByteArrayInputStream(buffer.toByteArray())); + transport.sendStatus("DONE", 0); // ignored + } + + private void syncSend(DataOutput output, DataInput input, int length) throws IOException, JadbException { + String remotePath = readString(input, length); + int idx = remotePath.lastIndexOf(','); + String path = remotePath; + int mode = 0666; + if (idx > 0) { + path = remotePath.substring(0, idx); + mode = Integer.parseInt(remotePath.substring(idx + 1)); + } + SyncTransport transport = getSyncTransport(output, input); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + transport.readChunksTo(buffer); + selected.filePushed(new RemoteFile(path), mode, buffer); + transport.sendStatus("OKAY", 0); // 0 = ignored } private String getCommandLength(String command) { @@ -171,4 +214,8 @@ class AdbProtocolHandler implements Runnable { writer.writeBytes(getCommandLength(response)); writer.writeBytes(response); } + + private SyncTransport getSyncTransport(DataOutput output, DataInput input) { + return new SyncTransport(output, input); + } }