package invalid.lena.scrcpy; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; // Pure-java adb sync v1 SEND framing. Extracted from Server.push() so // it can be unit-tested without an AdbStream or any android coupling. // // Layout on the wire (little-endian uint32 lengths): // "SEND" | u32(len(path,mode)) | path,mode // "DATA" | u32(chunk_size) | chunk_bytes (repeats, chunk <= 64K) // "DONE" | u32(mtime_seconds) // ----> response: // "OKAY" | u32(0) // or // "FAIL" | u32(msg_len) | msg public final class Sync { public static final int CHUNK = 64 * 1024; // FAIL responses carry a short human-readable message; cap what a // corrupt or hostile length field can make us allocate. private static final int MAX_FAIL_MSG = 4 * 1024; private Sync() {} // Push `src` to `remotePath` with the given mode and mtime, using the // already-open `out` and `in` of an adb sync stream. Returns the // total payload byte count. public static long push(InputStream src, OutputStream out, InputStream in, String remotePath, int mode, int mtimeSec) throws IOException { String header = remotePath + "," + mode; byte[] hb = header.getBytes(StandardCharsets.UTF_8); byte[] tag = new byte[8]; putTag(tag, 0, "SEND"); Wire.writeLe32(tag, 4, hb.length); out.write(tag); out.write(hb); byte[] chunk = new byte[CHUNK]; byte[] dataHdr = new byte[8]; putTag(dataHdr, 0, "DATA"); long total = 0; int n; while ((n = src.read(chunk)) > 0) { Wire.writeLe32(dataHdr, 4, n); out.write(dataHdr); out.write(chunk, 0, n); total += n; } byte[] done = new byte[8]; putTag(done, 0, "DONE"); Wire.writeLe32(done, 4, mtimeSec); out.write(done); out.flush(); byte[] resp = new byte[8]; Wire.readFully(in, resp); String code = new String(resp, 0, 4, StandardCharsets.US_ASCII); int len = Wire.readLe32(resp, 4); if ("OKAY".equals(code)) return total; byte[] msg = new byte[Math.min(Math.max(0, len), MAX_FAIL_MSG)]; if (msg.length > 0) Wire.readFully(in, msg); throw new IOException("sync " + code + ": " + new String(msg, StandardCharsets.UTF_8)); } private static void putTag(byte[] dst, int off, String tag) { byte[] b = tag.getBytes(StandardCharsets.US_ASCII); System.arraycopy(b, 0, dst, off, 4); } }