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
+11 -6
View File
@@ -8,6 +8,8 @@
#include <cstdlib>
#include <ctime>
#include <mutex>
#include <string>
#include <vector>
extern "C" {
#include <libavcodec/jni.h>
@@ -137,16 +139,19 @@ jni_func(void, nativeCommand, jobjectArray jarray) {
const char* arguments[128] = {0};
int len = env->GetArrayLength(jarray);
if (len >= ARRAYLEN(arguments)) {
if (len >= (int)ARRAYLEN(arguments)) {
die("too many command arguments");
return;
}
for (int i = 0; i < len; ++i)
arguments[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), NULL);
std::vector<std::string> storage;
storage.reserve(len);
for (int i = 0; i < len; ++i) {
jstring jarg = (jstring)env->GetObjectArrayElement(jarray, i);
storage.push_back(java_string_to_utf8(env, jarg));
arguments[i] = storage.back().c_str();
env->DeleteLocalRef(jarg);
}
mpv_command(g_mpv, arguments);
for (int i = 0; i < len; ++i)
env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(jarray, i), arguments[i]);
}