From f9586e171427c1b078b4f3fe81a5729a5d21aa5c Mon Sep 17 00:00:00 2001 From: ignat10 Date: Mon, 2 Nov 2026 05:14:30 +0100 Subject: [PATCH] fix(android): prevent crash on large clipboard payload (>1MB) (#16767) * Limit oversized remote clipboard text on Android * Add write permission * Increase MAX_CLIPBOARD_TEXT_CODE_UNITS to 1MB * fix: revert unrelated workflow permission changes * fix(android): address review feedback on clipboard size and exception handling - Replace UTF-16 character count check with a serialized Parcel size check capped at 800 KB, leaving headroom for Android Binder transaction overhead. - Wrap setPrimaryClip() in a try-catch to safely absorb direct and runtime-wrapped Binder exceptions (e.g. TransactionTooLargeException) without crashing. - Assign lastUpdatedClipData only after a successful clipboard write to preserve consistent synchronization bookkeeping on failure. --- .../carriez/flutter_hbb/RdClipboardManager.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt index 8c9d85028..fc50c3750 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt @@ -7,6 +7,7 @@ import java.util.TimerTask import android.content.ClipData import android.content.ClipDescription import android.content.ClipboardManager +import android.os.Parcel import android.util.Log import androidx.annotation.Keep @@ -17,6 +18,10 @@ import hbb.MessageOuterClass.MultiClipboards import ffi.FFI class RdClipboardManager(private val clipboardManager: ClipboardManager) { + companion object { + private const val MAX_CLIPBOARD_PARCEL_BYTES = 800 * 1024 + } + private val logTag = "RdClipboardManager" private val supportedMimeTypes = arrayOf( ClipDescription.MIMETYPE_TEXT_PLAIN, @@ -191,7 +196,23 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) { return } val clipData = ClipData(clipDescription, item) - lastUpdatedClipData = clipData - clipboardManager.setPrimaryClip(clipData) + val parcel = Parcel.obtain() + val isTooLarge = try { + clipData.writeToParcel(parcel, 0) + parcel.dataSize() > MAX_CLIPBOARD_PARCEL_BYTES + } finally { + parcel.recycle() + } + if (isTooLarge) { + Log.w(logTag, "Remote clipboard text is too large for Android clipboard") + return + } + + try { + clipboardManager.setPrimaryClip(clipData) + lastUpdatedClipData = clipData + } catch (e: Exception) { + Log.e(logTag, "Failed to set primary clip", e) + } } }