1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
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);
}
}
|