hivemq-mqtt-client icon indicating copy to clipboard operation
hivemq-mqtt-client copied to clipboard

HiveMQ client doesn't send ping request to broker if smartwatch screen is off (Android)

Open cd opened this issue 3 years ago • 9 comments

Expected behavior

HiveMQ android client should keep the connection to the broker alive with ping requests.

Actual behavior

HiveMQ android client doesn't send a ping request if the screen of my android smartwatch goes off. After the timeout is exceeded, the client will (re)connect to the broker.

Interesting notes

  • The MQTT client runs inside a foreground service, so it is very stable and won't be killed from the system.
  • Any kind of battery savings is turned off (network savings included)
  • My app is whitelisted from doze mode.
  • I turned off doze mode complety
  • The MQTT client reconnect itself without any problems. Then why can't it just send a ping request?
  • If I increase the keep alive time, it will take longer for the client to disconnect. However, the client always reacts immediately to the topic that it has subscribed to. So the connection is fine. It just doesn't ping.

To Reproduce

This is the test scenario:

  1. Open Android activity / start foreground service.
  2. Init the MQTT client
  3. Subscribe a random topic
  4. Keep screen of smartwatch active (if not, the ping-req-res part will not happen, see below)
  5. Just look at the logs

Wireshark: wireshark

Mosquitto broker: broker

Here is some code from the android service:

    private void initMqttClient() {
        client = MqttClient.builder()
                .useMqttVersion3()
                .identifier("new-fossil")
                .serverHost("192.168.0.17")
                .serverPort(1883)
                .automaticReconnectWithDefaultConfig()
                .addDisconnectedListener(context -> {
                    Log.d("MainService", "MQTT Disconnected");
                    ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
                    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                    boolean isConnected = activeNetwork != null && activeNetwork.isConnected();
                    Log.d("MainService", context.getCause().getMessage());
                    if (!isConnectedToWifi()) {
                        Log.d("MainService", "(WIFI down)");
                    } else {
                        Log.d("MainService", "(WIFI OK)");
                    }
                })
                .addConnectedListener(context -> {
                    Log.d("MainService", "MQTT Connected");
                })
                .buildAsync();

        // Connect to MQTT broker
        client.connect();

        // Test counter
        subscribeTopic("testline/mytest/counter", 1, 56045);
    }

Logcat:

2021-05-14 20:29:11.291 3409-3437/com.mycomp.myapp D/MainService: MQTT Connected
2021-05-14 20:35:16.134 3409-3437/com.mycomp.myapp D/MainService: MQTT Disconnected
2021-05-14 20:35:16.138 3409-3437/com.mycomp.myapp D/MainService: Server closed connection without DISCONNECT.
2021-05-14 20:35:16.141 3409-3437/com.mycomp.myapp D/MainService: (WIFI OK)
2021-05-14 20:35:18.466 3409-3437/com.mycomp.myapp D/MainService: MQTT Connected

Details

  • Affected HiveMQ MQTT Client version(s): com.hivemq:hivemq-mqtt-client:1.2.2
  • Used JVM version:
  • Used OS (name and version): Wear OS 2.27 (Android 9, API Level 28, H MR2)
  • Used MQTT version: 3.1.1
  • Used MQTT broker (name and version): Own mosquitto broker. I also tried the online test broker test.mosquitto.org.

I've tried a LOT of different things:

  • Changed network/router
  • Changed android configurations
  • Changed different MQTT settings
  • ...

I've been there for several days now and just can't get any further. Please help.

Thanks!

cd avatar May 14 '21 19:05 cd

Hi @cd This is very likely caused by the device not waking up the application, so that the PING can not be sent. Does the reconnect happen when the screen is still off or only after you turn the screen on again?

SgtSilvio avatar May 21 '21 16:05 SgtSilvio

Thanks for your reply! The reconnect also happen when the screen is off.

A workaround for me is, to create my own ping via an empty publish message from my device, triggered by a timer. Or to subscribe a "ping topic" from an anonther client.

I wonder what the basic problem here is. Technically, with a manual ping workaround, it works.

The ability to manually send a real ping would at least be a little cleaner.

cd avatar May 21 '21 16:05 cd

The problem seems to be that the ping task that is scheduled on an executor (Netty eventloop) does not trigger a wakeup of the device. Closing of the connection by the broker (because it did not receive a ping) seems to wakeup the device, so the reconnect works.

SgtSilvio avatar Jun 04 '21 09:06 SgtSilvio

@cd I have 2 asks:

  • Can you run a simple scheduled task in a 60s interval that prints System.nanoTime() (which is used by the ping logic) when the screen is off?
  • Can you create a netty eventloop and use this to schedule the task above when the screen is off?

SgtSilvio avatar Jun 04 '21 10:06 SgtSilvio

Why I am asking:

{@link #uptimeMillis} is counted in milliseconds since the system was booted. This clock stops when the system enters deep sleep (CPU off, display dark, device waiting for external input), but is not affected by clock scaling, idle, or other power saving mechanisms. This is the basis for most interval timing such as {@link Thread#sleep(long) Thread.sleep(millls)}, {@link Object#wait(long) Object.wait(millis)}, and {@link System#nanoTime System.nanoTime()}

https://android.googlesource.com/platform/frameworks/base/+/4118012/core/java/android/os/SystemClock.java

SgtSilvio avatar Jun 04 '21 10:06 SgtSilvio

To your questions:

  • What kind of sheduled task do you mean? Timer Task? Alarm Manager? Job Sheduler? ...?
  • I'm not familiar with netty and I don't know how to implement this in my android app.

But I have more knowledge now:

  • I wrote that the reconnect works well. Unfortunately, this is no longer the case as soon as I have no longer connected the smartwatch to my computer via ADB. Then the app no longer reconnects. It's crazy.
  • I've tested another smartwatch: An Oppo Watch with Wear OS 2.27 (also API Level 28). If I'm connected to the ADB server, then the watch even sends a ping. No reconnect required. But if I disconnect the watch from ADB, it's dead. No Ping, no reconnect.
  • My already mentioned workaround, to send a "publish ping" from my watch via a timer task doesn't work anymore. It seems that after a while the operating system has stopped this behavior more and more. If I switch the screen on at any time (no matter what kind of screen/app), all timers are fired at once.

It is to despair...

cd avatar Jun 05 '21 13:06 cd

After a lot of trial and error, I have more and more the feeling that any kind of short, exact polling (ping, reconnect strategy) is very difficult or even impossible under Android / Wear OS. And even if there is some kind of "hack", that does not mean that it will work on all other devices and in the future in the same way.

I have now rebuilt my app logic and rely more on network-driven actions, because these seem to work reliably. This is how I get on (for now). Let's see what the other long-term tests say ...

Nonetheless, of course, I hope that a solution will still be found here.

Thank you for the good support and efforts up to this point!

cd avatar Jun 06 '21 20:06 cd

This is how this issue was resolved for paho: https://github.com/eclipse/paho.mqtt.java/pull/775 to get the clocks working properly.

This is only a partial solution though, because even if you use an alarm receiver to wake up the device to preform a ping, the os may coalesce the wakeups such that they occur less frequently than the requested keepalive.

powturns avatar Jun 09 '21 20:06 powturns

Update: I am happy to announce that the ping problem does not occur with Wear OS 3.0 / OneUI Watch (Galaxy Watch 4). It remains to be seen whether this will also work as desired in future implementations. Then the problem would resolve itself in the medium term.

cd avatar Aug 23 '21 13:08 cd

Hi all - since this issue hasn't had an update in a couple years, I'm going to close it out. If anything remains, please feel free to file a new issue - we'd be happy to help out!

pglombardo avatar Mar 07 '23 14:03 pglombardo