mirror of https://github.com/revanced/jadb.git
154 lines
4.6 KiB
Java
154 lines
4.6 KiB
Java
package se.vidstige.jadb;
|
|
|
|
import java.io.*;
|
|
|
|
class ShellProtocolTransport implements Closeable {
|
|
private final DataOutputStream output;
|
|
private final DataInputStream input;
|
|
|
|
ShellProtocolTransport(DataOutputStream outputStream, DataInputStream inputStream) {
|
|
output = outputStream;
|
|
input = inputStream;
|
|
}
|
|
|
|
// replace with Integer.toUnsignedLong in Java 8
|
|
private static long integerToUnsignedLong(int i) {
|
|
return ((long) (int) i) & 0xffffffffL;
|
|
}
|
|
|
|
// replace with Byte.toUnsignedInt in Java 8
|
|
private static int byteToUnsignedInt(byte b) {
|
|
return ((int) b) & 0xff;
|
|
}
|
|
|
|
private ShellMessageType readMessageType() throws IOException {
|
|
return ShellMessageType.fromId(input.readByte());
|
|
}
|
|
|
|
private long readDataLength() throws IOException {
|
|
return integerToUnsignedLong(Integer.reverseBytes(input.readInt()));
|
|
}
|
|
|
|
private void readDataTo(OutputStream out, long dataLength, byte[] buffer) throws IOException {
|
|
long remaining = dataLength;
|
|
while (remaining > 0) {
|
|
int len = (int) Math.min(remaining, buffer.length);
|
|
input.readFully(buffer, 0, len);
|
|
out.write(buffer, 0, len);
|
|
remaining -= len;
|
|
}
|
|
out.flush();
|
|
}
|
|
|
|
int demuxOutput(OutputStream outputStream, OutputStream errorStream) throws JadbException, IOException {
|
|
int exitCode = 0;
|
|
byte[] buf = new byte[256 * 1024];
|
|
|
|
try {
|
|
while (true) {
|
|
ShellMessageType messageType = readMessageType();
|
|
long length = readDataLength();
|
|
switch (messageType) {
|
|
case STDOUT:
|
|
readDataTo(outputStream, length, buf);
|
|
break;
|
|
case STDERR:
|
|
readDataTo(errorStream, length, buf);
|
|
break;
|
|
case EXIT:
|
|
if (length != 1) {
|
|
throw new JadbException("Expected only one byte for exitCode");
|
|
}
|
|
exitCode = byteToUnsignedInt(input.readByte());
|
|
break;
|
|
default:
|
|
// ignore;
|
|
break;
|
|
}
|
|
}
|
|
} catch (EOFException e) {
|
|
// ignore
|
|
}
|
|
|
|
return exitCode;
|
|
}
|
|
|
|
private void writeData(ShellMessageType type, byte[] buf, int off, int len) throws IOException {
|
|
output.writeByte(byteToUnsignedInt(type.getId()));
|
|
output.writeInt(Integer.reverseBytes(len));
|
|
output.write(buf, off, len);
|
|
}
|
|
|
|
OutputStream getOutputStream() {
|
|
return new ShellProtocolOutputStream(this);
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
output.close();
|
|
input.close();
|
|
}
|
|
|
|
enum ShellMessageType {
|
|
STDIN((byte) 0), STDOUT((byte) 1), STDERR((byte) 2), EXIT((byte) 3), CLOSE_STDIN((byte) 4), WINDOW_SIZE_CHANGE((byte) 5), UNKNOWN(Byte.MIN_VALUE);
|
|
|
|
private final byte id;
|
|
|
|
ShellMessageType(byte id) {
|
|
this.id = id;
|
|
}
|
|
|
|
public static ShellMessageType fromId(byte b) {
|
|
switch (b) {
|
|
case 0:
|
|
return STDIN;
|
|
case 1:
|
|
return STDOUT;
|
|
case 2:
|
|
return STDERR;
|
|
case 3:
|
|
return EXIT;
|
|
case 4:
|
|
return CLOSE_STDIN;
|
|
case 5:
|
|
// unused
|
|
return WINDOW_SIZE_CHANGE;
|
|
default:
|
|
return UNKNOWN;
|
|
}
|
|
}
|
|
|
|
public byte getId() {
|
|
return id;
|
|
}
|
|
}
|
|
|
|
private static class ShellProtocolOutputStream extends OutputStream {
|
|
|
|
private final ShellProtocolTransport transport;
|
|
|
|
ShellProtocolOutputStream(ShellProtocolTransport transport) {
|
|
this.transport = transport;
|
|
}
|
|
|
|
@Override
|
|
public void write(int b) throws IOException {
|
|
write(new byte[]{(byte) b});
|
|
}
|
|
|
|
@Override
|
|
public void write(byte[] b, int off, int len) throws IOException {
|
|
transport.writeData(ShellMessageType.STDIN, b, off, len);
|
|
}
|
|
|
|
@Override
|
|
public void flush() throws IOException {
|
|
transport.output.flush();
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
transport.writeData(ShellMessageType.CLOSE_STDIN, new byte[0], 0, 0);
|
|
}
|
|
}
|
|
} |