Paho's MQTT client disconnects after the device locks - android

I'm currently working on a small "Panic Button" app supposed to operate in a medical facility. As one of the project's assumptions is to be Internet-independent, I've decided to go for MQTT with a server set up in the local WLAN.
I've implemented the Paho Android Service and it works pretty good. Up to a certain point. Once I lock the device and turn off the sreen, exactly after one minute the client gets disconnected. As I've set MQTT options to KeepAlive interval of 30s, this must be caused by Android itself, probably going into its lock-sleep. I'm obtaining the same results on couple of different smartphones, so it is probably also not user settings - related.
I'd rather avoid setting up an auto-reconnect procedure in
public class ServerCallback implements MqttCallback
{
public void connectionLost(Throwable cause) {
...
}
}
Because I want to use this method to prompt an error dialog once connection is lost due to less predictable reasons.
If so, what options do I have to prevent this disconnection?
EDIT:
Additional observation of mine is that as long as the device is plugged in and charging, disconnection does not occur.

After googling around I think I found the reason:
The Paho MQTT client uses a TimerTask to schedule the keepalive ping. A TimerTask will stop when the phone goes to sleep, and is therefore a poor choice here... The implementation for the keepalive ping can be found in the class TimerPingSender which is derived from the MqttPingSender class.
In order to get timed events when the phone is sleeping, it must be triggered by the AlarmManager. The best solution to the problem I found was to make an alternative class derived from the MqttPingSender class. Before I started writing such a class myself, I googled and found someone who had already done it on GitHub.
It can be found here:
https://github.com/Suxsem/Domo-Android/blob/master/app/src/main/java/com/suxsem/domo/MqttPingSender.java
I also had to add an alternative constructor to MqttClient:
public MqttClient(String serverURI, String clientId, MqttClientPersistence persistence, MqttPingSender pingSender) throws MqttException {
aClient = new MqttAsyncClient(serverURI, clientId, persistence, pingSender);
}
and where I instantiate the MqttClient (in my Service) I do this:
MqttPingSender pingSender = new MqttPingSenderAlarm(this);
mqClient = new MqttClient("tcp://<IP>:<PORT>", "MyTestServiceID", new MemoryPersistence(), pingSender);
Until now this seems to work flawlessly, but I've only tested it 20-30 minutes.

Related

System managed connection - place outbound call without treating it as a sim call

I'm working on implementing the calling functionality for a voip app and struggling with making an outbound call with a system managed connection service **
The part I'm struggling with is as follows:
val extras = Bundle()
extras.putString(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle.id) // I assume I need this, otherwise how will it know which connection service to invoke?
telecomManager.placeCall(Uri.parse("tel:+4412345"), extras)
what I'd like to happen is that the default dialler is invoked and then because I told it my own phone account handle it will then callback to my own implementation of connection service via: onCreateOutgoingConnection where I can then rig up the voip call and maintain the connection state myself.
What actually happens however is that the default dialler is invoked and then it just attempts to ring it as it would with a normal sim call (so I can't broker the connection myself via webrtc)
My setup for the phone account is as follows:
val extras = Bundle()
val builder = PhoneAccount.builder(phoneAccountHandle, label)
builder.setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER) // because I want to manage my own call connections but use default UI
builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) // because I want to make calls
// builder.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) // commented out because otherwise I have to be self managed for inbound
builder.setExtras(extras)
val phoneAccount = builder.build()
telecomManager.registerPhoneAccount(phoneAccount)
For what it's worth I've figured out the inbound side of things and that's working pretty well (I receive a firebase message, I tell telecom there is an inbound call, it then calls back to onCreateIncomingConnection, it then invokes the default in call UI, and I then crack on with completing the webrtc connection between both parties)
I'm fairly new to android development so I'm hoping that I've missed something "obvious" as opposed to what I'm attempting being impossible. Can anyone give me any help on this?
Note: I'm aware of the documentation at https://developer.android.com/guide/topics/connectivity/telecom/selfManaged but that is for a self managed connection, (also, I did initially follow this when attempting a self-managed solution but then gave up (see below). In my case I don't really want to show my own UI (because that would break symmetry with the inbound calls - and then I have the pain of trying to interoperate with other UIs correctly))
** The reason I've chosen system managed over self managed is that I have to play nicely with other sim calls and I had a world of pain trying to handle the case that my app call follows an ongoing sim call (seemed that nothing I did to my own connection did anything to cause a change in the other connection (like hold the call) so I wound up with two calls going on at the same time).
Update: helps if I add the right kind of extra! it should be parcelable and provide the phoneAccountHandle ... managed to get a bit further and may answer my own question if I get this working ...
My mistake here (left in original post for posterity) is that instead of adding the phone account handle id as a string extra when placing the call, I should have supplied the actual phone account handle as a parcelable extra. This now brings up the default UI, calls out to my connection service and then does nothing (which is fine because I haven't rigged up the webrtc yet)

Android JmDNS how to remove service

im currently writing an android app, which should keep track of other devices in the same wifi network using JmDNS.
The discovery process works correctly, but i don't know how to keep track of other devices especially noticing their removing.
As mentioned in
JmDNS device removal detection it seems like i have to implement this callback myself but I dont have any clue how to do this. It seems like the JmDNS Api doesn't provide any method to publish service messages myself.
My ideas so far:
Use the discovered socket connection to keep track of changes
Use JmDNS.requestServiceInfo() from time to time to check if the service is still available
Un-/Register services + listeners so they can find each other again
Does anyone know another way to solve this issue or could tell me how to trigger the serviceRemoved() callback?
PS: before taking this approach I tried Androids-NSD API, which seems to be quite unstable
So here are some things i figured out while experimenting:
The DNS-cache is set to one hour, which means that listeners won't remove a service as long as that counter didn't finish.
The mistake i made was unregistering my service after the wifi was switched off. Services send broadcast message that they aren't available anymore. This message can't be sent if the wifi connection was shutdown.
So instead of calling JmDNS.unregisterAllServices() after the wifi connection I have to call it when it's available. This leads to the onServiceRemoved() callback beeing fired in the listener

Long running client-server connection between two devices

If two (or more) devices are connected to the same network, and each has my apk installed, how might one device efficiently 'talk' to the other? Google Play services, Wifi Direct, and bluetooth is unfortunately not available on these devices.
I thought of using a 3rd party push notification service, but ideally I need the response between either device to be as fast as possible, and long-lived.
I have managed to get two devices sending messages to one another using the old client-server Network Discovery Sample app in the docs. However, if either of the apps is closed or leaves memory, the connection is obviously broken. Therefore I'm trying to figure out if this is possible through a Service, which I understand exists outside of the Activity lifecycle.
I understand how an Activity might connect to a Service to send a message (good sample on that located here), but from what I gather this all happens locally on the device. Is it possible to have this exchange happen over a local network, from one app to another? I guess what I'm saying is how can I set up a basic client server socket relationship between two apps that won't die?
It has been a long time but it should still work.
The problem here, as I understand it, is to have something that keeps running when the app is gone.
I remember using IntentService for this purpose. In the onHandleIntent() we made it loop while(!stopCondition) {...}
It was a stable solution then but it was around kitkat's time.
I'd try with the solution in your first paragraph being executed and managed by the IntentService which should keep it available.

How to send ping using Eclipse Paho MQTT client?

We've just started building our own push notification system (due to client's requirement) for Android and found Eclipse Paho (http://www.eclipse.org/paho/). Needless to say, this project is really exciting.
The problem with Android is, if the CPU is in sleep state, the MQTT client may not get the chance to send ping at its set interval. The workaround is using AlarmManager to wake it up and get the job done. The Android documentation says:
The Alarm Manager holds a CPU wake lock as long as the alarm
receiver's onReceive() method is executing. This guarantees that the
phone will not sleep until you have finished handling the broadcast.
Once onReceive() returns, the Alarm Manager releases this wake lock.
This means that the phone will in some cases sleep as soon as your
onReceive() method completes.
http://developer.android.com/reference/android/app/AlarmManager.html
I need to be sure that I could send the ping command within that onReceive() method while the CPU has PARTIAL_WAKE_LOCK, so I was searching a way to manually sending ping to server but it seems the client doesn't expose any such method. Am I missing something? Or, what is the workaround here except publishing my own "ping message"? I want to avoid that because of:
Larger overhead
We'll ensure that Android clients are subscriber only, may be with Mosquitto's ACL. They will not be allowed to publish messages.
I've been doing some work with MQTT on Android and I've experienced exactly the same issue.
As Dale says, the old version of the MQTT client used to have an explicit ping() method, but unfortunately this is now hidden away.
The simplest approach, and the one I use, is to explicitly publish a 1 byte message to a particular topic, to serve as the keepalive. I don't think this should add much to the overhead of your application and, while I'm not familiar with Mosquitto's ACL, I assume you could have every client use the same 'keepalive' topic and just provide write access to all. This shouldn't affect security as long as no-one can read from the topic.
An alternative approach would be to have the server send the client(s) a 'keepalive' message at QoS 1 or 2 (pub/sub through a single topic to all for efficiency) as, due to the QoS flows, this will involve the client sending a message back to the server under the covers; which will serve as the keepalive. This has the advantage of keeping your clients as subscriber only; however it's incompatible with 'clean session = false' (as you would have large amounts of messages queued up for delivery to clients who are offline for a while - needlessly affecting performance on reconnect).
Unfortunately these are the only two workarounds that I can currently think of.
Also, as a brief aside, I've experienced a number of issues using the MqttDefaultFilePersistence on Android, so you might want to be aware of this. In particular to do with file locking and problems when re-instantiating the client. To get around this I've created an implementation of MqttClientPersistence built on top of an SQLite database and this is much more robust; you might want to do the same.
I came across this issue when writing MQTT apps for Android a year or so ago. I've written about it at some length at http://dalelane.co.uk/blog/?p=1599 but in short, yes - I saw the same problem that you describe where if the CPU is asleep when the MQTT client should send it's ping, then the ping never gets sent.
The difference is that I was using a different MQTT client library to you (this was before the days of Paho), and the client library that I used did have a ping() method that I could call. (The full source for my implementation is at that link, and it does solve this problem).
Can you not extend the implementation of the Paho client library to include the PING command? I assume it should be a reasonably small modification.
There is a way to modify the paho code and make a ping at any time. If we use publishing topic to keep alive, we have to send at least 7 or 8 bytes to server. Yes, 8 bytes is still not big data. But the heartbeat of MQTT is only 2bytes. We have lost the best advantage of MQTT.
Look deeply into the paho code, I modify it and write a public method named nnnn() in MQTTClient. This method could send MqttPingReq to th server. the implemetation can be found here...https://github.com/chinesejie/paho-for-android
my solution:
(1) modify: ClientComms comms; from protected to public (in package org.eclipse.paho.client.mqttv3)
public class MqttAsyncClient implements IMqttAsyncClient { // DestinationProvider {
//...
public ClientComms comms; // Add by Ben for pingreq*
//...
}
(2) define new class: (derived from MqttClient)
public class MqttClient2 extends MqttClient {
public MqttClient2(String serverURI, String clientId, MqttClientPersistence persistence) throws MqttException {
super(serverURI, clientId, persistence);
}
public void pingreq() throws MqttException {
MqttDeliveryToken token = new MqttDeliveryToken(getClientId());
MqttPingReq pingMsg = new MqttPingReq();
aClient.comms.sendNoWait(pingMsg, token);
}
}
(3) anywhere, you can:
MqttClient2 mClient = new MqttClient2(url, mDeviceId, mDataStore);
mClient.pingreq();
hope this can be helpfull for you.

Detecting network connectivity on Android?

OS: Android
Given: User has stated that they wish to remain connected to app's server to receive updates, etc.
Goal: To ensure that users are connected to app's server even when app is in background state.
Question: One problem has been occasional disconnects from the network. If a user loses data network connectivity (loss of 2G, 3G, WiFi) and then later regains connectivity, our app is left without a connection. I am currently trying to make use of PhoneStateListener's in order to detect various network changes and thereby restart connectivity with the server when appropriate. On my Nexus One (2.1), I find that onSignalStrengthsChanged and onDataConnectionStateChanged aren't called except when the listeners are originally registered, but not afterwards. The listeners are registered inside of a Service so they are continuously listening as long as the Service is alive (which we can assume to be 'forever' for purposes of this question). Has anyone else had any issues with listening to the state of the Data Connection?
onServiceStateChanged seems to be the most reliable so far, but I wanted to know if people have had success with the other listeners as well.
I guess you'll have to send keepalive messages at regular intervals to check whether the connection is still there. If not, reestablish it. There is a smorgasbord of reasons why your connection might drop, and you won't be able to check all of those client side.
Might consider using google's cloud service for what you're doing though, since they already keep an connection open for that. That way your user's phones won't have the overhead of keeping yet another connection around (which can be quite expensive)
Since StackOverflow doesn't allow me to close the question otherwise, I will provide the answer to why my PhoneStateListeners were not working:
I discovered that my problem was simply that I wasn't registering my listeners in a bitwise manner, but rather successively (i.e.
telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
telephonyManager.listen(listener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
.
.
.
instead of (the correct):
telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE..............);)
I will close this question and perhaps later open a different question asking for design suggestion on maintaining 'always alive' connections to a server on Android. Thanks.
As far as i know listeners could not be called when app is in background. You should try use services not activity.
And also remember to retrieve TelephonyManager in proper way
TelephonyManager mTelephonyMgr = (TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE);
I would think you would use the ConnectivityManager for this.
Class that answers queries about the state of network connectivity. It also notifies applications when network connectivity changes. Get an instance of this class by calling Context.getSystemService(Context.CONNECTIVITY_SERVICE).
http://developer.android.com/reference/android/net/ConnectivityManager.html
per the docs, it monitors network connectivity and sends broadcast intents on change to applications.

Categories

Resources