diff --git a/meson.build b/meson.build index f36ed95c..7247681d 100644 --- a/meson.build +++ b/meson.build @@ -47,6 +47,12 @@ mpris = gnome.gdbus_codegen('mpris-dbus', portal_openuri = gnome.gdbus_codegen('portal-openuri', 'src/api-impl-jni/content/org.freedesktop.portal.OpenURI.xml', interface_prefix: 'org.freedesktop.portal') +unifiedpush_distributor = gnome.gdbus_codegen('unifiedpush-distributor', + 'src/api-impl-jni/content/org.unifiedpush.Distributor1.xml', + interface_prefix: 'org.unifiedpush') +unifiedpush_connector = gnome.gdbus_codegen('unifiedpush-connector', + 'src/api-impl-jni/content/org.unifiedpush.Connector1.xml', + interface_prefix: 'org.unifiedpush') extra_deps = [] extra_jni_srcs = [] @@ -157,6 +163,8 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/widgets/android_widget_TextView.c', mpris, portal_openuri, + unifiedpush_distributor, + unifiedpush_connector, wl_proto_headers, wl_proto_sources, extra_jni_srcs, diff --git a/src/api-impl-jni/content/android_content_Context.c b/src/api-impl-jni/content/android_content_Context.c index ea569ba9..bb95cd82 100644 --- a/src/api-impl-jni/content/android_content_Context.c +++ b/src/api-impl-jni/content/android_content_Context.c @@ -8,6 +8,8 @@ #endif #include "portal-openuri.h" +#include "unifiedpush-distributor.h" +#include "unifiedpush-connector.h" #include "../defines.h" #include "../util.h" @@ -86,3 +88,90 @@ JNIEXPORT void JNICALL Java_android_content_Context_nativeOpenFile(JNIEnv *env, g_object_unref(openuri); g_object_unref(connection); } + +static void on_bus_acquired(GDBusConnection *connection, const char *name, gpointer user_data) +{ + Connector1 *connector1 = user_data; + g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(connector1), + connection, "/org/unifiedpush/Connector", NULL); +} + +static gboolean on_new_endpoint(Connector1 *connector, GDBusMethodInvocation *invocation, gpointer user_data) +{ + GVariant *parameters = g_dbus_method_invocation_get_parameters(invocation); + const char *token; + g_variant_get_child(parameters, 0, "s", &token); + const char *endpoint; + g_variant_get_child(parameters, 1, "s", &endpoint); + connector1_complete_new_endpoint(connector, invocation); + + JNIEnv *env = get_jni_env(); + jobject intent = (*env)->NewObject(env, handle_cache.intent.class, handle_cache.intent.constructor); + _SET_OBJ_FIELD(intent, "action", "Ljava/lang/String;", _JSTRING("org.unifiedpush.android.connector.NEW_ENDPOINT")); + (*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraCharSequence, _JSTRING("token"), _JSTRING(token)); + (*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraCharSequence, _JSTRING("endpoint"), _JSTRING(endpoint)); + + jobject context = _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"); + (*env)->CallVoidMethod(env, context, handle_cache.context.sendBroadcast, intent); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + } + return TRUE; +} + +static gboolean on_message(Connector1 *connector, GDBusMethodInvocation *invocation, gpointer user_data) +{ + GVariant *parameters = g_dbus_method_invocation_get_parameters(invocation); + const char *token; + g_variant_get_child(parameters, 0, "s", &token); + gsize size; + const int8_t *message = g_variant_get_fixed_array(g_variant_get_child_value(parameters, 1), &size, 1); + connector1_complete_message(connector, invocation); + + JNIEnv *env = get_jni_env(); + jobject intent = (*env)->NewObject(env, handle_cache.intent.class, handle_cache.intent.constructor); + _SET_OBJ_FIELD(intent, "action", "Ljava/lang/String;", _JSTRING("org.unifiedpush.android.connector.MESSAGE")); + (*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraCharSequence, _JSTRING("token"), _JSTRING(token)); + jbyteArray bytesMessage = (*env)->NewByteArray(env, size); + (*env)->SetByteArrayRegion(env, bytesMessage, 0, size, message); + (*env)->CallObjectMethod(env, intent, handle_cache.intent.putExtraByteArray, _JSTRING("bytesMessage"), bytesMessage); + + jobject context = _GET_STATIC_OBJ_FIELD(handle_cache.context.class, "this_application", "Landroid/app/Application;"); + (*env)->CallVoidMethod(env, context, handle_cache.context.sendBroadcast, intent); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + } + return TRUE; +} + +JNIEXPORT void JNICALL Java_android_content_Context_nativeExportUnifiedPush(JNIEnv *env, jclass this, jstring application_jstr) +{ + const char *application = (*env)->GetStringUTFChars(env, application_jstr, NULL); + + Connector1 *connector1 = connector1_skeleton_new(); + g_signal_connect(connector1, "handle-new-endpoint", G_CALLBACK(on_new_endpoint), NULL); + g_signal_connect(connector1, "handle-message", G_CALLBACK(on_message), NULL); + g_bus_own_name(G_BUS_TYPE_SESSION, application, G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, NULL, NULL, connector1, NULL); + (*env)->ReleaseStringUTFChars(env, application_jstr, application); +} + +JNIEXPORT void JNICALL Java_android_content_Context_nativeRegisterUnifiedPush(JNIEnv *env, jclass this, jstring token_jstr, jstring application_jstr) +{ + const char *token = (*env)->GetStringUTFChars(env, token_jstr, NULL); + const char *application = (*env)->GetStringUTFChars(env, application_jstr, NULL); + + GDBusConnection *connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); + Distributor1 *distributor1 = distributor1_proxy_new_sync(connection, 0, "org.unifiedpush.Distributor.kde", "/org/unifiedpush/Distributor", NULL, NULL); + GError *error = NULL; + distributor1_call_register(distributor1, application, token, "", NULL, NULL, &error); + if (error) { + printf("nativeRegisterUnifiedPush: error=%s\n", error->message); + g_error_free(error); + } + g_object_unref(distributor1); + g_object_unref(connection); + + (*env)->ReleaseStringUTFChars(env, token_jstr, token); + (*env)->ReleaseStringUTFChars(env, application_jstr, application); +} diff --git a/src/api-impl-jni/content/org.unifiedpush.Connector1.xml b/src/api-impl-jni/content/org.unifiedpush.Connector1.xml new file mode 100644 index 00000000..6609ca78 --- /dev/null +++ b/src/api-impl-jni/content/org.unifiedpush.Connector1.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/api-impl-jni/content/org.unifiedpush.Distributor1.xml b/src/api-impl-jni/content/org.unifiedpush.Distributor1.xml new file mode 100644 index 00000000..d983fde5 --- /dev/null +++ b/src/api-impl-jni/content/org.unifiedpush.Distributor1.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + diff --git a/src/api-impl-jni/generated_headers/android_content_Context.h b/src/api-impl-jni/generated_headers/android_content_Context.h index b3609de5..bef33831 100644 --- a/src/api-impl-jni/generated_headers/android_content_Context.h +++ b/src/api-impl-jni/generated_headers/android_content_Context.h @@ -33,6 +33,22 @@ JNIEXPORT void JNICALL Java_android_content_Context_native_1updateConfig JNIEXPORT void JNICALL Java_android_content_Context_nativeOpenFile (JNIEnv *, jclass, jint); +/* + * Class: android_content_Context + * Method: nativeExportUnifiedPush + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_content_Context_nativeExportUnifiedPush + (JNIEnv *, jclass, jstring); + +/* + * Class: android_content_Context + * Method: nativeRegisterUnifiedPush + * Signature: (Ljava/lang/String;Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_android_content_Context_nativeRegisterUnifiedPush + (JNIEnv *, jclass, jstring, jstring); + #ifdef __cplusplus } #endif diff --git a/src/api-impl-jni/util.c b/src/api-impl-jni/util.c index 523ff2db..c950ee8b 100644 --- a/src/api-impl-jni/util.c +++ b/src/api-impl-jni/util.c @@ -156,6 +156,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.context.get_package_name = _METHOD(handle_cache.context.class, "getPackageName", "()Ljava/lang/String;"); if((*env)->ExceptionCheck(env)) (*env)->ExceptionDescribe(env); + handle_cache.context.sendBroadcast = _METHOD(handle_cache.context.class, "sendBroadcast", "(Landroid/content/Intent;)V"); handle_cache.application.class = _REF((*env)->FindClass(env, "android/app/Application")); handle_cache.application.get_app_icon_path = _METHOD(handle_cache.application.class, "get_app_icon_path", "()Ljava/lang/String;"); @@ -174,6 +175,7 @@ void set_up_handle_cache(JNIEnv *env) handle_cache.intent.class = _REF((*env)->FindClass(env, "android/content/Intent")); handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "", "()V"); handle_cache.intent.putExtraCharSequence = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;"); + handle_cache.intent.putExtraByteArray = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;[B)Landroid/content/Intent;"); handle_cache.instrumentation.class = _REF((*env)->FindClass(env, "android/app/Instrumentation")); diff --git a/src/api-impl-jni/util.h b/src/api-impl-jni/util.h index 5d343344..bd9a5365 100644 --- a/src/api-impl-jni/util.h +++ b/src/api-impl-jni/util.h @@ -100,6 +100,7 @@ struct handle_cache { struct { jclass class; jmethodID get_package_name; + jmethodID sendBroadcast; } context; struct { jclass class; @@ -123,6 +124,7 @@ struct handle_cache { jclass class; jmethodID constructor; jmethodID putExtraCharSequence; + jmethodID putExtraByteArray; } intent; struct { jclass class; diff --git a/src/api-impl/android/app/Notification.java b/src/api-impl/android/app/Notification.java index 2a527344..030d9871 100644 --- a/src/api-impl/android/app/Notification.java +++ b/src/api-impl/android/app/Notification.java @@ -188,6 +188,10 @@ public class Notification implements Parcelable { public Builder setSound(Uri sound) {return this;} + public Builder setSmallIcon(int icon) {return this;} + + public Builder setTicker(CharSequence tickerText) {return this;} + public Notification build() { return notification; } diff --git a/src/api-impl/android/content/Context.java b/src/api-impl/android/content/Context.java index de6484c1..93f04601 100644 --- a/src/api-impl/android/content/Context.java +++ b/src/api-impl/android/content/Context.java @@ -133,11 +133,22 @@ public class Context extends Object { Security.addProvider(provider); r.applyPackageQuirks(application_info.packageName); + + for (PackageParser.Activity receiver : pkg.receivers) { + for (PackageParser.ActivityIntentInfo intent : receiver.intents) { + if (intent.matchAction("org.unifiedpush.android.connector.MESSAGE")) { + nativeExportUnifiedPush(application_info.packageName); + break; + } + } + } } private static native String native_get_apk_path(); protected static native void native_updateConfig(Configuration config); private static native void nativeOpenFile(int fd); + private static native void nativeExportUnifiedPush(String packageName); + private static native void nativeRegisterUnifiedPush(String token, String application); static Application createApplication(long native_window) throws Exception { Application application; @@ -502,6 +513,16 @@ public class Context extends Object { public void unregisterComponentCallbacks(ComponentCallbacks callbacks) {} public boolean bindService(final Intent intent, final ServiceConnection serviceConnection, int dummy3) { + if (intent.getComponent() == null) { + for (PackageParser.Service s : pkg.services) { + for (PackageParser.IntentInfo ii : s.intents) { + if (ii.matchAction(intent.getAction())) { + intent.setComponent(new ComponentName(pkg.packageName, s.className)); + break; + } + } + } + } if (intent.getComponent() == null) { Slog.w(TAG, "Context.bindService: intent.getComponent() is null"); return false; @@ -629,11 +650,27 @@ public class Context extends Object { } public void sendBroadcast(Intent intent) { + if ("org.unifiedpush.android.distributor.REGISTER".equals(intent.getAction())) { + nativeRegisterUnifiedPush(intent.getStringExtra("token"), intent.getStringExtra("application")); + } for (IntentFilter filter : receiverMap.keySet()) { if (filter.matchAction(intent.getAction())) { receiverMap.get(filter).onReceive(this, intent); } } + for (PackageParser.Activity receiver : pkg.receivers) { + for (PackageParser.IntentInfo intentInfo : receiver.intents) { + if (intentInfo.matchAction(intent.getAction())) { + try { + Class cls = Class.forName(receiver.className).asSubclass(BroadcastReceiver.class); + BroadcastReceiver receiverInstance = cls.newInstance(); + receiverInstance.onReceive(this, intent); + } catch (ReflectiveOperationException e) { + e.printStackTrace(); + } + } + } + } } public boolean stopService(Intent intent) throws ClassNotFoundException { diff --git a/src/api-impl/android/content/pm/PackageManager.java b/src/api-impl/android/content/pm/PackageManager.java index c90cc541..80575d46 100644 --- a/src/api-impl/android/content/pm/PackageManager.java +++ b/src/api-impl/android/content/pm/PackageManager.java @@ -2318,6 +2318,11 @@ public class PackageManager { */ public List queryBroadcastReceivers(Intent intent, int flags) { + if ("org.unifiedpush.android.distributor.REGISTER".equals(intent.getAction())) { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo.exported = true; + return Arrays.asList(ri); + } return new ArrayList(); } diff --git a/src/api-impl/android/content/pm/ResolveInfo.java b/src/api-impl/android/content/pm/ResolveInfo.java index a7def7a6..2d13224d 100644 --- a/src/api-impl/android/content/pm/ResolveInfo.java +++ b/src/api-impl/android/content/pm/ResolveInfo.java @@ -6,6 +6,7 @@ public class ResolveInfo { public ActivityInfo activityInfo = new ActivityInfo(); public ServiceInfo serviceInfo = new ServiceInfo(); public IntentFilter filter = new IntentFilter(); + public int priority = -500; public static class DisplayNameComparator { diff --git a/src/api-impl/android/os/Binder.java b/src/api-impl/android/os/Binder.java index 2450a29a..db660d22 100644 --- a/src/api-impl/android/os/Binder.java +++ b/src/api-impl/android/os/Binder.java @@ -1,5 +1,7 @@ package android.os; +import android.content.Context; + public class Binder implements IBinder { public void attachInterface(IInterface owner, String descriptor) {} @@ -16,7 +18,7 @@ public class Binder implements IBinder { @Override public boolean transact(int code, Parcel data, Parcel reply, int flags) { return false; } - public static int getCallingUid() { return 0; } + public static int getCallingUid() { return Context.this_application.getApplicationInfo().uid; } public static int getCallingPid() { return 0; } }