diff options
| author | Lena <lena@omega> | 2026-01-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-06-24 22:14:50 +0300 |
| commit | eb0c8951196c637e44daf3c0617b131b997d5d2c (patch) | |
| tree | 2f83b6a41cee745467e53fc401942b82cea286ea /app/src/main/java/invalid/lena/scrcpy/Sync.java | |
| download | scrcpy-android-0.1.tar.gz | |
scrcpy-android: mirror an Android device over wireless ADB0.1
Native Java app for Android 12+ that mirrors another Android
device over wireless ADB, forwarding video, audio, touch input,
and clipboard. Bundles a pinned scrcpy-server.jar and the
vendored libadb-android stack. Supports h264/h265/av1 video and
raw/opus audio with in-app codec selection. No NDK, no Kotlin.
Includes JVM unit tests and a Docker-based emulator e2e rig.
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Sync.java')
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/Sync.java | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Sync.java b/app/src/main/java/invalid/lena/scrcpy/Sync.java new file mode 100644 index 0000000..6e37523 --- /dev/null +++ b/app/src/main/java/invalid/lena/scrcpy/Sync.java @@ -0,0 +1,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); + } +} |