From 666e6db458e1ff70653585068f64c0a7f7e9b4ff Mon Sep 17 00:00:00 2001 From: MGasztold Date: Mon, 6 Jul 2020 22:41:15 +0200 Subject: [PATCH 1/5] add API to support foreground service Signed-off-by: MGasztold --- .../android/service/MqttAndroidClient.java | 40 ++++++++++++++++++- .../paho/android/service/MqttService.java | 14 +++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java index b3035261a..6aa0fa714 100755 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java @@ -16,12 +16,14 @@ */ package org.eclipse.paho.android.service; +import android.app.Notification; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.os.Build; import android.os.Bundle; import android.os.IBinder; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -103,6 +105,9 @@ public class MqttAndroidClient extends BroadcastReceiver implements IMqttAsyncCl private boolean traceEnabled = false; private volatile boolean receiverRegistered = false; private volatile boolean bindedService = false; + // notification for Foreground Service + private int foregroundServiceNotificationId = 1; + private Notification foregroundServiceNotification; /** * Constructor - create an MqttAndroidClient that can be used to communicate with an MQTT server on android @@ -321,7 +326,21 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA if (mqttService == null) { // First time - must bind to the service Intent serviceStartIntent = new Intent(); serviceStartIntent.setClassName(myContext, SERVICE_NAME); - Object service = myContext.startService(serviceStartIntent); + + Object service = null; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && foregroundServiceNotification != null) { + serviceStartIntent.putExtra( + MqttService.PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION, + foregroundServiceNotification); + serviceStartIntent.putExtra( + MqttService.PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION_ID, + foregroundServiceNotificationId); + myContext.startForegroundService(serviceStartIntent); + } else { + myContext.startService(serviceStartIntent); + } + if (service == null) { IMqttActionListener listener = token.getActionCallback(); if (listener != null) { @@ -1421,6 +1440,25 @@ private synchronized IMqttToken getMqttToken(Bundle data) { return tokenMap.get(Integer.parseInt(activityToken)); } + /** + * If foregroundServiceNotification is not null at the time of + * MqttService start it will run in foreground mode. This method has no effect if + * Build.VERSION.SDK_INT < Build.VERSION_CODES.O + * + * @param notification notification to be used when MqttService runs in foreground mode + */ + public void setForegroundServiceNotification(Notification notification) { + foregroundServiceNotification = notification; + } + + /** + * + * @param id The identifier for foreground service notification + */ + public void setForegroundServiceNotificationId(int id) { + foregroundServiceNotificationId = id; + } + /** * Sets the DisconnectedBufferOptions for this client * diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java index cf563d221..3e14d3844 100644 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java @@ -16,6 +16,7 @@ package org.eclipse.paho.android.service; import android.annotation.SuppressLint; +import android.app.Notification; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; @@ -226,6 +227,9 @@ public class MqttService extends Service implements MqttTraceHandler { // Identifier for Intents, log messages, etc.. static final String TAG = "MqttService"; + // names of the start service Intent extras for foreground service mode + static final String PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION_ID = "org.eclipse.paho.android.service.MqttService.FOREGROUND_SERVICE_NOTIFICATION_ID"; + static final String PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION = "org.eclipse.paho.android.service.MqttService.FOREGROUND_SERVICE_NOTIFICATION"; // somewhere to persist received messages until we're sure // that they've reached the application MessageStore messageStore; @@ -596,6 +600,16 @@ public int onStartCommand(final Intent intent, int flags, final int startId) { // process restarted registerBroadcastReceivers(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Notification foregroundServiceNotification + = (Notification) (intent.getParcelableExtra(PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION)); + if (foregroundServiceNotification != null) + startForeground( + intent.getIntExtra(PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION_ID, 1), + foregroundServiceNotification + ); + } + return START_STICKY; } From 2082ce5567e5511545a065f2dcf0e667bed18f6a Mon Sep 17 00:00:00 2001 From: MGasztold Date: Tue, 7 Jul 2020 16:23:42 +0200 Subject: [PATCH 2/5] Improve comments Signed-off-by: MGasztold --- .../paho/android/service/MqttAndroidClient.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java index 6aa0fa714..d72843466 100755 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java @@ -1441,9 +1441,12 @@ private synchronized IMqttToken getMqttToken(Bundle data) { } /** - * If foregroundServiceNotification is not null at the time of - * MqttService start it will run in foreground mode. This method has no effect if - * Build.VERSION.SDK_INT < Build.VERSION_CODES.O + * Sets foregroundServiceNotification object. If it is not null at the time of + * MqttService start then the service will run in foreground mode which is + * mandatory to keep MQTT service operation when app is + * in the background on Android version >=8. + * + * This method has no effect if Build.VERSION.SDK_INT < Build.VERSION_CODES.O * * @param notification notification to be used when MqttService runs in foreground mode */ @@ -1452,6 +1455,8 @@ public void setForegroundServiceNotification(Notification notification) { } /** + * Sets ID of the foreground service notification. + * If this method is not used then the default ID 1 will be used. * * @param id The identifier for foreground service notification */ From d57f4ee461c70a1157ae5b237552fe794753b979 Mon Sep 17 00:00:00 2001 From: MGasztold Date: Thu, 16 Jul 2020 13:41:18 +0200 Subject: [PATCH 3/5] check if intent != null Signed-off-by: MGasztold --- .../main/java/org/eclipse/paho/android/service/MqttService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java index 3e14d3844..e04605c53 100644 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttService.java @@ -600,7 +600,7 @@ public int onStartCommand(final Intent intent, int flags, final int startId) { // process restarted registerBroadcastReceivers(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && intent != null) { Notification foregroundServiceNotification = (Notification) (intent.getParcelableExtra(PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION)); if (foregroundServiceNotification != null) From 1426cb2da2b1643e4ab4f2a7e62d0be41cd442d8 Mon Sep 17 00:00:00 2001 From: MGasztold Date: Tue, 25 Aug 2020 21:56:07 +0200 Subject: [PATCH 4/5] fix service variable in connect method not having value assigned Signed-off-by: MGasztold --- .../org/eclipse/paho/android/service/MqttAndroidClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java index d72843466..9f60bbb5e 100755 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java @@ -327,7 +327,7 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA Intent serviceStartIntent = new Intent(); serviceStartIntent.setClassName(myContext, SERVICE_NAME); - Object service = null; + Object service; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && foregroundServiceNotification != null) { serviceStartIntent.putExtra( @@ -336,9 +336,9 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA serviceStartIntent.putExtra( MqttService.PAHO_MQTT_FOREGROUND_SERVICE_NOTIFICATION_ID, foregroundServiceNotificationId); - myContext.startForegroundService(serviceStartIntent); + service = myContext.startForegroundService(serviceStartIntent); } else { - myContext.startService(serviceStartIntent); + service = myContext.startService(serviceStartIntent); } if (service == null) { From 902fb6d09b65e5a8723fee4e635043f154ab3de3 Mon Sep 17 00:00:00 2001 From: MGasztold Date: Mon, 2 Nov 2020 22:35:39 +0100 Subject: [PATCH 5/5] catch IllegalStateException exception when service cannot be started because app is in background --- .../paho/android/service/MqttAndroidClient.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java index 9f60bbb5e..d42ec29d3 100755 --- a/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java +++ b/org.eclipse.paho.android.service/src/main/java/org/eclipse/paho/android/service/MqttAndroidClient.java @@ -327,7 +327,7 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA Intent serviceStartIntent = new Intent(); serviceStartIntent.setClassName(myContext, SERVICE_NAME); - Object service; + Object service = null; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && foregroundServiceNotification != null) { serviceStartIntent.putExtra( @@ -338,7 +338,14 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA foregroundServiceNotificationId); service = myContext.startForegroundService(serviceStartIntent); } else { - service = myContext.startService(serviceStartIntent); + try { + service = myContext.startService(serviceStartIntent); + } catch(IllegalStateException ex) { + IMqttActionListener listener = token.getActionCallback(); + if (listener != null) { + listener.onFailure(token, ex); + } + } } if (service == null) {