implement Parcelable serialization using GVariantBuilder

This is needed for the error reporting notification intent in NewPipe
now that we support persistent notifications.
This commit is contained in:
Julian Winkler
2025-10-05 08:17:07 +02:00
parent 90bb5e4d10
commit b76eb0f1be
7 changed files with 171 additions and 11 deletions

View File

@@ -122,6 +122,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
'src/api-impl-jni/media/android_media_MediaPlayer.c', 'src/api-impl-jni/media/android_media_MediaPlayer.c',
'src/api-impl-jni/media/android_media_session_MediaSession.c', 'src/api-impl-jni/media/android_media_session_MediaSession.c',
'src/api-impl-jni/net/android_net_ConnectivityManager.c', 'src/api-impl-jni/net/android_net_ConnectivityManager.c',
'src/api-impl-jni/os/android_os_Parcel.c',
'src/api-impl-jni/os/android_os_Process.c', 'src/api-impl-jni/os/android_os_Process.c',
'src/api-impl-jni/sensors/android_hardware_SensorManager.c', 'src/api-impl-jni/sensors/android_hardware_SensorManager.c',
'src/api-impl-jni/text/android_text_Layout.c', 'src/api-impl-jni/text/android_text_Layout.c',

View File

@@ -0,0 +1,45 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class android_os_Parcel */
#ifndef _Included_android_os_Parcel
#define _Included_android_os_Parcel
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: android_os_Parcel
* Method: native_writeInt
* Signature: (JI)V
*/
JNIEXPORT void JNICALL Java_android_os_Parcel_native_1writeInt
(JNIEnv *, jclass, jlong, jint);
/*
* Class: android_os_Parcel
* Method: native_writeString
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_android_os_Parcel_native_1writeString
(JNIEnv *, jclass, jlong, jstring);
/*
* Class: android_os_Parcel
* Method: native_readInt
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_android_os_Parcel_native_1readInt
(JNIEnv *, jclass, jlong);
/*
* Class: android_os_Parcel
* Method: native_readString
* Signature: (J)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_android_os_Parcel_native_1readString
(JNIEnv *, jclass, jlong);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -124,6 +124,7 @@ void set_up_handle_cache(JNIEnv *env)
handle_cache.intent.putExtraByteArray = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;[B)Landroid/content/Intent;"); handle_cache.intent.putExtraByteArray = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;[B)Landroid/content/Intent;");
handle_cache.intent.putExtraInt = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;I)Landroid/content/Intent;"); handle_cache.intent.putExtraInt = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;I)Landroid/content/Intent;");
handle_cache.intent.putExtraLong = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;J)Landroid/content/Intent;"); handle_cache.intent.putExtraLong = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;J)Landroid/content/Intent;");
handle_cache.intent.putExtraParcelable = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;");
handle_cache.intent.getDataString = _METHOD(handle_cache.intent.class, "getDataString", "()Ljava/lang/String;"); handle_cache.intent.getDataString = _METHOD(handle_cache.intent.class, "getDataString", "()Ljava/lang/String;");
handle_cache.intent.setClassName = _METHOD(handle_cache.intent.class, "setClassName", "(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"); handle_cache.intent.setClassName = _METHOD(handle_cache.intent.class, "setClassName", "(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;");
@@ -145,4 +146,9 @@ void set_up_handle_cache(JNIEnv *env)
handle_cache.set.class = _REF((*env)->FindClass(env, "java/util/Set")); handle_cache.set.class = _REF((*env)->FindClass(env, "java/util/Set"));
handle_cache.set.toArray = _METHOD(handle_cache.set.class, "toArray", "()[Ljava/lang/Object;"); handle_cache.set.toArray = _METHOD(handle_cache.set.class, "toArray", "()[Ljava/lang/Object;");
handle_cache.parcel.class = _REF((*env)->FindClass(env, "android/os/Parcel"));
handle_cache.parcel.constructor = _METHOD(handle_cache.parcel.class, "<init>", "(JJ)V");
handle_cache.parcel.writeParcelable = _METHOD(handle_cache.parcel.class, "writeParcelable", "(Landroid/os/Parcelable;I)V");
handle_cache.parcel.readParcelable = _METHOD(handle_cache.parcel.class, "readParcelable", "(Ljava/lang/ClassLoader;)Landroid/os/Parcelable;");
} }

View File

@@ -127,6 +127,7 @@ struct handle_cache {
jmethodID putExtraByteArray; jmethodID putExtraByteArray;
jmethodID putExtraInt; jmethodID putExtraInt;
jmethodID putExtraLong; jmethodID putExtraLong;
jmethodID putExtraParcelable;
jmethodID getDataString; jmethodID getDataString;
jmethodID setClassName; jmethodID setClassName;
} intent; } intent;
@@ -157,6 +158,12 @@ struct handle_cache {
jclass class; jclass class;
jmethodID toArray; jmethodID toArray;
} set; } set;
struct {
jclass class;
jmethodID constructor;
jmethodID writeParcelable;
jmethodID readParcelable;
} parcel;
}; };
extern struct handle_cache handle_cache; extern struct handle_cache handle_cache;

View File

@@ -0,0 +1,37 @@
#include <glib.h>
#include "../defines.h"
#include "../generated_headers/android_os_Parcel.h"
JNIEXPORT void JNICALL Java_android_os_Parcel_native_1writeInt(JNIEnv *env, jclass clazz, jlong builder_ptr, jint value)
{
GVariantBuilder *builder = (GVariantBuilder *)builder_ptr;
if (builder)
g_variant_builder_add(builder, "i", value);
}
JNIEXPORT void JNICALL Java_android_os_Parcel_native_1writeString(JNIEnv *env, jclass clazz, jlong builder_ptr, jstring value)
{
GVariantBuilder *builder = (GVariantBuilder *)builder_ptr;
if (builder)
g_variant_builder_add(builder, "s", (*env)->GetStringUTFChars(env, value, NULL));
}
JNIEXPORT jint JNICALL Java_android_os_Parcel_native_1readInt(JNIEnv *env, jclass clazz, jlong iter_ptr)
{
GVariantIter *iter = (GVariantIter *)iter_ptr;
jint i = 0;
if (iter)
g_variant_iter_next(iter, "i", &i);
return i;
}
JNIEXPORT jstring JNICALL Java_android_os_Parcel_native_1readString(JNIEnv *env, jclass clazz, jlong iter_ptr)
{
GVariantIter *iter = (GVariantIter *)iter_ptr;
const char *s = NULL;
if (iter)
g_variant_iter_next(iter, "s", &s);
return s ? _JSTRING(s) : NULL;
}

View File

@@ -257,18 +257,27 @@ GVariant *intent_serialize(JNIEnv *env, jobject intent) {
jobject extras_key_set = (*env)->CallObjectMethod(env, extras, handle_cache.bundle.keySet); jobject extras_key_set = (*env)->CallObjectMethod(env, extras, handle_cache.bundle.keySet);
jobjectArray extras_keys = (*env)->CallObjectMethod(env, extras_key_set, handle_cache.set.toArray); jobjectArray extras_keys = (*env)->CallObjectMethod(env, extras_key_set, handle_cache.set.toArray);
jsize extras_keys_length = (*env)->GetArrayLength(env, extras_keys); jsize extras_keys_length = (*env)->GetArrayLength(env, extras_keys);
jclass parcelable_class = (*env)->FindClass(env, "android/os/Parcelable");
for (jint i = 0; i < extras_keys_length; i++) { for (jint i = 0; i < extras_keys_length; i++) {
jstring key_jstr = (*env)->GetObjectArrayElement(env, extras_keys, i); jstring key_jstr = (*env)->GetObjectArrayElement(env, extras_keys, i);
jobject value_jobj = (*env)->CallObjectMethod(env, extras, handle_cache.bundle.get, key_jstr); jobject value_jobj = (*env)->CallObjectMethod(env, extras, handle_cache.bundle.get, key_jstr);
if (!(*env)->IsSameObject(env, _CLASS(key_jstr), _CLASS(value_jobj))) {
printf("skipping non-string extra: %s\n", (*env)->GetStringUTFChars(env, key_jstr, NULL));
continue;
}
const char *key = (*env)->GetStringUTFChars(env, key_jstr, NULL); const char *key = (*env)->GetStringUTFChars(env, key_jstr, NULL);
if ((*env)->IsInstanceOf(env, value_jobj, _CLASS(key_jstr))) {
const char *value = (*env)->GetStringUTFChars(env, value_jobj, NULL); const char *value = (*env)->GetStringUTFChars(env, value_jobj, NULL);
g_variant_builder_add(&extras_builder, "{sv}", key, g_variant_new_string(value)); g_variant_builder_add(&extras_builder, "{sv}", key, g_variant_new_string(value));
(*env)->ReleaseStringUTFChars(env, key_jstr, key);
(*env)->ReleaseStringUTFChars(env, value_jobj, value); (*env)->ReleaseStringUTFChars(env, value_jobj, value);
} else if ((*env)->IsInstanceOf(env, value_jobj, parcelable_class)) {
GVariantBuilder parcel_builder;
g_variant_builder_init_static(&parcel_builder, G_VARIANT_TYPE_TUPLE);
jobject parcel = (*env)->NewObject(env, handle_cache.parcel.class, handle_cache.parcel.constructor, _INTPTR(&parcel_builder), 0);
(*env)->CallVoidMethod(env, parcel, handle_cache.parcel.writeParcelable, value_jobj, 0);
GVariant *parcel_variant = g_variant_builder_end(&parcel_builder);
g_variant_builder_add(&extras_builder, "{sv}", key, parcel_variant);
(*env)->DeleteLocalRef(env, parcel);
} else {
printf("intent_serialize: skipping non-string, non-parcelable extra: %s\n", key);
}
(*env)->ReleaseStringUTFChars(env, key_jstr, key);
(*env)->DeleteLocalRef(env, key_jstr); (*env)->DeleteLocalRef(env, key_jstr);
(*env)->DeleteLocalRef(env, value_jobj); (*env)->DeleteLocalRef(env, value_jobj);
} }
@@ -323,6 +332,20 @@ jobject intent_deserialize(JNIEnv *env, GVariant *variant) {
jbyteArray bytesMessage = (*env)->NewByteArray(env, size); jbyteArray bytesMessage = (*env)->NewByteArray(env, size);
(*env)->SetByteArrayRegion(env, bytesMessage, 0, size, message); (*env)->SetByteArrayRegion(env, bytesMessage, 0, size, message);
(*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraByteArray, _JSTRING(key), bytesMessage); (*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraByteArray, _JSTRING(key), bytesMessage);
} else if (g_variant_is_of_type(value, G_VARIANT_TYPE_TUPLE)) {
GVariantIter parcel_iter;
g_variant_iter_init(&parcel_iter, value);
jobject parcel = (*env)->NewObject(env, handle_cache.parcel.class, handle_cache.parcel.constructor, 0, _INTPTR(&parcel_iter));
jmethodID getClassLoader = _METHOD((*env)->FindClass(env, "java/lang/Class"), "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject class_loader = (*env)->CallObjectMethod(env, handle_cache.parcel.class, getClassLoader);
jobject parcelable = (*env)->CallObjectMethod(env, parcel, handle_cache.parcel.readParcelable, class_loader);
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
}
(*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraParcelable, _JSTRING(key), parcelable);
(*env)->DeleteLocalRef(env, parcelable);
(*env)->DeleteLocalRef(env, parcel);
} }
} }
g_variant_iter_free(extras); g_variant_iter_free(extras);

View File

@@ -5,8 +5,16 @@ import java.util.List;
public class Parcel { public class Parcel {
private long builder;
private long iter;
Parcel(long builder, long iter) {
this.builder = builder;
this.iter = iter;
}
public static Parcel obtain() { public static Parcel obtain() {
return new Parcel(); return new Parcel(0, 0);
} }
public void writeBundle(Bundle bundle) {} public void writeBundle(Bundle bundle) {}
@@ -25,6 +33,7 @@ public class Parcel {
public void writeString(String s) { public void writeString(String s) {
System.out.println("Parcel.writeString(" + s + ")"); System.out.println("Parcel.writeString(" + s + ")");
native_writeString(builder, s);
} }
public void writeLong(long l) { public void writeLong(long l) {
@@ -33,6 +42,7 @@ public class Parcel {
public void writeInt(int i) { public void writeInt(int i) {
System.out.println("Parcel.writeInt(" + i + ")"); System.out.println("Parcel.writeInt(" + i + ")");
native_writeInt(builder, i);
} }
public byte[] marshall() { public byte[] marshall() {
@@ -45,6 +55,8 @@ public class Parcel {
public void writeParcelable(Parcelable p, int flags) { public void writeParcelable(Parcelable p, int flags) {
System.out.println("Parcel.writeParcelable(" + p + ", " + flags + ")"); System.out.println("Parcel.writeParcelable(" + p + ", " + flags + ")");
native_writeString(builder, p.getClass().getName());
p.writeToParcel(this, flags);
} }
public void writeInterfaceToken(String s) { public void writeInterfaceToken(String s) {
@@ -66,7 +78,7 @@ public class Parcel {
public void unmarshall(byte[] data, int offset, int length) {} public void unmarshall(byte[] data, int offset, int length) {}
public String readString() { public String readString() {
return "fixme: Parcel.readString()"; return native_readString(iter);
} }
public byte readByte() { public byte readByte() {
@@ -77,11 +89,40 @@ public class Parcel {
return new ArrayList<String>(); return new ArrayList<String>();
} }
public Parcelable readParcelable(ClassLoader loader) { public Parcelable readParcelable(ClassLoader loader) throws ReflectiveOperationException {
return null; String className = native_readString(iter);
@SuppressWarnings("unchecked")
Parcelable.Creator<Parcelable> creator = loader.loadClass(className + "$Creator").asSubclass(Parcelable.Creator.class).getConstructor().newInstance();
return creator.createFromParcel(this);
} }
public void writeStrongBinder(IBinder binder) {} public void writeStrongBinder(IBinder binder) {}
public void readException() {} public void readException() {}
public int readInt() {
return native_readInt(iter);
}
public void writeStringArray(String[] strings) {
System.out.println("Parcel.writeStringArray(" + strings + ")");
native_writeInt(builder, strings.length);
for (String s : strings) {
native_writeString(builder, s);
}
}
public String[] createStringArray() {
int len = native_readInt(iter);
String[] strings = new String[len];
for (int i = 0; i < len; i++) {
strings[i] = native_readString(iter);
}
return strings;
}
protected static native void native_writeInt(long builder, int i);
protected static native void native_writeString(long builder, String s);
protected static native int native_readInt(long iter);
protected static native String native_readString(long iter);
} }