Android : Stopping a Bonjour service left running after the parent process quit abrubtly
Asked Answered
H

2

6

My app is essentially a background service that needs to occasionally register an NSD service (Bonjour service) for the purpose of enabling discovery of a socket server run by the main background service (aka run by the app).

If I am reading the Android Bonjour Service doc correctly, this is how you start the Bonjour service (abbreviated for conciseness):

mNsdManager = Context.getSystemService(Context.NSD_SERVICE);
mDiscoveryListener = new NsdManager.DiscoveryListener()
mNsdManager.discoverServices(
        SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

...and this is how you stop it:

mNsdManager.unregisterService(mRegistrationListener);

Here the part I can't wrap my head around: if the main service goes down abruptly, any Bonjour service that was registered at the time of the crash keeps running even though it no longer has a purpose (the socket server it helps discover is no longer around).

I can't event cleanup the zombie Bonjour services when the main service is restarted because the mRegistrationListener the service was initially registered with is also no longer around.

I suspect I am taking the wrong approach: how do I make sure I don't leave a mess of zombie Bonjour services behind after the main service crashed?

Haiphong answered 14/12, 2015 at 18:25 Comment(0)
F
2

Non-specific to Android Bonjour, you could try to handle the crash by setting up your service as is outlined in the answer here: Can I call a method before my application go to crash

If you can't set this up to make the unregisterService call, you should be able to set it up to use ActivityManager's API killBackgroundProcesses. This requires adding the permission to your manifest:

android.permission.KILL_BACKGROUND_PROCESSES
Free answered 14/12, 2015 at 18:44 Comment(3)
Thanks, catching uncaught exceptions with UncaughtExceptionHandler is probably the way to go - though I was hoping for a way to run the Bonjour service within the main process (I find it surprising the Android implementation does not allow for that option). I will accept your answer if nothing better comes through in the next couple of days.Haiphong
I agree, you would think there is a proper way to handle it. I couldn't find any documentation to support the theory, but it may get handled by the OS. It may be Google thinks people may not think to do it at all, so they should automatically handle it!Free
Thanks @thril, using the UncaughtExceptionHandler to unregister the service worked for me (I tested successfully by crashing the service a few seconds after the service was registered, using a daemon timer running in a separate thread)Haiphong
C
2

If I understood you correctly the main service (the one with the server socket) registers/unregisters a Nsd service while a background service starts/stops discovery the Nsd service. I think this is what you do, so your "approach" is correct.

Regarding the problem, I should welcome you to Android Nsd. There are a lot of bugs with the framework (among which you can find your issue) that as of Android 6.0 haven't been fixed yet making developers use other frameworks instead.

Getting back to the issue, you might try UncaughtExceptionHandler, just keep in mind that all the callbacks are invoked by the system asynchronously, and you may get NPE when it calls mRegistrationListener.onServiceUnregistered(), because, as you said, "it is no longer around".

As to the service cleanup, theoretically it is possible, but only after NsdManager source code customization (access modifier of a few methods needs to be changed in order to reach and then unregister mRegistrationListener from another process that would lead to deleting it from a listener map of NsdManager). But it doesn't make any sense if the app is to be published on market.

There is another workaround you might try/experiment with. If I remember correctly (may be mistaken), the necessary cleanup takes place upon disabling Nsd. I tried it via adb:

// Disable
adb shell service call servicediscovery 2
// Enable
adb shell service call servicediscovery 2 i32 1

However note, that making these calls programmatically may not be trivial and most likely require root, which, again, restricts the audience of your app.

Regarding the killBackgroundProcesses() method proposed by @thril, it takes a string with app's package name as parameter. But servicediscovery isn't an app, it's a system service. Also, you could try to kill the process (although I don't know which one) at runtime, but be careful, you should investigate what effect it brings to the system and be sure that the service will be started again when required (automatically by system or manually). Again, to do this root is needed.

Summing up the answer, before you proceed with Nsd, I highly recommend to do a search regarding its functionality/bugs in order to avoid possible wasting of your time and efforts. Some references in addition to the link provided above:

  1. NSD Device Lost Message Not Received on Disabling Wifi
  2. NsdManager doesn't stop service discovery

P.S. Personally I, after struggling with multiple Nsd framework bugs, ended up writing my own framework.

Contumelious answered 19/12, 2015 at 16:38 Comment(2)
Thanks @Onik, eventually using the UncaughtExceptionHandler worked as designed, I tested successfully by crashing the main service from a separate thread, using a daemon timer (Timer timer = new Timer(true) ; timer.schedule(new SimulateUncaughtException(), 2000);)Haiphong
Very well @Hugo, handling Nsd work in a separate thread is a good idea, - clearly you have time to unregister the service in that case. Although, I still recommend to check all the Nsd callbacks, especially when turning on/off Wifi (Ethernet)...Contumelious

© 2022 - 2024 — McMap. All rights reserved.