This website reflects my personal opinion. None of my (current or former) employers can be held responsible for the content of this site.

Heeere

Test HTML+CSS presentations!
See my updated page with demos.

Summary

2012-11-03: It seems android is making progress on the discovery side and since 4.1 it is included in Android itself.

2011-03-27: updated the link to github (was pointing to the wrong project). Thanks to Christopher for the feedback.

2011-02-21: updated how to bundle jmdns library (this much cleaner version is now pushed to github).

It seems that people have troubles using dns-sd/zeroconf/bonjour on Android. I can understand that ;D. I managed to make things work so I provide here the main steps for using jmdns (3.4.0) on Android. I pushed also some operationnal projects on github. The discovery works in both directions: the phone sees services from remote computers and vice versa.

Let's do it

Setting

I tested with a laptop and a phone, both connected to the same wifi ad'hoc network created by a third laptop. I also tested on a more exotic network mixing wired and wifi connections.

I pushed the projects to github: https://github.com/twitwi/AndroidDnssdDemo. Both an eclipse and a Netbeans project are provided. I used the android SDK and tested with a Nexus One running Android 2.1-update1 and then on the save device in 2.2.1, CM6 (that's the only I have).

Main steps

You can just take the projects from github, here I just explain the important things that I did to make it work.

Setting Permissions

Android clearly identifies permissions given to an application. Here we need two permissions: android.permission.INTERNET and android.permission.CHANGE_WIFI_MULTICAST_STATE. The first is for wifi communications, the second is required for ZeroConf (dnssd) to be operational.

Including JmDNS 3.4.0 (latest version at the time)

Android build system recompiles jar files using a tool called "dex" that complains when processing jmdns. Actually, the problem comes from the jmdns.jar file that contains duplicates of .class files. I basically rebuilt a clean jmdns.jar file by just unjaring and rejaring the original one. At the same time, I dropped the classes that are not used when using jmdns as a library. Here are the commands I used:

mkdir unjar
cd unjar
jar xf ../jmdns.jar
jar cfm ../jmdns.jar META-INF/MANIFEST.MF javax/
        

I could not manage to make dex properly compile jmdns (3.4.0), so I decided to add the sources of jmdns directly in my project. Only the "javax.**" packages are included and, also, I removed the javax.jmdns.test package (this last removal is not done in the new method).

Making ZeroConf works: enabling multicast processing

To improve battery life, processing of multicast packets is disabled by default on Android. We can and must reenable this for the service discovery to work. This is done programmatically by acquiring a lock in our activity. See the java code for the full class, but here are the related pieces:

class ... {
   android.net.wifi.WifiManager.MulticastLock lock;
   ...
   private void setUp() { // to be called by onCreate
	android.net.wifi.WifiManager wifi =
           (android.net.wifi.WifiManager)
              getSystemService(android.content.Context.WIFI_SERVICE);
	lock = wifi.createMulticastLock("HeeereDnssdLock");
        lock.setReferenceCounted(true);
        lock.acquire();
        ...
   }
   protected void onDestroy() {
        if (lock != null) lock.release();
        ...
   }
        

Looking for services

With jmdns, you can list all visible services of a particular type. Here we will list the "_workstation" services that corresponds to the computers (at least the ones running recent linux systems). You do it by registering a listener that will "resolve" all found services. See the java code for the full class, but here are the related pieces:

private String type = "_workstation._tcp.local.";
...
    jmdns = JmDNS.create();
    jmdns.addServiceListener(type, listener = new ServiceListener() {
        public void serviceResolved(ServiceEvent ev) {
            notifyUser("Service resolved: "
                     + ev.getInfo().getQualifiedName()
                     + " port:" + ev.getInfo().getPort());
        }
        public void serviceRemoved(ServiceEvent ev) {
            notifyUser("Service removed: " + ev.getName());
        }
        public void serviceAdded(ServiceEvent event) {
            // Required to force serviceResolved to be called again
            // (after the first search)
            jmdns.requestServiceInfo(event.getType(), event.getName(), 1);
        }
    });

    ...

    jmdns.removeServiceListener(type, listener);
    jmdns.close();
        

Exposing a test service

To start a simple service, you have to first create (and optionnally fill) a ServiceInfo structure, then it's a single call to jmdns:

serviceInfo = ServiceInfo.create("_test._tcp.local.",
                                 "AndroidTest", 0,
                                 "test from android");
jmdns.registerService(serviceInfo);

...

jmdns.unregisterAllServices();
jmdns.close();
        

Building and Deploying

If you're using eclipse just use "Run As -> Android Application".

With Netbeans and/or the command line, just build your project, e.g. with ant. Then I deploy through USB with $SDK/platform-tools/adb install -r dist/DemoWithNetbeans.apk and monitor it with $SDK/platform-tools/adb logcat.



Enjoyed it, want to add comments or remarks? Contact me at click-me ;-p @nospam.com.

Links to apps using dnssd (let me know if you want to appear here)