aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/Wire.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-08-01 00:00:00 +0000
committerLena <lena@omega>2026-08-01 00:00:00 +0000
commit852a8a00273c9128efeed0217a8e5d3fcd8bf780 (patch)
treec72f959731fa3c7c809cf1a0222363b6c9c9b03b /app/src/main/java/invalid/lena/scrcpy/Wire.java
parentf379b93d52bf0dbd3816ec3f64d165314771d2a6 (diff)
downloadscrcpy-android-852a8a00273c9128efeed0217a8e5d3fcd8bf780.tar.gz
app: harden mirroring lifecycle and state
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Wire.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Wire.java71
1 files changed, 59 insertions, 12 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Wire.java b/app/src/main/java/invalid/lena/scrcpy/Wire.java
index 7ed6c6a..3d78c9b 100644
--- a/app/src/main/java/invalid/lena/scrcpy/Wire.java
+++ b/app/src/main/java/invalid/lena/scrcpy/Wire.java
@@ -3,6 +3,10 @@ package invalid.lena.scrcpy;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
// Byte-order helpers used everywhere we touch raw streams.
//
@@ -18,6 +22,7 @@ public final class Wire {
// ---- big-endian (scrcpy) ----
public static int readBe32(byte[] b, int off) {
+ checkRange(b, off, 4);
return ((b[off] & 0xff) << 24)
| ((b[off + 1] & 0xff) << 16)
| ((b[off + 2] & 0xff) << 8)
@@ -25,10 +30,12 @@ public final class Wire {
}
public static long readBe64(byte[] b, int off) {
+ checkRange(b, off, 8);
return ((long)(readBe32(b, off)) << 32) | (readBe32(b, off + 4) & 0xffffffffL);
}
public static void writeBe32(byte[] b, int off, int v) {
+ checkRange(b, off, 4);
b[off] = (byte)(v >>> 24);
b[off + 1] = (byte)(v >>> 16);
b[off + 2] = (byte)(v >>> 8);
@@ -36,6 +43,7 @@ public final class Wire {
}
public static void writeBe64(byte[] b, int off, long v) {
+ checkRange(b, off, 8);
writeBe32(b, off, (int)(v >>> 32));
writeBe32(b, off + 4, (int) v);
}
@@ -43,6 +51,7 @@ public final class Wire {
// ---- little-endian (adb sync) ----
public static int readLe32(byte[] b, int off) {
+ checkRange(b, off, 4);
return (b[off] & 0xff)
| ((b[off + 1] & 0xff) << 8)
| ((b[off + 2] & 0xff) << 16)
@@ -50,6 +59,7 @@ public final class Wire {
}
public static void writeLe32(byte[] b, int off, int v) {
+ checkRange(b, off, 4);
b[off] = (byte) v;
b[off + 1] = (byte)(v >>> 8);
b[off + 2] = (byte)(v >>> 16);
@@ -59,11 +69,17 @@ public final class Wire {
// ---- I/O helpers ----
public static void readFully(InputStream in, byte[] buf, int off, int len) throws IOException {
+ if (in == null || buf == null) throw new NullPointerException();
+ if (off < 0 || len < 0 || off > buf.length || len > buf.length - off) {
+ throw new IndexOutOfBoundsException();
+ }
int got = 0;
while (got < len) {
int n = in.read(buf, off + got, len - got);
if (n < 0) throw new EOFException(
"short read: wanted " + len + " got " + got);
+ if (n == 0) throw new IOException(
+ "input made no progress: wanted " + len + " got " + got);
got += n;
}
}
@@ -72,28 +88,59 @@ public final class Wire {
readFully(in, buf, 0, buf.length);
}
- // scrcpy codec identifiers, mirrored from com.genymobile.scrcpy.{video,audio}.*Codec.
- // Short names (raw, aac) are NUL-padded on the left, not space-padded on the right.
+ public static String decodeUtf8(byte[] data, int off, int len)
+ throws CharacterCodingException {
+ checkRange(data, off, len);
+ return StandardCharsets.UTF_8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ .decode(ByteBuffer.wrap(data, off, len)).toString();
+ }
+
+ public static String decodeUtf8(byte[] data) throws CharacterCodingException {
+ return decodeUtf8(data, 0, data.length);
+ }
+
+ // scrcpy codec identifiers, mirrored from
+ // vendor/scrcpy/server/src/main/java/com/genymobile/scrcpy/video/VideoCodec.java
+ // and .../audio/AudioCodec.java, which are the authoritative source.
+ // Every id is the 4-char ASCII name NUL-padded on the LEFT for names
+ // shorter than four characters (av1, raw, aac), never space-padded on
+ // the right. WireTest asserts that rule rather than copying the
+ // literals, so a mis-transcribed id fails the build.
// Declared as int-literal constants so they can drive switch-case labels.
public static final int CODEC_H264 = 0x68_32_36_34; // 'h264'
public static final int CODEC_H265 = 0x68_32_36_35; // 'h265'
- public static final int CODEC_AV1 = 0x61_76_30_31; // 'av01'
+ public static final int CODEC_AV1 = 0x00_61_76_31; // '\0av1'
public static final int CODEC_OPUS = 0x6f_70_75_73; // 'opus'
- public static final int CODEC_FLAC = 0x66_6c_61_63; // 'flac'
- public static final int CODEC_AAC = 0x00_61_61_63; // '\0aac'
public static final int CODEC_RAW = 0x00_72_61_77; // '\0raw'
public static String fourccName(int v) {
// Skip any leading NUL bytes - scrcpy left-pads short names like
// 'raw' and 'aac' with \0, which would otherwise render as
// non-printable characters in logs.
- char[] cs = new char[]{
- (char)((v >>> 24) & 0xff),
- (char)((v >>> 16) & 0xff),
- (char)((v >>> 8) & 0xff),
- (char)( v & 0xff)};
+ int[] bytes = {(v >>> 24) & 0xff, (v >>> 16) & 0xff,
+ (v >>> 8) & 0xff, v & 0xff};
int from = 0;
- while (from < cs.length && cs[from] == 0) from++;
- return new String(cs, from, cs.length - from);
+ while (from < bytes.length && bytes[from] == 0) from++;
+ StringBuilder out = new StringBuilder(4);
+ char[] hex = "0123456789abcdef".toCharArray();
+ for (int i = from; i < bytes.length; i++) {
+ int b = bytes[i];
+ if (b >= 0x20 && b <= 0x7e) {
+ out.append((char) b);
+ } else {
+ out.append("\\x").append(hex[b >>> 4]).append(hex[b & 0xf]);
+ }
+ }
+ return out.toString();
+ }
+
+ private static void checkRange(byte[] data, int off, int len) {
+ if (data == null) throw new NullPointerException("data");
+ if (off < 0 || len < 0 || off > data.length - len) {
+ throw new IndexOutOfBoundsException("off=" + off + " len=" + len
+ + " size=" + data.length);
+ }
}
}