31

Wifi P2P service discovery is not behaving as expected. I am seeing intermittent issues where the DNSSD listeners are not called always and hence I have no clue of nearby devices running the same app. I am using the following two APIs - one to register a service to be discovered by other devices and the other to discover the nearby services running on other devices. Any idea if I am doing anything wrong here or is there some specific sequence of other android API calls that need to be made before I call these APIs to ensure that the listeners are always called whenever there is a new service registered or even if a service is registered before we call the API to discover the local services.

API to register a local service:

private void registerService() {
    Map<String, String> values = new HashMap<String, String>();
    values.put("name", "Steve");
    values.put("port", "8080");
    WifiP2pServiceInfo srvcInfo = WifiP2pDnsSdServiceInfo.newInstance(mMyDevice.deviceName, "_http._tcp", values);

    manager.addLocalService(channel, srvcInfo, new WifiP2pManager.ActionListener() {

        @Override
        public void onSuccess() {
            Toast.makeText(WiFiDirectActivity.this, "Local service added successfully", 
                Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(int reasonCode) {
            Toast.makeText(WiFiDirectActivity.this, "Local service addition failed : " + reasonCode,
                    Toast.LENGTH_SHORT).show();
        }
    });
}

API to discover local services:

public void discoverService() {

    manager.clearServiceRequests(channel, null);

    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
        @Override
        /* Callback includes:
         * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
         * record: TXT record data as a map of key/value pairs.
         * device: The device running the advertised service.
         */
        public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
            Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
        }
    };

    DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
        @Override
        public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
            Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
        }
    };

    manager.setDnsSdResponseListeners(channel, servListener, txtListener);

    WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
    manager.addServiceRequest(channel, serviceRequest, new ActionListener() {

        @Override
        public void onSuccess() {
            // Success!
            Log.d(TAG, "addServiceRequest success");
        }

        @Override
        public void onFailure(int code) {
            // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            Log.d(TAG, "addServiceRequest failure with code " + code);
        }

    });
    manager.discoverServices(channel, new ActionListener() {

        @Override
        public void onSuccess() {
            // Success!
            Log.d(TAG, "discoverServices success");
        }

        @Override
        public void onFailure(int code) {
            // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                Log.d(TAG, "P2P isn't supported on this device.");
            } else {
                Log.d(TAG, "discoverServices failure");
            }
        }
    });
}

Note: manager & channel are initialized as

WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);
Soumya Das
  • 1,635
  • 2
  • 19
  • 28
  • I'm experiencing the same thing, callbacks to DnsSdServiceResponseListener happens - sometimes. Did you figure out the reasons for this or is this not a stable feature? – user331244 Jun 15 '15 at 10:47

2 Answers2

39

WifiP2p (in general):

Some time ago I was developing an application with a pretty complex network connectivity system based on WifiP2p with Service Broadcasting/Discovery. And based on that experience I already wrote few posts here on SO about how difficult, wearing and problematic that is. Here are two of them (they are quite full of the inside knowledge I acquired about WifiP2p with Service Discovery, and WifiP2p itself):

Why is discovering peers for Android WifiDirect so unreliable

Wi-fi P2P. Inform all peers available of some event

I would advise you to read both of my answers (even though they are focused a bit more on the WifiP2p itself). They should give you some perspective on the things you should be looking for when working with the WifiP2p Service Discovery. I can easily say that if you want to build an efficient, relatively reliable and robust WifiP2p connection system (especially with Service Discovery), you will have to work your ass off.

WifiP2p Service Discovery:

To better answer your exact question, I will tell you what I did (different from you) to make my Service Discovery work pretty reliably.

1. Broadcasting Service:

First of all: before registering your Service (with addLocalService method) you should use the WifiP2pManager's clearLocalServices method. And it is important, that you should only call addLocalService if the listener passed in the clearLocalServices returned with the onSuccess callback.

Although this sets up the broadcasting pretty nicely, I found that other nodes were not always able to detect the broadcasted service (especially when those nodes weren't already actively detecting services at the moment of registering your local Service - but they "joined" later). I couldn't find a way to fix this issue 100% reliably. And believe me I was trying probably everything WifiP2p-related. And no, the clearLocalServices-addLocalService sequence wasn't really giving satisfying results. Or more so: doing something different was working much better. What I decided to do, was after I successfully added local service (onSuccess callback from addLocalService), I started a Thread that would periodically call WifiP2pManager's method discoverPeers. That seemed to be forcing to rebroadcast all the service information.

So... basically the base of your broadcasting code should look more-less like this (bare in mind that every single piece of code I will post here is stripped-off of all "checks" if the network connectivity system is in the right state, you should design them yourself to fit your solution the best):

public void startBroadcastingService(){
    mWifiP2pManager.clearLocalServices(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            mWifiP2pManager.addLocalService(mWifiP2pChannel, mWifiP2pServiceInfo,
                    new WifiP2pManager.ActionListener() {

                        @Override
                        public void onSuccess() {
                            // service broadcasting started
                            mServiceBroadcastingHandler
                                    .postDelayed(mServiceBroadcastingRunnable,
                                            SERVICE_BROADCASTING_INTERVAL);
                        }

                        @Override
                        public void onFailure(int error) {
                            // react to failure of adding the local service
                        }
                    });
        }

        @Override
        public void onFailure(int error) {
            // react to failure of clearing the local services
        }
    });
}

where the mServiceBroadcastingRunnable should be:

private Runnable mServiceBroadcastingRunnable = new Runnable() {
    @Override
    public void run() {
        mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onFailure(int error) {
            }
        });
        mServiceBroadcastingHandler
                .postDelayed(mServiceBroadcastingRunnable, SERVICE_BROADCASTING_INTERVAL);
    }
};

2. Discovering Service:

For the discovering of your service I used similar approach. Both with the setting up the discovering, and with trying to force "rediscovery" of services.

Setting up was performed with the sequence of the following three WifiP2pManager's methods:

removeServiceRequest, addServiceRequest, discoverServices

They were called in this exact order and a particular method (second or the third one to be exact) has been called only after the previous one had "returned" with the onSuccess callback.

The rediscovery of services was being performed with the intuitive method (just by repeating the mentioned sequence: removeServiceRequest -> addServiceRequest -> discoverServices).

The base of my code looked more-less like this (to start Service Discovery I would first call prepareServiceDiscovery() and then startServiceDiscovery()):

public void prepareServiceDiscovery() {
    mWifiP2pManager.setDnsSdResponseListeners(mWifiP2pChannel,
            new WifiP2pManager.DnsSdServiceResponseListener() {

                @Override
                public void onDnsSdServiceAvailable(String instanceName,
                                                    String registrationType, WifiP2pDevice srcDevice) {
                    // do all the things you need to do with detected service
                }
            }, new WifiP2pManager.DnsSdTxtRecordListener() {

                @Override
                public void onDnsSdTxtRecordAvailable(
                        String fullDomainName, Map<String, String> record,
                        WifiP2pDevice device) {
                    // do all the things you need to do with detailed information about detected service
                }
            });

    mWifiP2pServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();
}

private void startServiceDiscovery() {
    mWifiP2pManager.removeServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
            new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    mWifiP2pManager.addServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
                            new WifiP2pManager.ActionListener() {

                                @Override
                                public void onSuccess() {
                                    mWifiP2pManager.discoverServices(mWifiP2pChannel,
                                            new WifiP2pManager.ActionListener() {

                                                @Override
                                                public void onSuccess() {
                                                    //service discovery started

                                                    mServiceDiscoveringHandler.postDelayed(
                                                            mServiceDiscoveringRunnable,
                                                            SERVICE_DISCOVERING_INTERVAL);
                                                }

                                                @Override
                                                public void onFailure(int error) {
                                                    // react to failure of starting service discovery
                                                }
                                            });
                                }

                                @Override
                                public void onFailure(int error) {
                                    // react to failure of adding service request
                                }
                            });
                }

                @Override
                public void onFailure(int reason) {
                    // react to failure of removing service request
                }
            });
}

the mServiceDiscoveringRunnable was just:

private Runnable mServiceDiscoveringRunnable = new Runnable() {
    @Override
    public void run() {
        startServiceDiscovery();
    }
};

All this made my system work quite well. It wasn't perfect yet, but with the lack of documentation on this subject I think I couldn't do much more to improve it.

If you test this approach, be sure to tell me how it works for you (or if it works for you ;) ).

Community
  • 1
  • 1
Bartek Lipinski
  • 30,698
  • 10
  • 94
  • 132
  • I tried your approach and it is working better than the documented tutorial by Google. However I still run into one problem: the service discovery is able to discover the services broadcasted by other devices for a period of time, then it discovers nothing for a while, and then the process repeat: seeing services -> nothing -> services -> nothing... What do you think? Could it be the problem with the hardware or the system itself? – Richard Wong Sep 16 '15 at 16:43
  • It's hard to tell honestly... I remeber it wasn't perfect and sometimes users had to wait a few seconds to be able to discover other devices (services). I couldn't really find a solution to this problem. The issue you're describing might have been happening to me as well, I just didnt see the pattern (services -> nothing -> services -> nothing...). – Bartek Lipinski Sep 18 '15 at 07:35
  • I've found out that the publishing phase should always go before the discovery phase. Otherwise, the discovery will not return anything at all. – Roberto Betancourt Oct 21 '15 at 17:21
  • @RichardWong, I think what you're seeing is that the discovery times out, check this android issue: https://code.google.com/p/android/issues/detail?id=206123 .. The way around it is to force discovery every 2 minutes – Nonos Jun 15 '16 at 17:49
  • 1
    @BartekLipinski Thanks for the code sample. I have implemented something similar and it does work. However, it somehow stops my phone from connected to any wifi network. Have you noticed something like this? – vipul mittal Dec 13 '16 at 02:05
  • @vipulmittal I don't think so... although it was so long ago, maybe something changed since then. Sorry I couldn't help. – Bartek Lipinski Dec 13 '16 at 10:06
  • @vipulmittal I've the same issue. Did you find any solution so far ? – osayilgan Jan 23 '17 at 23:33
  • @osayilgan yes. It terns out you need to call stopPeerDiscovery to make wifi work again. The idea way to implement would be to call discoverService and discoverPeers once and after some time call stopPeerDiscovery wait for some time repeat. – vipul mittal Jan 24 '17 at 05:29
  • @vipulmittal Thanks for the tip. But I think it has some other issues. It used to work a while ago, the same code. But then I was trying to implement the ServiceDiscovery beside the Peer Discovery, then it started to behave a bit weird. Now, if I go to Wifi Direct section in Wifi Settings, it searches Wifi Direct devices, finds some results and then the Wifi Restarts itself. Any idea ? – osayilgan Jan 24 '17 at 14:38
  • @vipulmittal one more thing. I'm starting the peer discovery, and the result is delivered successfully. But then starting the Service Discovery, which does not return anything to the Listeners. – osayilgan Jan 24 '17 at 14:40
  • @BartekLipinski Does not work. Tested different devices and used the exact code as described in the Google documentation. Service is registered successfully but it is not found by the other device... I think the wifi p2p framework is just completely broken, like the Nearby framework is. – trinity420 May 02 '17 at 12:12
  • What do you use for your WifiP2pServiceInfo ? That's the only thing I don't see defined here – Code Wiget Apr 17 '20 at 14:11
  • That would have taken me ages to figure out! – Siddharth Kamaria Sep 10 '20 at 19:31
0

if the problem is the detection of the service i believe that crearing group is the best way to make the device and service detectable but the if created group in the all devices then you cannot connect in direct. but as wifi network. i do it every day and it works.

NDev
  • 31
  • 2