From 852a8a00273c9128efeed0217a8e5d3fcd8bf780 Mon Sep 17 00:00:00 2001 From: Lena Date: Sat, 1 Aug 2026 00:00:00 +0000 Subject: app: harden mirroring lifecycle and state --- .../java/invalid/lena/scrcpy/ControlMessages.java | 37 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'app/src/main/java/invalid/lena/scrcpy/ControlMessages.java') diff --git a/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java b/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java index aba2ca8..6455a65 100644 --- a/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java +++ b/app/src/main/java/invalid/lena/scrcpy/ControlMessages.java @@ -8,28 +8,52 @@ import java.nio.charset.StandardCharsets; // unit-tested without android.* on the classpath. public final class ControlMessages { - static final int MAX_CLIPBOARD_BYTES = 1 << 20; + // scrcpy caps a whole control message at MESSAGE_MAX_SIZE = 256 KiB. + // For SET_CLIPBOARD that leaves 256 KiB minus the 14-byte header + // (type 1, sequence 8, paste flag 1, length 4); see the server's + // ControlMessageReader.CLIPBOARD_TEXT_MAX_LENGTH. Going over makes + // the server raise ControlProtocolException and drop the control + // connection, taking the session with it, so refuse locally instead. + static final int MAX_CLIPBOARD_BYTES = (1 << 18) - 14; + + // The reverse direction, from the server's DeviceMessageWriter: + // 256 KiB minus its 5-byte header (type 1, length 4). Used to bound + // what we are willing to read off the control socket. + static final int MAX_DEVICE_CLIPBOARD_BYTES = (1 << 18) - 5; public static final int TYPE_INJECT_KEYCODE = 0; - public static final int TYPE_INJECT_TEXT = 1; public static final int TYPE_INJECT_TOUCH_EVENT = 2; public static final int TYPE_BACK_OR_SCREEN_ON = 4; public static final int TYPE_SET_CLIPBOARD = 9; + public static final int TYPE_RESET_VIDEO = 17; // KeyEvent.ACTION_DOWN / ACTION_UP. Mirror the int values rather // than depend on android.view.KeyEvent so this stays android-free. public static final int ACTION_DOWN = 0; public static final int ACTION_UP = 1; + // AOSP keycodes, mirrored rather than imported so this stays + // android-free. KeyEvent.KEYCODE_HOME / KEYCODE_APP_SWITCH. + public static final int KEYCODE_HOME = 3; + public static final int KEYCODE_APP_SWITCH = 187; + public static final int TOUCH_MSG_LEN = 32; // 1 + 1 + 8 + 4 + 4 + 2 + 2 + 2 + 4 + 4 public static final int KEY_MSG_LEN = 14; // 1 + 1 + 4 + 4 + 4 - public static final int BACK_MSG_LEN = 2; // 1 + 1 private ControlMessages() {} public static byte[] touch(int action, long pointerId, int x, int y, int targetW, int targetH, int pressureU16, int actionButton, int buttons) { + if (targetW < 1 || targetW > 0xffff || targetH < 1 || targetH > 0xffff) { + throw new IllegalArgumentException("touch target size is out of range"); + } + if (x < 0 || x >= targetW || y < 0 || y >= targetH) { + throw new IllegalArgumentException("touch position is out of range"); + } + if (pressureU16 < 0 || pressureU16 > 0xffff) { + throw new IllegalArgumentException("touch pressure is out of range"); + } byte[] m = new byte[TOUCH_MSG_LEN]; m[0] = TYPE_INJECT_TOUCH_EVENT; m[1] = (byte) action; @@ -61,7 +85,14 @@ public final class ControlMessages { return new byte[]{(byte) TYPE_BACK_OR_SCREEN_ON, (byte) action}; } + public static byte[] resetVideo() { + return new byte[]{(byte) TYPE_RESET_VIDEO}; + } + public static byte[] setClipboard(long sequence, boolean paste, String text) { + // UTF-8 is at least one byte per char, so this rejects the + // hopeless cases without encoding a huge string first. The byte + // count below is the check that actually matters. if (text.length() > MAX_CLIPBOARD_BYTES) { throw new IllegalArgumentException("clipboard text is too large"); } -- cgit v1.2.3