0

I've been trying to discover multiple peers - multiple times in a background service instead of an activity. Broadly based on this: http://developer.android.com/training/connect-devices-wirelessly/nsd-wifi-direct.html

Since, an advertised service of a peer once received doesn't reappear if that peer goes away and comes back in range. The reason why I need to re-discover the same peers is I'm changing the TXT record value depending on the app usage. Which needs to be re-advertised and the receiving device updates a local file with the new values.

Even though, I've managed to get this to work in a kind of an unreliable manner. By, removing the localservice and serviceRequest and re-initiate the registration and discovery by using a handler.

But, what I've noticed is some of the earlier advertisements are pooled and keep showing even when the advertising device has stopped advertising, and the TXT Listeners do not attach and at times. Which causes a WifiP2pManager ignored warning while adding both the local service as well as the service discovery request. Hence, the listener doesn't get the advertisement at times, while keeps getting advertisements at times when even Wifi is disabled.

01-06 17:14:34.324  26591-26591/com.cutting.chai.wifi D/WifiP2pManager﹕ Ignored { when=-25ms what=139313 target=android.net.wifi.p2p.WifiP2pManager$Channel$P2pHandler }
[ 01-06 17:14:34.324 26591:26591 D/Status] Added Local Service
01-06 17:14:34.324  26591-26591/com.cutting.chai.wifi D/WifiP2pManager﹕ Ignored { when=-25ms what=139313 target=android.net.wifi.p2p.WifiP2pManager$Channel$P2pHandler }
01-06 17:14:34.324  26591-26591/com.cutting.chai.wifi D/WifiP2pManager﹕ Ignored { when=-26ms what=139313 target=android.net.wifi.p2p.WifiP2pManager$Channel$P2pHandler }
[ 01-06 17:14:34.324 26591:26591 D/Status]  Added service discovery request

And below is my code:

public class WifiDirectDiscoverService extends Service{


public static final String TAG = "wifidirectservicedemo";

// TXT RECORD properties
public static final String TXTRECORD_PROP_AVAILABLE = "available";
public static final String SERVICE_INSTANCE = "_ccwifidirect";
public static final String SERVICE_REG_TYPE = "_presence._tcp";


private WifiP2pManager manager;


private final IntentFilter intentFilter = new IntentFilter();
private WifiP2pManager.Channel channel;
private BroadcastReceiver mReceiver;
private WifiP2pDnsSdServiceRequest serviceRequest;
WifiP2pDnsSdServiceInfo service;

/* Preferences to get user data to set the advertisement record */
private ccSharedPreferences sharedPreferences;
private String[] interestCodes;
private String[] hobbyCodes;


Handler mHandler = new Handler();


@Override
public IBinder onBind(Intent arg0) {

    return null;
}

public void onCreate() {
    super.onCreate();
    Log.d("Server", ">>>onCreate()");
}



final Runnable ToastRunnable = new Runnable(){
    public void run(){
        Toast.makeText(getApplicationContext(), "discover service",
                Toast.LENGTH_LONG).show();



        manager.removeLocalService(channel, service, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess () {
                appendStatus("Remove Local Service");
            }

            @Override
            public void onFailure ( int error){
                appendStatus("Failed to remove a service "+error);
            }
        });


        manager.removeServiceRequest(channel, serviceRequest, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                appendStatus("Clear Service Requests");
            }

            @Override
            public void onFailure(int error) {
                appendStatus("Failed to clear Service Requests "+error);
            }
        });


        startRegistration();
        setupListener();
        initiateDiscovery();

        mHandler.postDelayed(ToastRunnable, 15000);
    }
};


@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    super.onStartCommand(intent, startId, startId);

    intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
    intentFilter
            .addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    intentFilter
            .addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);

    manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
    channel = manager.initialize(getApplicationContext(), getMainLooper(), null);

    mReceiver = new WiFiDirectBroadcastReceiver(manager, channel, WifiDirectDiscoverService.this);
    registerReceiver(mReceiver, intentFilter);

    startRegistration();
    setupListener();
    initiateDiscovery();

    mHandler.postDelayed(ToastRunnable, 10000);

    return START_STICKY;

}


/**
 * Registers a local service and then initiates a service discovery
 */
private void startRegistration() {

    sharedPreferences = new ccSharedPreferences();

    Map<String, String> record = new HashMap<>();



    record.put(TXTRECORD_PROP_AVAILABLE, "visible");



    int size = record.toString().length();


    Toast.makeText(getApplicationContext(), size + " bytes record ", Toast.LENGTH_LONG).show();

    service = WifiP2pDnsSdServiceInfo.newInstance(
            SERVICE_INSTANCE, SERVICE_REG_TYPE, record);

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

        @Override
        public void onSuccess() {
            appendStatus("Added Local Service");
        }

        @Override
        public void onFailure(int error) {
            appendStatus("Failed to add a service "+error);
        }
    });

}

private void setupListener() {

    /*
     * Register listeners for DNS-SD services. These are callbacks invoked
     * by the system when a service is actually discovered.
     */

    WifiP2pManager.DnsSdTxtRecordListener txtListener = new WifiP2pManager.DnsSdTxtRecordListener() {

                    /**
                     * A new TXT record is available. Pick up the advertised
                     * buddy name.
                     */
                    @Override
                    public void onDnsSdTxtRecordAvailable(
                            String fullDomainName, Map<String, String> record,
                            WifiP2pDevice device) {


                        if (record.get(TXTRECORD_PROP_AVAILABLE) != null) {
                            Log.d(TAG,
                                    device.deviceName + " is "
                                            + record.get(TXTRECORD_PROP_AVAILABLE));

                            Toast.makeText(getApplicationContext(), device.deviceName + " is " + record.get(TXTRECORD_PROP_AVAILABLE), Toast.LENGTH_LONG).show();

                            Toast.makeText(getApplicationContext(), "Advertised Service is " + record.toString(), Toast.LENGTH_LONG).show();
                            Log.d("Record", record.toString());



                        }

                    }
                };


    WifiP2pManager.DnsSdServiceResponseListener servListener = new WifiP2pManager.DnsSdServiceResponseListener() {

        @Override
        public void onDnsSdServiceAvailable(String instanceName,
                                            String registrationType, WifiP2pDevice srcDevice) {

            // A service has been discovered. Is this our app?

            if (instanceName.equalsIgnoreCase(SERVICE_INSTANCE)) {

                // update the UI and add the item the discovered
                // device.

                WiFiP2pService service = new WiFiP2pService();
                service.device = srcDevice;
                service.instanceName = instanceName;
                service.serviceRegistrationType = registrationType;


                Log.d(TAG, "onBonjourServiceAvailable "
                        + instanceName);

                Toast.makeText(getApplicationContext(), service.device + " is around ", Toast.LENGTH_LONG).show();
                Toast.makeText(getApplicationContext(), instanceName + " is the instance name ", Toast.LENGTH_LONG).show();


            }

        }
    };




    manager.setDnsSdResponseListeners(channel, servListener, txtListener);


}

public void initiateDiscovery(){
    // After attaching listeners, create a service request and initiate
    // discovery.
    serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
    manager.addServiceRequest(channel, serviceRequest,
            new WifiP2pManager.ActionListener() {

                @Override
                public void onSuccess() {
                    appendStatus("Added service discovery request");
                }

                @Override
                public void onFailure(int arg0) {
                    appendStatus("Failed adding service discovery request "+arg0);
                }
            });

    manager.discoverServices(channel, new WifiP2pManager.ActionListener() {

        @Override
        public void onSuccess() {
            appendStatus("Service discovery initiated");
        }

        @Override
        public void onFailure(int arg0) {
            appendStatus("Service discovery failed "+arg0);

        }
    });

}

public void appendStatus(String status) {
    Log.d("Status" + "\n", status);
}

@Override
public void onDestroy() {
    Toast.makeText(this, "service onDestroy", Toast.LENGTH_LONG).show();
    mHandler.removeCallbacksAndMessages(null);
}
}

I've also read this: Sending data in Android WiFi Direct service discovery instead of connecting

While, I understand the unreliability of using multicast-DNS discovery is essentially meant for service discovery and not messaging scenarios. In my case, the record value doesn't change very often. But, I need to ensure the advertisement and discovery and multiple peers happens without an erratic behavior as described.

Any assistance with this is much appreciated!

Thanks!

Arnab

Community
  • 1
  • 1
Arnab Saha
  • 511
  • 6
  • 15
  • Have you confirmed the behavior: "Since, an advertised service of a peer once received doesn't reappear if that peer goes away and comes back in range", I have seen services being discovered multiple times, even if they don't go away. Other observations are basically caused by really unreliable implementation for the API, as far as I know, there's not much you could do to get it any better. – Dr.Jukka Jan 08 '16 at 07:48
  • Hey @Dr.Jukka, Thanks for the response. Have you confirmed the behavior: "Since, an advertised service of a peer once received doesn't reappear if that peer goes away and comes back in range". It does discover the service again, but there's some latency or erratic behavior at times. I worked on another implementation using this wrapper https://github.com/markrjr/Salut and extending it to be used in a Service. Fairly, better performance with this, but most of the other observations do show up at times dude to the API implementation l believe, like you mentioned. – Arnab Saha Jan 11 '16 at 06:02
  • Hi @Dr.Jukka, I happened to follow you're github, and learnt about thaliproject and you're contributions towards it. http://thaliproject.org/Alumni/ which mentions figuring out that wifi direct isn't a great option on android. The API limitations on the discovery part is something i've realized too, and was looking at https://github.com/thaliproject/BTConApp. Wanted to check with you on your views on the option of using wifi-direct connection to transfer data instead of bluetooth or ble? Were there any known limitations/drawbacks with wifi-direct connections too in you're learning? – Arnab Saha Jan 19 '16 at 06:23
  • Basically, with Thali they needed connection establishment without user interaction, thus they opted for insecure bluetooth on android. The Wifi direct connection would allow native IP based communications with higher data rate, but indeed it also requires user interaction on initial "Pairing" – Dr.Jukka Jan 19 '16 at 08:00
  • Thanks for the help! I'm looking at a connection establishment without user interaction as well. And have been trying to implement a similar approach as with the BTConApp library. But, of what I've noticed there are multiple connections done from a single device to the recipient device. In cases where I want to discover some device info between e.g. 3 devices, ideally in the best case, a single device would make 2 connections to the other two devices when sending data. Making a total of 6 connections. So, n^n-1 connections. Could the connections be made bi-directional to optimize? – Arnab Saha Jan 26 '16 at 15:33
  • Basically once you have the pipline established, you can indeed sand data both ways. Anyway, do note that I have not checked any codes with Marshmallow, thus there might be some general issues with communication on that OS versions which I'm not aware of. – Dr.Jukka Jan 26 '16 at 19:34
  • Thanks a lot! was able to send data both way by passing them in sayHi() and sayAck() methods Also, was able to get it working on marshmallow as well. Follow scenario: Test Scenario: Devices: Moto E (1st gen) (Custom ROM 5.1) Nexus 4 (Stock 5.1.1) Nexus 6 (Stock 5.1) Nexus 7 (Custom ROM 6.0) Results: Moto E, Nexus 4 and Nexus 6, these 3 devices discovered each other under a single connection in under ~1.40 minutes Nexus 7 (Marshmallow) came in after all the others were discovered, and discovered the other 3 devices. And everyone finished seeing each other in around 3.3minutes – Arnab Saha Jan 28 '16 at 12:43
  • Hi @Dr.Jukka, had another quick question on the insecure bt connection. Is there a way to increase the range with this? Like how RSSI/TX power level in BLE could be tweaked for a longer range? – Arnab Saha Jan 29 '16 at 11:19

0 Answers0