fix(android): transcode mpv strings as standard UTF-8 across JNI

Emoji and other supplementary-plane characters in titles, file names, and
subtitle paths were corrupted on the Android MPV path, and malformed bytes
from mpv logs could abort under CheckJNI. NewStringUTF/GetStringUTFChars
speak JNI's modified UTF-8, not the standard UTF-8 mpv produces and
consumes; the native lead-byte filter and the Kotlin surrogate scrubber
ran on the wrong side of that conversion and could not recover it.

Both directions now go through UTF-16 (NewString/GetStringChars) with a
small JNI-free transcoder that replaces malformed input with U+FFFD, the
same policy as shared/cpp/sanitize_utf8.h on desktop. The header is
covered by the Android host native test harness.
This commit is contained in:
edde746
2026-09-03 11:36:29 +02:00
parent 54efd4fe73
commit c70f88cee0
9 changed files with 240 additions and 55 deletions
+4 -11
View File
@@ -6,7 +6,7 @@
#include "log.h"
static void sendPropertyUpdateToJava(JNIEnv* env, mpv_event_property* prop) {
jstring jprop = env->NewStringUTF(prop->name);
jstring jprop = new_java_string(env, prop->name);
jstring jvalue = NULL;
switch (prop->format) {
case MPV_FORMAT_NONE:
@@ -22,7 +22,7 @@ static void sendPropertyUpdateToJava(JNIEnv* env, mpv_event_property* prop) {
env->CallStaticVoidMethod(mpv_MpvPlayer, mpv_MpvPlayer_onPropertyChanged_Sd, jprop, *(double*)prop->data);
break;
case MPV_FORMAT_STRING:
jvalue = env->NewStringUTF(*(const char**)prop->data);
jvalue = new_java_string(env, *(const char**)prop->data);
env->CallStaticVoidMethod(mpv_MpvPlayer, mpv_MpvPlayer_onPropertyChanged_SS, jprop, jvalue);
break;
default:
@@ -42,16 +42,9 @@ static void sendEndFileToJava(JNIEnv* env, mpv_event* event) {
env->CallStaticVoidMethod(mpv_MpvPlayer, mpv_MpvPlayer_onEndFile, (jint)reason);
}
static inline bool invalid_utf8(unsigned char c) { return c == 0xc0 || c == 0xc1 || c >= 0xf5; }
static void sendLogMessageToJava(JNIEnv* env, mpv_event_log_message* msg) {
const auto invalid_utf8 = [](unsigned char c) { return c == 0xc0 || c == 0xc1 || c >= 0xf5; };
for (int i = 0; msg->text[i]; i++) {
if (invalid_utf8(static_cast<unsigned char>(msg->text[i]))) return;
}
jstring jprefix = env->NewStringUTF(msg->prefix);
jstring jtext = env->NewStringUTF(msg->text);
jstring jprefix = new_java_string(env, msg->prefix);
jstring jtext = new_java_string(env, msg->text);
env->CallStaticVoidMethod(mpv_MpvPlayer, mpv_MpvPlayer_onLogMessage, jprefix, (jint)msg->log_level, jtext);