From e3a121121baf0ea810a8616f9da22667b3127297 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 26 Jun 2023 16:08:57 +0200 Subject: [PATCH] UP: add custom extensions for app<->distributor interaction On registration the app can pass in a 'Messenger' to get a direct response instead of having to somehow wait for the broadcast receiver to fire. The app name can be passed as a pending intent which allows the distributor to validate the sender. --- src/main/AndroidManifest.xml | 2 + .../services/UnifiedPushBroker.java | 73 +++++++++++++++---- .../services/UnifiedPushDistributor.java | 23 +++++- .../services/XmppConnectionService.java | 21 +++++- 4 files changed, 99 insertions(+), 20 deletions(-) diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 806232a22..9b92438eb 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -114,6 +114,8 @@ + + diff --git a/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java b/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java index 7d2d90dd5..f27bf7fc5 100644 --- a/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java +++ b/src/main/java/eu/siacs/conversations/services/UnifiedPushBroker.java @@ -4,6 +4,9 @@ import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; import com.google.common.base.Optional; @@ -62,7 +65,7 @@ public class UnifiedPushBroker { Log.d( Config.LOGTAG, account.getJid().asBareJid() + ": trigger endpoint renewal on bind"); - renewUnifiedEndpoint(transportOptional.get()); + renewUnifiedEndpoint(transportOptional.get(), null); } } } @@ -74,11 +77,15 @@ public class UnifiedPushBroker { } public Optional renewUnifiedPushEndpoints() { + return renewUnifiedPushEndpoints(null); + } + + public Optional renewUnifiedPushEndpoints(final PushTargetMessenger pushTargetMessenger) { final Optional transportOptional = getTransport(); if (transportOptional.isPresent()) { final Transport transport = transportOptional.get(); if (transport.account.isEnabled()) { - renewUnifiedEndpoint(transportOptional.get()); + renewUnifiedEndpoint(transportOptional.get(), pushTargetMessenger); } else { Log.d(Config.LOGTAG, "skipping UnifiedPush endpoint renewal. Account is disabled"); } @@ -88,7 +95,7 @@ public class UnifiedPushBroker { return transportOptional; } - private void renewUnifiedEndpoint(final Transport transport) { + private void renewUnifiedEndpoint(final Transport transport, final PushTargetMessenger pushTargetMessenger) { final Account account = transport.account; final UnifiedPushDatabase unifiedPushDatabase = UnifiedPushDatabase.getInstance(service); final List renewals = @@ -114,16 +121,23 @@ public class UnifiedPushBroker { final Element register = registration.addChild("register", Namespace.UNIFIED_PUSH); register.setAttribute("application", hashedApplication); register.setAttribute("instance", hashedInstance); + final Messenger messenger; + if (pushTargetMessenger != null && renewal.equals(pushTargetMessenger.pushTarget)) { + messenger = pushTargetMessenger.messenger; + } else { + messenger = null; + } this.service.sendIqPacket( account, registration, - (a, response) -> processRegistration(transport, renewal, response)); + (a, response) -> processRegistration(transport, renewal, messenger, response)); } } private void processRegistration( final Transport transport, final UnifiedPushDatabase.PushTarget renewal, + final Messenger messenger, final IqPacket response) { if (response.getType() == IqPacket.TYPE.RESULT) { final Element registered = response.findChild("registered", Namespace.UNIFIED_PUSH); @@ -142,7 +156,7 @@ public class UnifiedPushBroker { Log.d(Config.LOGTAG, "could not parse expiration", e); return; } - renewUnifiedPushEndpoint(transport, renewal, endpoint, expiration); + renewUnifiedPushEndpoint(transport, renewal, messenger, endpoint, expiration); } else { Log.d(Config.LOGTAG, "could not register UP endpoint " + response.getErrorCondition()); } @@ -151,6 +165,7 @@ public class UnifiedPushBroker { private void renewUnifiedPushEndpoint( final Transport transport, final UnifiedPushDatabase.PushTarget renewal, + final Messenger messenger, final String endpoint, final long expiration) { Log.d(Config.LOGTAG, "registered endpoint " + endpoint + " expiration=" + expiration); @@ -171,9 +186,24 @@ public class UnifiedPushBroker { + renewal.instance + " was updated to " + endpoint); - broadcastEndpoint( - renewal.instance, - new UnifiedPushDatabase.ApplicationEndpoint(renewal.application, endpoint)); + final UnifiedPushDatabase.ApplicationEndpoint applicationEndpoint = new UnifiedPushDatabase.ApplicationEndpoint(renewal.application, endpoint); + sendEndpoint(messenger, renewal.instance, applicationEndpoint); + } + } + + private void sendEndpoint(final Messenger messenger, String instance, final UnifiedPushDatabase.ApplicationEndpoint applicationEndpoint) { + if (messenger != null) { + Log.d(Config.LOGTAG,"using messenger instead of broadcast to communicate endpoint to "+applicationEndpoint.application); + final Message message = new Message(); + message.obj = endpointIntent(instance, applicationEndpoint); + try { + messenger.send(message); + } catch (final RemoteException e) { + Log.d(Config.LOGTAG,"messenger failed. falling back to broadcast"); + broadcastEndpoint(instance, applicationEndpoint); + } + } else { + broadcastEndpoint(instance, applicationEndpoint); } } @@ -302,14 +332,19 @@ public class UnifiedPushBroker { private void broadcastEndpoint( final String instance, final UnifiedPushDatabase.ApplicationEndpoint endpoint) { Log.d(Config.LOGTAG, "broadcasting endpoint to " + endpoint.application); - final Intent updateIntent = new Intent(UnifiedPushDistributor.ACTION_NEW_ENDPOINT); - updateIntent.setPackage(endpoint.application); - updateIntent.putExtra("token", instance); - updateIntent.putExtra("endpoint", endpoint.endpoint); + final Intent updateIntent = endpointIntent(instance, endpoint); service.sendBroadcast(updateIntent); } - public void rebroadcastEndpoint(final String instance, final Transport transport) { + private static Intent endpointIntent(final String instance, final UnifiedPushDatabase.ApplicationEndpoint endpoint) { + final Intent intent = new Intent(UnifiedPushDistributor.ACTION_NEW_ENDPOINT); + intent.setPackage(endpoint.application); + intent.putExtra("token", instance); + intent.putExtra("endpoint", endpoint.endpoint); + return intent; + } + + public void rebroadcastEndpoint(final Messenger messenger, final String instance, final Transport transport) { final UnifiedPushDatabase unifiedPushDatabase = UnifiedPushDatabase.getInstance(service); final UnifiedPushDatabase.ApplicationEndpoint endpoint = unifiedPushDatabase.getEndpoint( @@ -317,7 +352,7 @@ public class UnifiedPushBroker { transport.transport.toEscapedString(), instance); if (endpoint != null) { - broadcastEndpoint(instance, endpoint); + sendEndpoint(messenger, instance, endpoint); } } @@ -330,4 +365,14 @@ public class UnifiedPushBroker { this.transport = transport; } } + + public static class PushTargetMessenger { + private final UnifiedPushDatabase.PushTarget pushTarget; + private final Messenger messenger; + + public PushTargetMessenger(UnifiedPushDatabase.PushTarget pushTarget, Messenger messenger) { + this.pushTarget = pushTarget; + this.messenger = messenger; + } + } } diff --git a/src/main/java/eu/siacs/conversations/services/UnifiedPushDistributor.java b/src/main/java/eu/siacs/conversations/services/UnifiedPushDistributor.java index 64c16dbcd..c5402dec6 100644 --- a/src/main/java/eu/siacs/conversations/services/UnifiedPushDistributor.java +++ b/src/main/java/eu/siacs/conversations/services/UnifiedPushDistributor.java @@ -1,10 +1,13 @@ package eu.siacs.conversations.services; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Messenger; +import android.os.Parcelable; import android.util.Log; import com.google.common.base.Charsets; @@ -46,12 +49,21 @@ public class UnifiedPushDistributor extends BroadcastReceiver { return; } final String action = intent.getAction(); - final String application = intent.getStringExtra("application"); + final String application; + final Parcelable appByPendingIntent = intent.getParcelableExtra("app"); + if (appByPendingIntent instanceof PendingIntent) { + final PendingIntent pendingIntent = (PendingIntent) appByPendingIntent; + application = pendingIntent.getIntentSender().getCreatorPackage(); + Log.d(Config.LOGTAG,"received application name via pending intent "+ application); + } else { + application = intent.getStringExtra("application"); + } + final Parcelable messenger = intent.getParcelableExtra("messenger"); final String instance = intent.getStringExtra("token"); final List features = intent.getStringArrayListExtra("features"); switch (Strings.nullToEmpty(action)) { case ACTION_REGISTER: - register(context, application, instance, features); + register(context, application, instance, features, messenger); break; case ACTION_UNREGISTER: unregister(context, instance); @@ -69,7 +81,8 @@ public class UnifiedPushDistributor extends BroadcastReceiver { final Context context, final String application, final String instance, - final Collection features) { + final Collection features, + final Parcelable messenger) { if (Strings.isNullOrEmpty(application) || Strings.isNullOrEmpty(instance)) { Log.w(Config.LOGTAG, "ignoring invalid UnifiedPush registration"); return; @@ -92,6 +105,10 @@ public class UnifiedPushDistributor extends BroadcastReceiver { final Intent serviceIntent = new Intent(context, XmppConnectionService.class); serviceIntent.setAction(XmppConnectionService.ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS); serviceIntent.putExtra("instance", instance); + serviceIntent.putExtra("application", application); + if (messenger instanceof Messenger) { + serviceIntent.putExtra("messenger", messenger); + } Compatibility.startService(context, serviceIntent); } else { Log.d(Config.LOGTAG, "not successful. sending error message back to application"); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ebbbef4c4..dcb613399 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -32,6 +32,8 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; +import android.os.Messenger; +import android.os.Parcelable; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemClock; @@ -815,9 +817,18 @@ public class XmppConnectionService extends Service { break; case ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS: final String instance = intent.getStringExtra("instance"); - final Optional transport = renewUnifiedPushEndpoints(); + final String application = intent.getStringExtra("application"); + final Messenger messenger = intent.getParcelableExtra("messenger"); + final UnifiedPushBroker.PushTargetMessenger pushTargetMessenger; + if (messenger != null && application != null && instance != null) { + pushTargetMessenger = new UnifiedPushBroker.PushTargetMessenger(new UnifiedPushDatabase.PushTarget(application, instance),messenger); + Log.d(Config.LOGTAG,"found push target messenger"); + } else { + pushTargetMessenger = null; + } + final Optional transport = renewUnifiedPushEndpoints(pushTargetMessenger); if (instance != null && transport.isPresent()) { - unifiedPushBroker.rebroadcastEndpoint(instance, transport.get()); + unifiedPushBroker.rebroadcastEndpoint(messenger, instance, transport.get()); } break; case ACTION_IDLE_PING: @@ -2363,8 +2374,12 @@ public class XmppConnectionService extends Service { return this.unifiedPushBroker.reconfigurePushDistributor(); } + private Optional renewUnifiedPushEndpoints(final UnifiedPushBroker.PushTargetMessenger pushTargetMessenger) { + return this.unifiedPushBroker.renewUnifiedPushEndpoints(pushTargetMessenger); + } + public Optional renewUnifiedPushEndpoints() { - return this.unifiedPushBroker.renewUnifiedPushEndpoints(); + return this.unifiedPushBroker.renewUnifiedPushEndpoints(null); } private void provisionAccount(final String address, final String password) {