├── .gitignore
├── MQTTLibrary
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── itfitness
│ │ └── mqttlibrary
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── itfitness
│ │ └── mqttlibrary
│ │ ├── AlarmPingSender.java
│ │ ├── DatabaseMessageStore.java
│ │ ├── MessageStore.java
│ │ ├── MqttAndroidClient.java
│ │ ├── MqttConnection.java
│ │ ├── MqttDeliveryTokenAndroid.java
│ │ ├── MqttService.java
│ │ ├── MqttServiceBinder.java
│ │ ├── MqttServiceConstants.java
│ │ ├── MqttTokenAndroid.java
│ │ ├── MqttTraceHandler.java
│ │ ├── ParcelableMqttMessage.java
│ │ └── Status.java
│ └── test
│ └── java
│ └── com
│ └── itfitness
│ └── mqttlibrary
│ └── ExampleUnitTest.kt
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── itfitness
│ │ └── mqttandroid
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── itfitness
│ │ │ └── mqttandroid
│ │ │ ├── MQTTHelper.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── data
│ │ │ ├── Qos.kt
│ │ │ └── Topic.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── itfitness
│ └── mqttandroid
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/MQTTLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/MQTTLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdk 31
8 |
9 | defaultConfig {
10 | minSdk 21
11 | targetSdk 31
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles "consumer-rules.pro"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility JavaVersion.VERSION_1_8
27 | targetCompatibility JavaVersion.VERSION_1_8
28 | }
29 | kotlinOptions {
30 | jvmTarget = '1.8'
31 | }
32 | }
33 |
34 | dependencies {
35 |
36 | implementation 'androidx.core:core-ktx:1.7.0'
37 | implementation 'androidx.appcompat:appcompat:1.4.1'
38 | implementation 'com.google.android.material:material:1.5.0'
39 | testImplementation 'junit:junit:4.+'
40 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
42 | api 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
43 | }
--------------------------------------------------------------------------------
/MQTTLibrary/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itfitness/MQTTAndroid/268eba5feca208d3d91ada4479182f288ad3cc14/MQTTLibrary/consumer-rules.pro
--------------------------------------------------------------------------------
/MQTTLibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/MQTTLibrary/src/androidTest/java/com/itfitness/mqttlibrary/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.itfitness.mqttlibrary
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.itfitness.mqttlibrary.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/MQTTLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
This class implements the {@link MqttPingSender} pinger interface 40 | * allowing applications to send ping packet to server every keep alive interval. 41 | *
42 | * 43 | * @see MqttPingSender 44 | */ 45 | class AlarmPingSender implements MqttPingSender { 46 | // Identifier for Intents, log messages, etc.. 47 | private static final String TAG = "AlarmPingSender"; 48 | 49 | // TODO: Add log. 50 | private ClientComms comms; 51 | private MqttService service; 52 | private BroadcastReceiver alarmReceiver; 53 | private AlarmPingSender that; 54 | private PendingIntent pendingIntent; 55 | private volatile boolean hasStarted = false; 56 | 57 | public AlarmPingSender(MqttService service) { 58 | if (service == null) { 59 | throw new IllegalArgumentException( 60 | "Neither service nor client can be null."); 61 | } 62 | this.service = service; 63 | that = this; 64 | } 65 | 66 | @Override 67 | public void init(ClientComms comms) { 68 | this.comms = comms; 69 | this.alarmReceiver = new AlarmReceiver(); 70 | } 71 | 72 | @RequiresApi(api = Build.VERSION_CODES.M) 73 | @Override 74 | public void start() { 75 | String action = MqttServiceConstants.PING_SENDER 76 | + comms.getClient().getClientId(); 77 | Log.d(TAG, "Register alarmreceiver to MqttService"+ action); 78 | service.registerReceiver(alarmReceiver, new IntentFilter(action)); 79 | 80 | pendingIntent = PendingIntent.getBroadcast(service, 0, new Intent( 81 | action), PendingIntent.FLAG_UPDATE_CURRENT|PendingIntent.FLAG_IMMUTABLE); 82 | 83 | schedule(comms.getKeepAlive()); 84 | hasStarted = true; 85 | } 86 | 87 | @Override 88 | public void stop() { 89 | 90 | Log.d(TAG, "Unregister alarmreceiver to MqttService"+comms.getClient().getClientId()); 91 | if(hasStarted){ 92 | if(pendingIntent != null){ 93 | // Cancel Alarm. 94 | AlarmManager alarmManager = (AlarmManager) service.getSystemService(Service.ALARM_SERVICE); 95 | alarmManager.cancel(pendingIntent); 96 | } 97 | 98 | hasStarted = false; 99 | try{ 100 | service.unregisterReceiver(alarmReceiver); 101 | }catch(IllegalArgumentException e){ 102 | //Ignore unregister errors. 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public void schedule(long delayInMilliseconds) { 109 | long nextAlarmInMilliseconds = System.currentTimeMillis() 110 | + delayInMilliseconds; 111 | Log.d(TAG, "Schedule next alarm at " + nextAlarmInMilliseconds); 112 | AlarmManager alarmManager = (AlarmManager) service 113 | .getSystemService(Service.ALARM_SERVICE); 114 | 115 | if(Build.VERSION.SDK_INT >= 23){ 116 | // In SDK 23 and above, dosing will prevent setExact, setExactAndAllowWhileIdle will force 117 | // the device to run this task whilst dosing. 118 | Log.d(TAG, "Alarm scheule using setExactAndAllowWhileIdle, next: " + delayInMilliseconds); 119 | alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds, 120 | pendingIntent); 121 | } else if (Build.VERSION.SDK_INT >= 19) { 122 | Log.d(TAG, "Alarm scheule using setExact, delay: " + delayInMilliseconds); 123 | alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds, 124 | pendingIntent); 125 | } else { 126 | alarmManager.set(AlarmManager.RTC_WAKEUP, nextAlarmInMilliseconds, 127 | pendingIntent); 128 | } 129 | } 130 | 131 | /* 132 | * This class sends PingReq packet to MQTT broker 133 | */ 134 | class AlarmReceiver extends BroadcastReceiver { 135 | private WakeLock wakelock; 136 | private final String wakeLockTag = MqttServiceConstants.PING_WAKELOCK 137 | + that.comms.getClient().getClientId(); 138 | 139 | @Override 140 | @SuppressLint("Wakelock") 141 | public void onReceive(Context context, Intent intent) { 142 | // According to the docs, "Alarm Manager holds a CPU wake lock as 143 | // long as the alarm receiver's onReceive() method is executing. 144 | // This guarantees that the phone will not sleep until you have 145 | // finished handling the broadcast.", but this class still get 146 | // a wake lock to wait for ping finished. 147 | 148 | Log.d(TAG, "Sending Ping at:" + System.currentTimeMillis()); 149 | 150 | PowerManager pm = (PowerManager) service 151 | .getSystemService(Service.POWER_SERVICE); 152 | wakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, wakeLockTag); 153 | wakelock.acquire(); 154 | 155 | // Assign new callback to token to execute code after PingResq 156 | // arrives. Get another wakelock even receiver already has one, 157 | // release it until ping response returns. 158 | IMqttToken token = comms.checkForActivity(new IMqttActionListener() { 159 | 160 | @Override 161 | public void onSuccess(IMqttToken asyncActionToken) { 162 | Log.d(TAG, "Success. Release lock(" + wakeLockTag + "):" 163 | + System.currentTimeMillis()); 164 | //Release wakelock when it is done. 165 | wakelock.release(); 166 | } 167 | 168 | @Override 169 | public void onFailure(IMqttToken asyncActionToken, 170 | Throwable exception) { 171 | Log.d(TAG, "Failure. Release lock(" + wakeLockTag + "):" 172 | + System.currentTimeMillis()); 173 | //Release wakelock when it is done. 174 | wakelock.release(); 175 | } 176 | }); 177 | 178 | 179 | if (token == null && wakelock.isHeld()) { 180 | wakelock.release(); 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/DatabaseMessageStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * James Sutton - Removing SQL Injection vunerability (bug 467378) 15 | */ 16 | package com.itfitness.mqttlibrary; 17 | 18 | import android.content.ContentValues; 19 | import android.content.Context; 20 | import android.database.Cursor; 21 | import android.database.SQLException; 22 | import android.database.sqlite.SQLiteDatabase; 23 | import android.database.sqlite.SQLiteOpenHelper; 24 | 25 | import org.eclipse.paho.client.mqttv3.MqttMessage; 26 | 27 | import java.util.Iterator; 28 | 29 | /** 30 | * Implementation of the {@link MessageStore} interface, using a SQLite database 31 | * 32 | */ 33 | class DatabaseMessageStore implements MessageStore { 34 | 35 | // TAG used for indentify trace data etc. 36 | private static final String TAG = "DatabaseMessageStore"; 37 | 38 | // One "private" database column name 39 | // The other database column names are defined in MqttServiceConstants 40 | private static final String MTIMESTAMP = "mtimestamp"; 41 | 42 | // the name of the table in the database to which we will save messages 43 | private static final String ARRIVED_MESSAGE_TABLE_NAME = "MqttArrivedMessageTable"; 44 | 45 | // the database 46 | private SQLiteDatabase db = null; 47 | 48 | // a SQLiteOpenHelper specific for this database 49 | private MQTTDatabaseHelper mqttDb = null; 50 | 51 | // a place to send trace data 52 | private MqttTraceHandler traceHandler = null; 53 | 54 | /** 55 | * We need a SQLiteOpenHelper to handle database creation and updating 56 | * 57 | */ 58 | private static class MQTTDatabaseHelper extends SQLiteOpenHelper { 59 | // TAG used for indentify trace data etc. 60 | private static final String TAG = "MQTTDatabaseHelper"; 61 | 62 | private static final String DATABASE_NAME = "mqttAndroidService.db"; 63 | 64 | // database version, used to recognise when we need to upgrade 65 | // (delete and recreate) 66 | private static final int DATABASE_VERSION = 1; 67 | 68 | // a place to send trace data 69 | private MqttTraceHandler traceHandler = null; 70 | 71 | /** 72 | * Constructor. 73 | * 74 | * @param traceHandler 75 | * @param context 76 | */ 77 | public MQTTDatabaseHelper(MqttTraceHandler traceHandler, Context context) { 78 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 79 | this.traceHandler = traceHandler; 80 | } 81 | 82 | /** 83 | * When the database is (re)created, create our table 84 | * 85 | * @param database 86 | */ 87 | @Override 88 | public void onCreate(SQLiteDatabase database) { 89 | String createArrivedTableStatement = "CREATE TABLE " 90 | + ARRIVED_MESSAGE_TABLE_NAME + "(" 91 | + MqttServiceConstants.MESSAGE_ID + " TEXT PRIMARY KEY, " 92 | + MqttServiceConstants.CLIENT_HANDLE + " TEXT, " 93 | + MqttServiceConstants.DESTINATION_NAME + " TEXT, " 94 | + MqttServiceConstants.PAYLOAD + " BLOB, " 95 | + MqttServiceConstants.QOS + " INTEGER, " 96 | + MqttServiceConstants.RETAINED + " TEXT, " 97 | + MqttServiceConstants.DUPLICATE + " TEXT, " + MTIMESTAMP 98 | + " INTEGER" + ");"; 99 | traceHandler.traceDebug(TAG, "onCreate {" 100 | + createArrivedTableStatement + "}"); 101 | try { 102 | database.execSQL(createArrivedTableStatement); 103 | traceHandler.traceDebug(TAG, "created the table"); 104 | } catch (SQLException e) { 105 | traceHandler.traceException(TAG, "onCreate", e); 106 | throw e; 107 | } 108 | } 109 | 110 | /** 111 | * To upgrade the database, drop and recreate our table 112 | * 113 | * @param db 114 | * the database 115 | * @param oldVersion 116 | * ignored 117 | * @param newVersion 118 | * ignored 119 | */ 120 | 121 | @Override 122 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 123 | traceHandler.traceDebug(TAG, "onUpgrade"); 124 | try { 125 | db.execSQL("DROP TABLE IF EXISTS " + ARRIVED_MESSAGE_TABLE_NAME); 126 | } catch (SQLException e) { 127 | traceHandler.traceException(TAG, "onUpgrade", e); 128 | throw e; 129 | } 130 | onCreate(db); 131 | traceHandler.traceDebug(TAG, "onUpgrade complete"); 132 | } 133 | } 134 | 135 | /** 136 | * Constructor - create a DatabaseMessageStore to store arrived MQTT message 137 | * 138 | * @param service 139 | * our parent MqttService 140 | * @param context 141 | * a context to use for android calls 142 | */ 143 | public DatabaseMessageStore(MqttService service, Context context) { 144 | this.traceHandler = service; 145 | 146 | // Open message database 147 | mqttDb = new MQTTDatabaseHelper(traceHandler, context); 148 | 149 | // Android documentation suggests that this perhaps 150 | // could/should be done in another thread, but as the 151 | // database is only one table, I doubt it matters... 152 | 153 | traceHandler.traceDebug(TAG, "DatabaseMessageStore21 | * Mechanism for persisting messages until we know they have been received 22 | *
23 | *44 | * MqttConnection holds a MqttAsyncClient {host,port,clientId} instance to perform 45 | * MQTT operations to MQTT broker. 46 | *
47 | *
48 | * Most of the major API here is intended to implement the most general forms of
49 | * the methods in IMqttAsyncClient, with slight adjustments for the Android
50 | * environment
51 | * These adjustments usually consist of adding two parameters to each method :-
52 | *
61 | * Operations are very much asynchronous, so success and failure are notified by 62 | * packing the relevant data into Intent objects which are broadcast back to the 63 | * Activity via the MqttService.callbackToActivity() method. 64 | *
65 | */ 66 | class MqttConnection implements MqttCallbackExtended { 67 | 68 | // Strings for Intents etc.. 69 | private static final String TAG = "MqttConnection"; 70 | // Error status messages 71 | private static final String NOT_CONNECTED = "not connected"; 72 | 73 | // fields for the connection definition 74 | private String serverURI; 75 | public String getServerURI() { 76 | return serverURI; 77 | } 78 | 79 | public void setServerURI(String serverURI) { 80 | this.serverURI = serverURI; 81 | } 82 | 83 | public String getClientId() { 84 | return clientId; 85 | } 86 | 87 | public void setClientId(String clientId) { 88 | this.clientId = clientId; 89 | } 90 | 91 | private String clientId; 92 | private MqttClientPersistence persistence = null; 93 | private MqttConnectOptions connectOptions; 94 | 95 | public MqttConnectOptions getConnectOptions() { 96 | return connectOptions; 97 | } 98 | 99 | public void setConnectOptions(MqttConnectOptions connectOptions) { 100 | this.connectOptions = connectOptions; 101 | } 102 | 103 | // Client handle, used for callbacks... 104 | private String clientHandle; 105 | 106 | public String getClientHandle() { 107 | return clientHandle; 108 | } 109 | 110 | public void setClientHandle(String clientHandle) { 111 | this.clientHandle = clientHandle; 112 | } 113 | 114 | //store connect ActivityToken for reconnect 115 | private String reconnectActivityToken = null; 116 | 117 | // our client object - instantiated on connect 118 | private MqttAsyncClient myClient = null; 119 | 120 | private AlarmPingSender alarmPingSender = null; 121 | 122 | // our (parent) service object 123 | private MqttService service = null; 124 | 125 | private volatile boolean disconnected = true; 126 | private boolean cleanSession = true; 127 | 128 | // Indicate this connection is connecting or not. 129 | // This variable uses to avoid reconnect multiple times. 130 | private volatile boolean isConnecting = false; 131 | 132 | // Saved sent messages and their corresponding Topics, activityTokens and 133 | // invocationContexts, so we can handle "deliveryComplete" callbacks 134 | // from the mqttClient 135 | private Map
971 | * Simply handles the basic success/failure cases for operations which don't
972 | * return results
973 | *
974 | */
975 | private class MqttConnectionListener implements IMqttActionListener {
976 |
977 | private final Bundle resultBundle;
978 |
979 | private MqttConnectionListener(Bundle resultBundle) {
980 | this.resultBundle = resultBundle;
981 | }
982 |
983 | @Override
984 | public void onSuccess(IMqttToken asyncActionToken) {
985 | service.callbackToActivity(clientHandle, Status.OK, resultBundle);
986 | }
987 |
988 | @Override
989 | public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
990 | resultBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE,
991 | exception.getLocalizedMessage());
992 |
993 | resultBundle.putSerializable(
994 | MqttServiceConstants.CALLBACK_EXCEPTION, exception);
995 |
996 | service.callbackToActivity(clientHandle, Status.ERROR, resultBundle);
997 | }
998 | }
999 |
1000 | /**
1001 | * Receive notification that we are offline
1002 | * if cleanSession is true, we need to regard this as a disconnection
1003 | */
1004 | void offline() {
1005 |
1006 | if (!disconnected && !cleanSession) {
1007 | Exception e = new Exception("Android offline");
1008 | connectionLost(e);
1009 | }
1010 | }
1011 |
1012 | /**
1013 | * Reconnect
1014 | * Only appropriate if cleanSession is false and we were connected.
1015 | * Declare as synchronized to avoid multiple calls to this method to send connect
1016 | * multiple times
1017 | */
1018 | synchronized void reconnect() {
1019 |
1020 | if (myClient == null) {
1021 | service.traceError(TAG,"Reconnect myClient = null. Will not do reconnect");
1022 | return;
1023 | }
1024 |
1025 | if (isConnecting) {
1026 | service.traceDebug(TAG, "The client is connecting. Reconnect return directly.");
1027 | return ;
1028 | }
1029 |
1030 | if(!service.isOnline()){
1031 | service.traceDebug(TAG,
1032 | "The network is not reachable. Will not do reconnect");
1033 | return;
1034 | }
1035 |
1036 | if(connectOptions.isAutomaticReconnect()){
1037 | //The Automatic reconnect functionality is enabled here
1038 | Log.i(TAG, "Requesting Automatic reconnect using New Java AC");
1039 | final Bundle resultBundle = new Bundle();
1040 | resultBundle.putString(
1041 | MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN,
1042 | reconnectActivityToken);
1043 | resultBundle.putString(
1044 | MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT, null);
1045 | resultBundle.putString(MqttServiceConstants.CALLBACK_ACTION,
1046 | MqttServiceConstants.CONNECT_ACTION);
1047 | try {
1048 | myClient.reconnect();
1049 | } catch (MqttException ex){
1050 | Log.e(TAG, "Exception occurred attempting to reconnect: " + ex.getMessage());
1051 | setConnectingState(false);
1052 | handleException(resultBundle, ex);
1053 | }
1054 | } else if (disconnected && !cleanSession) {
1055 | // use the activityToke the same with action connect
1056 | service.traceDebug(TAG,"Do Real Reconnect!");
1057 | final Bundle resultBundle = new Bundle();
1058 | resultBundle.putString(
1059 | MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN,
1060 | reconnectActivityToken);
1061 | resultBundle.putString(
1062 | MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT, null);
1063 | resultBundle.putString(MqttServiceConstants.CALLBACK_ACTION,
1064 | MqttServiceConstants.CONNECT_ACTION);
1065 |
1066 | try {
1067 |
1068 | IMqttActionListener listener = new MqttConnectionListener(resultBundle) {
1069 | @Override
1070 | public void onSuccess(IMqttToken asyncActionToken) {
1071 | // since the device's cpu can go to sleep, acquire a
1072 | // wakelock and drop it later.
1073 | service.traceDebug(TAG,"Reconnect Success!");
1074 | service.traceDebug(TAG,"DeliverBacklog when reconnect.");
1075 | doAfterConnectSuccess(resultBundle);
1076 | }
1077 |
1078 | @Override
1079 | public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
1080 | resultBundle.putString(
1081 | MqttServiceConstants.CALLBACK_ERROR_MESSAGE,
1082 | exception.getLocalizedMessage());
1083 | resultBundle.putSerializable(
1084 | MqttServiceConstants.CALLBACK_EXCEPTION,
1085 | exception);
1086 | service.callbackToActivity(clientHandle, Status.ERROR,
1087 | resultBundle);
1088 |
1089 | doAfterConnectFail(resultBundle);
1090 |
1091 | }
1092 | };
1093 |
1094 | myClient.connect(connectOptions, null, listener);
1095 | setConnectingState(true);
1096 | } catch (MqttException e) {
1097 | service.traceError(TAG, "Cannot reconnect to remote server." + e.getMessage());
1098 | setConnectingState(false);
1099 | handleException(resultBundle, e);
1100 | } catch (Exception e){
1101 | /* TODO: Added Due to: https://github.com/eclipse/paho.mqtt.android/issues/101
1102 | For some reason in a small number of cases, myClient is null here and so
1103 | a NullPointer Exception is thrown. This is a workaround to pass the exception
1104 | up to the application. myClient should not be null so more investigation is
1105 | required.
1106 | */
1107 | service.traceError(TAG, "Cannot reconnect to remote server." + e.getMessage());
1108 | setConnectingState(false);
1109 | MqttException newEx = new MqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR, e.getCause());
1110 | handleException(resultBundle, newEx);
1111 | }
1112 | }
1113 | }
1114 |
1115 | /**
1116 | *
1117 | * @param isConnecting
1118 | */
1119 | private synchronized void setConnectingState(boolean isConnecting){
1120 | this.isConnecting = isConnecting;
1121 | }
1122 |
1123 | /**
1124 | * Sets the DisconnectedBufferOptions for this client
1125 | * @param bufferOpts
1126 | */
1127 | public void setBufferOpts(DisconnectedBufferOptions bufferOpts) {
1128 | this.bufferOpts = bufferOpts;
1129 | myClient.setBufferOpts(bufferOpts);
1130 | }
1131 |
1132 | public int getBufferedMessageCount(){
1133 | return myClient.getBufferedMessageCount();
1134 | }
1135 |
1136 | public MqttMessage getBufferedMessage(int bufferIndex){
1137 | return myClient.getBufferedMessage(bufferIndex);
1138 | }
1139 |
1140 | public void deleteBufferedMessage(int bufferIndex){
1141 | myClient.deleteBufferedMessage(bufferIndex);
1142 | }
1143 | }
1144 |
--------------------------------------------------------------------------------
/MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/MqttDeliveryTokenAndroid.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 1999, 2014 IBM Corp.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Eclipse Distribution License v1.0 which accompany this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | * and the Eclipse Distribution License is available at
11 | * http://www.eclipse.org/org/documents/edl-v10.php.
12 | */
13 | package com.itfitness.mqttlibrary;
14 |
15 | import org.eclipse.paho.client.mqttv3.IMqttActionListener;
16 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
17 | import org.eclipse.paho.client.mqttv3.MqttException;
18 | import org.eclipse.paho.client.mqttv3.MqttMessage;
19 |
20 | /**
21 | *
22 | * Implementation of the IMqttDeliveryToken interface for use from within the 23 | * MqttAndroidClient implementation 24 | */ 25 | class MqttDeliveryTokenAndroid extends MqttTokenAndroid 26 | implements IMqttDeliveryToken { 27 | 28 | // The message which is being tracked by this token 29 | private MqttMessage message; 30 | 31 | MqttDeliveryTokenAndroid(MqttAndroidClient client, 32 | Object userContext, IMqttActionListener listener, MqttMessage message) { 33 | super(client, userContext, listener); 34 | this.message = message; 35 | } 36 | 37 | /** 38 | * @see org.eclipse.paho.client.mqttv3.IMqttDeliveryToken#getMessage() 39 | */ 40 | @Override 41 | public MqttMessage getMessage() throws MqttException { 42 | return message; 43 | } 44 | 45 | void setMessage(MqttMessage message) { 46 | this.message = message; 47 | } 48 | 49 | void notifyDelivery(MqttMessage delivered) { 50 | message = delivered; 51 | super.notifyComplete(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/MqttService.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2016 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * James Sutton - isOnline Null Pointer (bug 473775) 15 | */ 16 | package com.itfitness.mqttlibrary; 17 | 18 | import android.annotation.SuppressLint; 19 | import android.app.Service; 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.IntentFilter; 24 | import android.net.ConnectivityManager; 25 | import android.net.NetworkInfo; 26 | import android.os.Build; 27 | import android.os.Bundle; 28 | import android.os.IBinder; 29 | import android.os.PowerManager; 30 | import android.os.PowerManager.WakeLock; 31 | 32 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 33 | 34 | import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions; 35 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 36 | import org.eclipse.paho.client.mqttv3.IMqttMessageListener; 37 | import org.eclipse.paho.client.mqttv3.MqttClientPersistence; 38 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 39 | import org.eclipse.paho.client.mqttv3.MqttException; 40 | import org.eclipse.paho.client.mqttv3.MqttMessage; 41 | import org.eclipse.paho.client.mqttv3.MqttPersistenceException; 42 | import org.eclipse.paho.client.mqttv3.MqttSecurityException; 43 | 44 | import java.util.Map; 45 | import java.util.concurrent.ConcurrentHashMap; 46 | 47 | /** 48 | *
49 | * The android service which interfaces with an MQTT client implementation 50 | *
51 | *
52 | * The main API of MqttService is intended to pretty much mirror the
53 | * IMqttAsyncClient with appropriate adjustments for the Android environment.
54 | * These adjustments usually consist of adding two parameters to each method :-
55 | *
64 | * To support multiple client connections, the bulk of the MQTT work is 65 | * delegated to MqttConnection objects. These are identified by "client 66 | * handle" strings, which is how the Activity, and the higher-level APIs refer 67 | * to them. 68 | *
69 | *70 | * Activities using this service are expected to start it and bind to it using 71 | * the BIND_AUTO_CREATE flag. The life cycle of this service is based on this 72 | * approach. 73 | *
74 | *
75 | * Operations are highly asynchronous - in most cases results are returned to
76 | * the Activity by broadcasting one (or occasionally more) appropriate Intents,
77 | * which the Activity is expected to register a listener for.
78 | * The Intents have an Action of
79 | * {@link MqttServiceConstants#CALLBACK_TO_ACTIVITY
80 | * MqttServiceConstants.CALLBACK_TO_ACTIVITY} which allows the Activity to
81 | * register a listener with an appropriate IntentFilter.
82 | * Further data is provided by "Extra Data" in the Intent, as follows :-
83 | *
Name | 87 | *Data Type | 88 | *Value | 89 | *Operations used for | 90 | *||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
93 | * {@link MqttServiceConstants#CALLBACK_CLIENT_HANDLE 94 | * MqttServiceConstants.CALLBACK_CLIENT_HANDLE} | 95 | *String | 96 | *The clientHandle identifying the client which 97 | * initiated this operation | 98 | *All operations | 99 | *||||||||
{@link MqttServiceConstants#CALLBACK_STATUS 102 | * MqttServiceConstants.CALLBACK_STATUS} | 103 | *Serializable | 104 | *An {@link Status} value indicating success or 105 | * otherwise of the operation | 106 | *All operations | 107 | *||||||||
110 | * {@link MqttServiceConstants#CALLBACK_ACTIVITY_TOKEN 111 | * MqttServiceConstants.CALLBACK_ACTIVITY_TOKEN} | 112 | *String | 113 | *the activityToken passed into the operation | 114 | *All operations | 115 | *||||||||
118 | * {@link MqttServiceConstants#CALLBACK_INVOCATION_CONTEXT 119 | * MqttServiceConstants.CALLBACK_INVOCATION_CONTEXT} | 120 | *String | 121 | *the invocationContext passed into the operation 122 | * | 123 | *All operations | 124 | *||||||||
{@link MqttServiceConstants#CALLBACK_ACTION 127 | * MqttServiceConstants.CALLBACK_ACTION} | 128 | *String | 129 | *one of
130 | *
|
169 | * All operations | 170 | *||||||||
173 | * {@link MqttServiceConstants#CALLBACK_ERROR_MESSAGE 174 | * MqttServiceConstants.CALLBACK_ERROR_MESSAGE} 175 | * | String | 176 | *A suitable error message (taken from the 177 | * relevant exception where possible) | 178 | *All failing operations | 179 | *||||||||
182 | * {@link MqttServiceConstants#CALLBACK_ERROR_NUMBER 183 | * MqttServiceConstants.CALLBACK_ERROR_NUMBER} 184 | * | int | 185 | *A suitable error code (taken from the relevant 186 | * exception where possible) | 187 | *All failing operations | 188 | *||||||||
191 | * {@link MqttServiceConstants#CALLBACK_EXCEPTION_STACK 192 | * MqttServiceConstants.CALLBACK_EXCEPTION_STACK} | 193 | *String | 194 | *The stacktrace of the failing call | 195 | *The Connection Lost event | 196 | *||||||||
199 | * {@link MqttServiceConstants#CALLBACK_MESSAGE_ID 200 | * MqttServiceConstants.CALLBACK_MESSAGE_ID} | 201 | *String | 202 | *The identifier for the message in the message 203 | * store, used by the Activity to acknowledge the arrival of the message, so 204 | * that the service may remove it from the store | 205 | *The Message Arrived event | 206 | *||||||||
209 | * {@link MqttServiceConstants#CALLBACK_DESTINATION_NAME 210 | * MqttServiceConstants.CALLBACK_DESTINATION_NAME} 211 | * | String | 212 | *The topic on which the message was received | 213 | *The Message Arrived event | 214 | *||||||||
217 | * {@link MqttServiceConstants#CALLBACK_MESSAGE_PARCEL 218 | * MqttServiceConstants.CALLBACK_MESSAGE_PARCEL} | 219 | *Parcelable | 220 | *The new message encapsulated in Android 221 | * Parcelable format as a {@link ParcelableMqttMessage} | 222 | *The Message Arrived event | 223 | *
true
to turn on tracing, false
to turn off tracing
694 | */
695 | public void setTraceEnabled(boolean traceEnabled) {
696 | this.traceEnabled = traceEnabled;
697 | }
698 |
699 | /**
700 | * Check whether trace is on or off.
701 | *
702 | * @return the state of trace
703 | */
704 | public boolean isTraceEnabled(){
705 | return this.traceEnabled;
706 | }
707 |
708 | /**
709 | * Trace debugging information
710 | *
711 | * @param tag
712 | * identifier for the source of the trace
713 | * @param message
714 | * the text to be traced
715 | */
716 | @Override
717 | public void traceDebug(String tag, String message) {
718 | traceCallback(MqttServiceConstants.TRACE_DEBUG, tag, message);
719 | }
720 |
721 | /**
722 | * Trace error information
723 | *
724 | * @param tag
725 | * identifier for the source of the trace
726 | * @param message
727 | * the text to be traced
728 | */
729 | @Override
730 | public void traceError(String tag, String message) {
731 | traceCallback(MqttServiceConstants.TRACE_ERROR, tag, message);
732 | }
733 |
734 | private void traceCallback(String severity, String tag, String message) {
735 | if ((traceCallbackId != null) && (traceEnabled)) {
736 | Bundle dataBundle = new Bundle();
737 | dataBundle.putString(MqttServiceConstants.CALLBACK_ACTION, MqttServiceConstants.TRACE_ACTION);
738 | dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_SEVERITY, severity);
739 | dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_TAG, tag);
740 | //dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_ID, traceCallbackId);
741 | dataBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE, message);
742 | callbackToActivity(traceCallbackId, Status.ERROR, dataBundle);
743 | }
744 | }
745 |
746 | /**
747 | * trace exceptions
748 | *
749 | * @param tag
750 | * identifier for the source of the trace
751 | * @param message
752 | * the text to be traced
753 | * @param e
754 | * the exception
755 | */
756 | @Override
757 | public void traceException(String tag, String message, Exception e) {
758 | if (traceCallbackId != null) {
759 | Bundle dataBundle = new Bundle();
760 | dataBundle.putString(MqttServiceConstants.CALLBACK_ACTION, MqttServiceConstants.TRACE_ACTION);
761 | dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_SEVERITY, MqttServiceConstants.TRACE_EXCEPTION);
762 | dataBundle.putString(MqttServiceConstants.CALLBACK_ERROR_MESSAGE, message);
763 | dataBundle.putSerializable(MqttServiceConstants.CALLBACK_EXCEPTION, e); //TODO: Check
764 | dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_TAG, tag);
765 | //dataBundle.putString(MqttServiceConstants.CALLBACK_TRACE_ID, traceCallbackId);
766 | callbackToActivity(traceCallbackId, Status.ERROR, dataBundle);
767 | }
768 | }
769 |
770 | @SuppressWarnings("deprecation")
771 | private void registerBroadcastReceivers() {
772 | if (networkConnectionMonitor == null) {
773 | networkConnectionMonitor = new NetworkConnectionIntentReceiver();
774 | registerReceiver(networkConnectionMonitor, new IntentFilter(
775 | ConnectivityManager.CONNECTIVITY_ACTION));
776 | }
777 |
778 | if (Build.VERSION.SDK_INT < 14 /**Build.VERSION_CODES.ICE_CREAM_SANDWICH**/) {
779 | // Support the old system for background data preferences
780 | ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
781 | backgroundDataEnabled = cm.getBackgroundDataSetting();
782 | if (backgroundDataPreferenceMonitor == null) {
783 | backgroundDataPreferenceMonitor = new BackgroundDataPreferenceReceiver();
784 | registerReceiver(
785 | backgroundDataPreferenceMonitor,
786 | new IntentFilter(
787 | ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED));
788 | }
789 | }
790 | }
791 |
792 | private void unregisterBroadcastReceivers(){
793 | if(networkConnectionMonitor != null){
794 | unregisterReceiver(networkConnectionMonitor);
795 | networkConnectionMonitor = null;
796 | }
797 |
798 | if (Build.VERSION.SDK_INT < 14 /**Build.VERSION_CODES.ICE_CREAM_SANDWICH**/) {
799 | if(backgroundDataPreferenceMonitor != null){
800 | unregisterReceiver(backgroundDataPreferenceMonitor);
801 | }
802 | }
803 | }
804 |
805 | /*
806 | * Called in response to a change in network connection - after losing a
807 | * connection to the server, this allows us to wait until we have a usable
808 | * data connection again
809 | */
810 | private class NetworkConnectionIntentReceiver extends BroadcastReceiver {
811 |
812 | @Override
813 | @SuppressLint("Wakelock")
814 | public void onReceive(Context context, Intent intent) {
815 | traceDebug(TAG, "Internal network status receive.");
816 | // we protect against the phone switching off
817 | // by requesting a wake lock - we request the minimum possible wake
818 | // lock - just enough to keep the CPU running until we've finished
819 | PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
820 | WakeLock wl = pm
821 | .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MQTT");
822 | wl.acquire();
823 | traceDebug(TAG,"Reconnect for Network recovery.");
824 | if (isOnline()) {
825 | traceDebug(TAG,"Online,reconnect.");
826 | // we have an internet connection - have another try at
827 | // connecting
828 | reconnect();
829 | } else {
830 | notifyClientsOffline();
831 | }
832 |
833 | wl.release();
834 | }
835 | }
836 |
837 | /**
838 | * @return whether the android service can be regarded as online
839 | */
840 | public boolean isOnline() {
841 | ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
842 | NetworkInfo networkInfo = cm.getActiveNetworkInfo();
843 | //noinspection RedundantIfStatement
844 | if (networkInfo != null
845 | && networkInfo.isAvailable()
846 | && networkInfo.isConnected()
847 | && backgroundDataEnabled) {
848 | return true;
849 | }
850 |
851 | return false;
852 | }
853 |
854 | /**
855 | * Notify clients we're offline
856 | */
857 | private void notifyClientsOffline() {
858 | for (MqttConnection connection : connections.values()) {
859 | connection.offline();
860 | }
861 | }
862 |
863 | /**
864 | * Detect changes of the Allow Background Data setting - only used below
865 | * ICE_CREAM_SANDWICH
866 | */
867 | private class BackgroundDataPreferenceReceiver extends BroadcastReceiver {
868 |
869 | @SuppressWarnings("deprecation")
870 | @Override
871 | public void onReceive(Context context, Intent intent) {
872 | ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
873 | traceDebug(TAG,"Reconnect since BroadcastReceiver.");
874 | if (cm.getBackgroundDataSetting()) {
875 | if (!backgroundDataEnabled) {
876 | backgroundDataEnabled = true;
877 | // we have the Internet connection - have another try at
878 | // connecting
879 | reconnect();
880 | }
881 | } else {
882 | backgroundDataEnabled = false;
883 | notifyClientsOffline();
884 | }
885 | }
886 | }
887 |
888 | /**
889 | * Sets the DisconnectedBufferOptions for this client
890 | * @param clientHandle identifier for the client
891 | * @param bufferOpts the DisconnectedBufferOptions for this client
892 | */
893 | public void setBufferOpts(String clientHandle, DisconnectedBufferOptions bufferOpts) {
894 | MqttConnection client = getConnection(clientHandle);
895 | client.setBufferOpts(bufferOpts);
896 | }
897 |
898 | public int getBufferedMessageCount(String clientHandle){
899 | MqttConnection client = getConnection(clientHandle);
900 | return client.getBufferedMessageCount();
901 | }
902 |
903 | public MqttMessage getBufferedMessage(String clientHandle, int bufferIndex){
904 | MqttConnection client = getConnection(clientHandle);
905 | return client.getBufferedMessage(bufferIndex);
906 | }
907 |
908 | public void deleteBufferedMessage(String clientHandle, int bufferIndex){
909 | MqttConnection client = getConnection(clientHandle);
910 | client.deleteBufferedMessage(bufferIndex);
911 | }
912 |
913 | }
914 |
--------------------------------------------------------------------------------
/MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/MqttServiceBinder.java:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | * Copyright (c) 1999, 2014 IBM Corp.
3 | *
4 | * All rights reserved. This program and the accompanying materials
5 | * are made available under the terms of the Eclipse Public License v1.0
6 | * and Eclipse Distribution License v1.0 which accompany this distribution.
7 | *
8 | * The Eclipse Public License is available at
9 | * http://www.eclipse.org/legal/epl-v10.html
10 | * and the Eclipse Distribution License is available at
11 | * http://www.eclipse.org/org/documents/edl-v10.php.
12 | */
13 | package com.itfitness.mqttlibrary;
14 |
15 | import android.os.Binder;
16 |
17 | /**
18 | * What the Service passes to the Activity on binding:-
19 | * Used for the column names in the database 29 | */ 30 | String DUPLICATE = "duplicate"; 31 | String RETAINED = "retained"; 32 | String QOS = "qos"; 33 | String PAYLOAD = "payload"; 34 | String DESTINATION_NAME = "destinationName"; 35 | String CLIENT_HANDLE = "clientHandle"; 36 | String MESSAGE_ID = "messageId"; 37 | 38 | /* Tags for actions passed between the Activity and the Service */ 39 | String SEND_ACTION = "send"; 40 | String UNSUBSCRIBE_ACTION = "unsubscribe"; 41 | String SUBSCRIBE_ACTION = "subscribe"; 42 | String DISCONNECT_ACTION = "disconnect"; 43 | String CONNECT_ACTION = "connect"; 44 | String CONNECT_EXTENDED_ACTION = "connectExtended"; 45 | String MESSAGE_ARRIVED_ACTION = "messageArrived"; 46 | String MESSAGE_DELIVERED_ACTION = "messageDelivered"; 47 | String ON_CONNECTION_LOST_ACTION = "onConnectionLost"; 48 | String TRACE_ACTION = "trace"; 49 | 50 | /* Identifies an Intent which calls back to the Activity */ 51 | String CALLBACK_TO_ACTIVITY = MqttService.TAG 52 | + ".callbackToActivity"+"."+VERSION; 53 | 54 | /* Identifiers for extra data on Intents broadcast to the Activity */ 55 | String CALLBACK_ACTION = MqttService.TAG + ".callbackAction"; 56 | String CALLBACK_STATUS = MqttService.TAG + ".callbackStatus"; 57 | String CALLBACK_CLIENT_HANDLE = MqttService.TAG + "." 58 | + CLIENT_HANDLE; 59 | String CALLBACK_ERROR_MESSAGE = MqttService.TAG 60 | + ".errorMessage"; 61 | String CALLBACK_EXCEPTION_STACK = MqttService.TAG 62 | + ".exceptionStack"; 63 | String CALLBACK_INVOCATION_CONTEXT = MqttService.TAG + "." 64 | + "invocationContext"; 65 | String CALLBACK_ACTIVITY_TOKEN = MqttService.TAG + "." 66 | + "activityToken"; 67 | String CALLBACK_DESTINATION_NAME = MqttService.TAG + '.' 68 | + DESTINATION_NAME; 69 | String CALLBACK_MESSAGE_ID = MqttService.TAG + '.' 70 | + MESSAGE_ID; 71 | String CALLBACK_RECONNECT = MqttService.TAG + ".reconnect"; 72 | String CALLBACK_SERVER_URI = MqttService.TAG + ".serverURI"; 73 | String CALLBACK_MESSAGE_PARCEL = MqttService.TAG + ".PARCEL"; 74 | String CALLBACK_TRACE_SEVERITY = MqttService.TAG 75 | + ".traceSeverity"; 76 | String CALLBACK_TRACE_TAG = MqttService.TAG + ".traceTag"; 77 | String CALLBACK_TRACE_ID = MqttService.TAG + ".traceId"; 78 | String CALLBACK_ERROR_NUMBER = MqttService.TAG 79 | + ".ERROR_NUMBER"; 80 | 81 | String CALLBACK_EXCEPTION = MqttService.TAG + ".exception"; 82 | 83 | //Intent prefix for Ping sender. 84 | String PING_SENDER = MqttService.TAG + ".pingSender."; 85 | 86 | //Constant for wakelock 87 | String PING_WAKELOCK = MqttService.TAG + ".client."; 88 | String WAKELOCK_NETWORK_INTENT = MqttService.TAG + ""; 89 | 90 | //Trace severity levels 91 | String TRACE_ERROR = "error"; 92 | String TRACE_DEBUG = "debug"; 93 | String TRACE_EXCEPTION = "exception"; 94 | 95 | 96 | //exception code for non MqttExceptions 97 | int NON_MQTT_EXCEPTION = -1; 98 | 99 | } -------------------------------------------------------------------------------- /MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/MqttTokenAndroid.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package com.itfitness.mqttlibrary; 14 | 15 | import org.eclipse.paho.client.mqttv3.IMqttActionListener; 16 | import org.eclipse.paho.client.mqttv3.IMqttAsyncClient; 17 | import org.eclipse.paho.client.mqttv3.IMqttToken; 18 | import org.eclipse.paho.client.mqttv3.MqttException; 19 | import org.eclipse.paho.client.mqttv3.MqttSecurityException; 20 | import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; 21 | 22 | /** 23 | *
24 | * Implementation of the IMqttToken interface for use from within the 25 | * MqttAndroidClient implementation 26 | */ 27 | 28 | class MqttTokenAndroid implements IMqttToken { 29 | 30 | private IMqttActionListener listener; 31 | 32 | private volatile boolean isComplete; 33 | 34 | private volatile MqttException lastException; 35 | 36 | private Object waitObject = new Object(); 37 | 38 | private MqttAndroidClient client; 39 | 40 | private Object userContext; 41 | 42 | private String[] topics; 43 | 44 | private IMqttToken delegate; // specifically for getMessageId 45 | 46 | private MqttException pendingException; 47 | 48 | /** 49 | * Standard constructor 50 | * 51 | * @param client used to pass MqttAndroidClient object 52 | * @param userContext used to pass context 53 | * @param listener optional listener that will be notified when the action completes. Use null if not required. 54 | */ 55 | MqttTokenAndroid(MqttAndroidClient client, 56 | Object userContext, IMqttActionListener listener) { 57 | this(client, userContext, listener, null); 58 | } 59 | 60 | /** 61 | * Constructor for use with subscribe operations 62 | * 63 | * @param client used to pass MqttAndroidClient object 64 | * @param userContext used to pass context 65 | * @param listener optional listener that will be notified when the action completes. Use null if not required. 66 | * @param topics topics to subscribe to, which can include wildcards. 67 | */ 68 | MqttTokenAndroid(MqttAndroidClient client, 69 | Object userContext, IMqttActionListener listener, String[] topics) { 70 | this.client = client; 71 | this.userContext = userContext; 72 | this.listener = listener; 73 | this.topics = topics; 74 | } 75 | 76 | /** 77 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion() 78 | */ 79 | @Override 80 | public void waitForCompletion() throws MqttException, MqttSecurityException { 81 | synchronized (waitObject) { 82 | try { 83 | waitObject.wait(); 84 | } 85 | catch (InterruptedException e) { 86 | // do nothing 87 | } 88 | } 89 | if (pendingException != null) { 90 | throw pendingException; 91 | } 92 | } 93 | 94 | /** 95 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#waitForCompletion(long) 96 | */ 97 | @Override 98 | public void waitForCompletion(long timeout) throws MqttException, 99 | MqttSecurityException { 100 | synchronized (waitObject) { 101 | try { 102 | waitObject.wait(timeout); 103 | } 104 | catch (InterruptedException e) { 105 | // do nothing 106 | } 107 | if (!isComplete) { 108 | throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); 109 | } 110 | if (pendingException != null) { 111 | throw pendingException; 112 | } 113 | } 114 | } 115 | 116 | /** 117 | * notify successful completion of the operation 118 | */ 119 | void notifyComplete() { 120 | synchronized (waitObject) { 121 | isComplete = true; 122 | waitObject.notifyAll(); 123 | if (listener != null) { 124 | listener.onSuccess(this); 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * notify unsuccessful completion of the operation 131 | */ 132 | void notifyFailure(Throwable exception) { 133 | synchronized (waitObject) { 134 | isComplete = true; 135 | if (exception instanceof MqttException) { 136 | pendingException = (MqttException) exception; 137 | } 138 | else { 139 | pendingException = new MqttException(exception); 140 | } 141 | waitObject.notifyAll(); 142 | if (exception instanceof MqttException) { 143 | lastException = (MqttException) exception; 144 | } 145 | if (listener != null) { 146 | listener.onFailure(this, exception); 147 | } 148 | } 149 | 150 | } 151 | 152 | /** 153 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#isComplete() 154 | */ 155 | @Override 156 | public boolean isComplete() { 157 | return isComplete; 158 | } 159 | 160 | void setComplete(boolean complete) { 161 | isComplete = complete; 162 | } 163 | 164 | /** 165 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getException() 166 | */ 167 | @Override 168 | public MqttException getException() { 169 | return lastException; 170 | } 171 | 172 | void setException(MqttException exception) { 173 | lastException = exception; 174 | } 175 | 176 | /** 177 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getClient() 178 | */ 179 | @Override 180 | public IMqttAsyncClient getClient() { 181 | return client; 182 | } 183 | 184 | /** 185 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#setActionCallback(IMqttActionListener) 186 | */ 187 | @Override 188 | public void setActionCallback(IMqttActionListener listener) { 189 | this.listener = listener; 190 | } 191 | 192 | /** 193 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getActionCallback() 194 | */ 195 | @Override 196 | public IMqttActionListener getActionCallback() { 197 | return listener; 198 | } 199 | 200 | /** 201 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getTopics() 202 | */ 203 | @Override 204 | public String[] getTopics() { 205 | return topics; 206 | } 207 | 208 | /** 209 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#setUserContext(Object) 210 | */ 211 | @Override 212 | public void setUserContext(Object userContext) { 213 | this.userContext = userContext; 214 | 215 | } 216 | 217 | /** 218 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getUserContext() 219 | */ 220 | @Override 221 | public Object getUserContext() { 222 | return userContext; 223 | } 224 | 225 | void setDelegate(IMqttToken delegate) { 226 | this.delegate = delegate; 227 | } 228 | 229 | /** 230 | * @see org.eclipse.paho.client.mqttv3.IMqttToken#getMessageId() 231 | */ 232 | @Override 233 | public int getMessageId() { 234 | return (delegate != null) ? delegate.getMessageId() : 0; 235 | } 236 | 237 | @Override 238 | public MqttWireMessage getResponse() { 239 | return delegate.getResponse(); 240 | } 241 | 242 | @Override 243 | public boolean getSessionPresent() { 244 | return delegate.getSessionPresent(); 245 | } 246 | 247 | @Override 248 | public int[] getGrantedQos() { 249 | return delegate.getGrantedQos(); 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/MqttTraceHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package com.itfitness.mqttlibrary; 14 | 15 | /** 16 | * Interface for simple trace handling, pass the trace message to trace 17 | * callback. 18 | * 19 | */ 20 | 21 | public interface MqttTraceHandler { 22 | 23 | /** 24 | * Trace debugging information 25 | * 26 | * @param tag 27 | * identifier for the source of the trace 28 | * @param message 29 | * the text to be traced 30 | */ 31 | void traceDebug(String tag, String message); 32 | 33 | /** 34 | * Trace error information 35 | * 36 | * @param tag 37 | * identifier for the source of the trace 38 | * @param message 39 | * the text to be traced 40 | */ 41 | void traceError(String tag, String message); 42 | 43 | /** 44 | * trace exceptions 45 | * 46 | * @param tag 47 | * identifier for the source of the trace 48 | * @param message 49 | * the text to be traced 50 | * @param e 51 | * the exception 52 | */ 53 | void traceException(String tag, String message, 54 | Exception e); 55 | 56 | } -------------------------------------------------------------------------------- /MQTTLibrary/src/main/java/com/itfitness/mqttlibrary/ParcelableMqttMessage.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package com.itfitness.mqttlibrary; 14 | 15 | import android.os.Parcel; 16 | import android.os.Parcelable; 17 | 18 | import org.eclipse.paho.client.mqttv3.MqttMessage; 19 | 20 | /** 21 | *
22 | * A way to flow MqttMessages via Bundles/Intents 23 | *
24 | * 25 | *26 | * An application will probably use this only when receiving a message from a 27 | * Service in a Bundle - the necessary code will be something like this :- 28 | *
29 | *
30 | *
31 | * private void messageArrivedAction(Bundle data) {
32 | * ParcelableMqttMessage message = (ParcelableMqttMessage) data
33 | * .getParcelable(MqttServiceConstants.CALLBACK_MESSAGE_PARCEL);
34 | * Use the normal {@link MqttMessage} methods on the the message object.
35 | * }
36 | *
37 | *
38 | *
39 | *
40 | * 41 | * It is unlikely that an application will directly use the methods which are 42 | * specific to this class. 43 | *
44 | */ 45 | 46 | public class ParcelableMqttMessage extends MqttMessage implements Parcelable { 47 | 48 | String messageId = null; 49 | 50 | ParcelableMqttMessage(MqttMessage original) { 51 | super(original.getPayload()); 52 | setQos(original.getQos()); 53 | setRetained(original.isRetained()); 54 | setDuplicate(original.isDuplicate()); 55 | } 56 | 57 | ParcelableMqttMessage(Parcel parcel) { 58 | super(parcel.createByteArray()); 59 | setQos(parcel.readInt()); 60 | boolean[] flags = parcel.createBooleanArray(); 61 | setRetained(flags[0]); 62 | setDuplicate(flags[1]); 63 | messageId = parcel.readString(); 64 | } 65 | 66 | /** 67 | * @return the messageId 68 | */ 69 | public String getMessageId() { 70 | return messageId; 71 | } 72 | 73 | /** 74 | * Describes the contents of this object 75 | */ 76 | @Override 77 | public int describeContents() { 78 | return 0; 79 | } 80 | 81 | /** 82 | * Writes the contents of this object to a parcel 83 | * 84 | * @param parcel 85 | * The parcel to write the data to. 86 | * @param flags 87 | * this parameter is ignored 88 | */ 89 | @Override 90 | public void writeToParcel(Parcel parcel, int flags) { 91 | parcel.writeByteArray(getPayload()); 92 | parcel.writeInt(getQos()); 93 | parcel.writeBooleanArray(new boolean[]{isRetained(), isDuplicate()}); 94 | parcel.writeString(messageId); 95 | } 96 | 97 | /** 98 | * A creator which creates the message object from a parcel 99 | */ 100 | public static final Creator