Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wifi P2P service discovery works intermittently

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); 
like image 757
Soumya Das Avatar asked Oct 10 '14 13:10

Soumya Das


People also ask

What is WIFI P2P service?

Wi-Fi Direct (P2P) allows devices with the appropriate hardware to connect directly to each other via Wi-Fi without an intermediate access point.

Is P2P a hotspot?

Hotspot Shield is a good VPN for torrenting. It supports P2P traffic on each of its 1,800 servers, has strong security features, and doesn't limit your data usage.


1 Answers

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 ;) ).

like image 197
Bartek Lipinski Avatar answered Oct 02 '22 06:10

Bartek Lipinski