diff --git a/meson.build b/meson.build index 7291dfc2..3f3a9127 100644 --- a/meson.build +++ b/meson.build @@ -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_session_MediaSession.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/sensors/android_hardware_SensorManager.c', 'src/api-impl-jni/text/android_text_Layout.c', diff --git a/src/api-impl-jni/generated_headers/android_os_Parcel.h b/src/api-impl-jni/generated_headers/android_os_Parcel.h new file mode 100644 index 00000000..61adf2d9 --- /dev/null +++ b/src/api-impl-jni/generated_headers/android_os_Parcel.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* 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 diff --git a/src/api-impl-jni/handle_cache.c b/src/api-impl-jni/handle_cache.c index 0541f220..765f6ccd 100644 --- a/src/api-impl-jni/handle_cache.c +++ b/src/api-impl-jni/handle_cache.c @@ -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.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.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.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.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, "", "(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;"); } diff --git a/src/api-impl-jni/handle_cache.h b/src/api-impl-jni/handle_cache.h index 4d0d3a32..1adbb03c 100644 --- a/src/api-impl-jni/handle_cache.h +++ b/src/api-impl-jni/handle_cache.h @@ -127,6 +127,7 @@ struct handle_cache { jmethodID putExtraByteArray; jmethodID putExtraInt; jmethodID putExtraLong; + jmethodID putExtraParcelable; jmethodID getDataString; jmethodID setClassName; } intent; @@ -157,6 +158,12 @@ struct handle_cache { jclass class; jmethodID toArray; } set; + struct { + jclass class; + jmethodID constructor; + jmethodID writeParcelable; + jmethodID readParcelable; + } parcel; }; extern struct handle_cache handle_cache; diff --git a/src/api-impl-jni/os/android_os_Parcel.c b/src/api-impl-jni/os/android_os_Parcel.c new file mode 100644 index 00000000..b7c9ad9b --- /dev/null +++ b/src/api-impl-jni/os/android_os_Parcel.c @@ -0,0 +1,37 @@ +#include + +#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; +} diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 85d8db03..431a7502 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -257,18 +257,27 @@ GVariant *intent_serialize(JNIEnv *env, jobject intent) { 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); 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++) { jstring key_jstr = (*env)->GetObjectArrayElement(env, extras_keys, i); 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 *value = (*env)->GetStringUTFChars(env, value_jobj, NULL); - g_variant_builder_add(&extras_builder, "{sv}", key, g_variant_new_string(value)); + if ((*env)->IsInstanceOf(env, value_jobj, _CLASS(key_jstr))) { + const char *value = (*env)->GetStringUTFChars(env, value_jobj, NULL); + g_variant_builder_add(&extras_builder, "{sv}", key, g_variant_new_string(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)->ReleaseStringUTFChars(env, value_jobj, value); (*env)->DeleteLocalRef(env, key_jstr); (*env)->DeleteLocalRef(env, value_jobj); } @@ -323,6 +332,20 @@ jobject intent_deserialize(JNIEnv *env, GVariant *variant) { jbyteArray bytesMessage = (*env)->NewByteArray(env, size); (*env)->SetByteArrayRegion(env, bytesMessage, 0, size, message); (*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); diff --git a/src/api-impl/android/os/Parcel.java b/src/api-impl/android/os/Parcel.java index 498b03be..701da17b 100644 --- a/src/api-impl/android/os/Parcel.java +++ b/src/api-impl/android/os/Parcel.java @@ -5,8 +5,16 @@ import java.util.List; public class Parcel { + private long builder; + private long iter; + + Parcel(long builder, long iter) { + this.builder = builder; + this.iter = iter; + } + public static Parcel obtain() { - return new Parcel(); + return new Parcel(0, 0); } public void writeBundle(Bundle bundle) {} @@ -25,6 +33,7 @@ public class Parcel { public void writeString(String s) { System.out.println("Parcel.writeString(" + s + ")"); + native_writeString(builder, s); } public void writeLong(long l) { @@ -33,6 +42,7 @@ public class Parcel { public void writeInt(int i) { System.out.println("Parcel.writeInt(" + i + ")"); + native_writeInt(builder, i); } public byte[] marshall() { @@ -45,6 +55,8 @@ public class Parcel { public void writeParcelable(Parcelable p, int flags) { System.out.println("Parcel.writeParcelable(" + p + ", " + flags + ")"); + native_writeString(builder, p.getClass().getName()); + p.writeToParcel(this, flags); } public void writeInterfaceToken(String s) { @@ -66,7 +78,7 @@ public class Parcel { public void unmarshall(byte[] data, int offset, int length) {} public String readString() { - return "fixme: Parcel.readString()"; + return native_readString(iter); } public byte readByte() { @@ -77,11 +89,40 @@ public class Parcel { return new ArrayList(); } - public Parcelable readParcelable(ClassLoader loader) { - return null; + public Parcelable readParcelable(ClassLoader loader) throws ReflectiveOperationException { + String className = native_readString(iter); + @SuppressWarnings("unchecked") + Parcelable.Creator creator = loader.loadClass(className + "$Creator").asSubclass(Parcelable.Creator.class).getConstructor().newInstance(); + return creator.createFromParcel(this); } public void writeStrongBinder(IBinder binder) {} 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); }