map Android UnifiedPush to DBus UnifiedPush

This commit is contained in:
Julian Winkler 2025-04-21 07:31:06 +02:00
parent ec45faaf87
commit e652089252
12 changed files with 211 additions and 1 deletions

View file

@ -47,6 +47,12 @@ mpris = gnome.gdbus_codegen('mpris-dbus',
portal_openuri = gnome.gdbus_codegen('portal-openuri', portal_openuri = gnome.gdbus_codegen('portal-openuri',
'src/api-impl-jni/content/org.freedesktop.portal.OpenURI.xml', 'src/api-impl-jni/content/org.freedesktop.portal.OpenURI.xml',
interface_prefix: 'org.freedesktop.portal') 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_deps = []
extra_jni_srcs = [] extra_jni_srcs = []
@ -157,6 +163,8 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
'src/api-impl-jni/widgets/android_widget_TextView.c', 'src/api-impl-jni/widgets/android_widget_TextView.c',
mpris, mpris,
portal_openuri, portal_openuri,
unifiedpush_distributor,
unifiedpush_connector,
wl_proto_headers, wl_proto_headers,
wl_proto_sources, wl_proto_sources,
extra_jni_srcs, extra_jni_srcs,

View file

@ -8,6 +8,8 @@
#endif #endif
#include "portal-openuri.h" #include "portal-openuri.h"
#include "unifiedpush-distributor.h"
#include "unifiedpush-connector.h"
#include "../defines.h" #include "../defines.h"
#include "../util.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(openuri);
g_object_unref(connection); 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);
}

View file

@ -0,0 +1,24 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!--
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
-->
<node>
<interface name="org.unifiedpush.Connector1">
<method name="Message">
<arg name="token" type="s" direction="in"/>
<arg name="message" type="ay" direction="in"/>
<arg name="messageIdentifier" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="NewEndpoint">
<arg name="token" type="s" direction="in"/>
<arg name="endpoint" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
<method name="Unregistered">
<arg name="token" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>

View file

@ -0,0 +1,20 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!--
SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
-->
<node>
<interface name="org.unifiedpush.Distributor1">
<method name="Register">
<arg name="serviceName" type="s" direction="in"/>
<arg name="token" type="s" direction="in"/>
<arg name="description" type="s" direction="in"/>
<arg name="registrationResult" type="s" direction="out"/>
<arg name="registrationResultReason" type="s" direction="out"/>
</method>
<method name="Unregister">
<arg name="token" type="s" direction="in"/>
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
</method>
</interface>
</node>

View file

@ -33,6 +33,22 @@ JNIEXPORT void JNICALL Java_android_content_Context_native_1updateConfig
JNIEXPORT void JNICALL Java_android_content_Context_nativeOpenFile JNIEXPORT void JNICALL Java_android_content_Context_nativeOpenFile
(JNIEnv *, jclass, jint); (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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -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;"); handle_cache.context.get_package_name = _METHOD(handle_cache.context.class, "getPackageName", "()Ljava/lang/String;");
if((*env)->ExceptionCheck(env)) if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(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.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;"); 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.class = _REF((*env)->FindClass(env, "android/content/Intent"));
handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "<init>", "()V"); handle_cache.intent.constructor = _METHOD(handle_cache.intent.class, "<init>", "()V");
handle_cache.intent.putExtraCharSequence = _METHOD(handle_cache.intent.class, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;"); 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")); handle_cache.instrumentation.class = _REF((*env)->FindClass(env, "android/app/Instrumentation"));

View file

@ -100,6 +100,7 @@ struct handle_cache {
struct { struct {
jclass class; jclass class;
jmethodID get_package_name; jmethodID get_package_name;
jmethodID sendBroadcast;
} context; } context;
struct { struct {
jclass class; jclass class;
@ -123,6 +124,7 @@ struct handle_cache {
jclass class; jclass class;
jmethodID constructor; jmethodID constructor;
jmethodID putExtraCharSequence; jmethodID putExtraCharSequence;
jmethodID putExtraByteArray;
} intent; } intent;
struct { struct {
jclass class; jclass class;

View file

@ -188,6 +188,10 @@ public class Notification implements Parcelable {
public Builder setSound(Uri sound) {return this;} public Builder setSound(Uri sound) {return this;}
public Builder setSmallIcon(int icon) {return this;}
public Builder setTicker(CharSequence tickerText) {return this;}
public Notification build() { public Notification build() {
return notification; return notification;
} }

View file

@ -133,11 +133,22 @@ public class Context extends Object {
Security.addProvider(provider); Security.addProvider(provider);
r.applyPackageQuirks(application_info.packageName); 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(); private static native String native_get_apk_path();
protected static native void native_updateConfig(Configuration config); protected static native void native_updateConfig(Configuration config);
private static native void nativeOpenFile(int fd); 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 { static Application createApplication(long native_window) throws Exception {
Application application; Application application;
@ -502,6 +513,16 @@ public class Context extends Object {
public void unregisterComponentCallbacks(ComponentCallbacks callbacks) {} public void unregisterComponentCallbacks(ComponentCallbacks callbacks) {}
public boolean bindService(final Intent intent, final ServiceConnection serviceConnection, int dummy3) { 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) { if (intent.getComponent() == null) {
Slog.w(TAG, "Context.bindService: intent.getComponent() is null"); Slog.w(TAG, "Context.bindService: intent.getComponent() is null");
return false; return false;
@ -629,11 +650,27 @@ public class Context extends Object {
} }
public void sendBroadcast(Intent intent) { 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()) { for (IntentFilter filter : receiverMap.keySet()) {
if (filter.matchAction(intent.getAction())) { if (filter.matchAction(intent.getAction())) {
receiverMap.get(filter).onReceive(this, intent); 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<? extends BroadcastReceiver> 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 { public boolean stopService(Intent intent) throws ClassNotFoundException {

View file

@ -2318,6 +2318,11 @@ public class PackageManager {
*/ */
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, public List<ResolveInfo> queryBroadcastReceivers(Intent intent,
int flags) { 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<ResolveInfo>(); return new ArrayList<ResolveInfo>();
} }

View file

@ -6,6 +6,7 @@ public class ResolveInfo {
public ActivityInfo activityInfo = new ActivityInfo(); public ActivityInfo activityInfo = new ActivityInfo();
public ServiceInfo serviceInfo = new ServiceInfo(); public ServiceInfo serviceInfo = new ServiceInfo();
public IntentFilter filter = new IntentFilter(); public IntentFilter filter = new IntentFilter();
public int priority = -500;
public static class DisplayNameComparator { public static class DisplayNameComparator {

View file

@ -1,5 +1,7 @@
package android.os; package android.os;
import android.content.Context;
public class Binder implements IBinder { public class Binder implements IBinder {
public void attachInterface(IInterface owner, String descriptor) {} public void attachInterface(IInterface owner, String descriptor) {}
@ -16,7 +18,7 @@ public class Binder implements IBinder {
@Override @Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) { return false; } 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; } public static int getCallingPid() { return 0; }
} }