Android wifi p2p peers visibility

3.9k views Asked by At

I'm trying to connect 2 devices with wifi direct, let's say device A and device B with android > 4.1. If I'm on device A and I press the button to search for other devices it doesn't act always the same way.

For example if I press the search button on device A, it doesn't find anything until I press the search button on the device B too, even if the app is going on both. So the device B is not visible until it doesn't start searching for other devices too.

Other times if I search for devices with device A, it finds device B even if the app was closed recently on device B and if I try to connect to device B it works. The problem is that I want to establish a connection only if the app is running on both devices.

Sometimes when device A finds out device B and it tries to connect to it, it doesn't work until device B starts to find devices. So when I start the searching on device B it receives the connection request from A, but nothing until then.

Some other times, after pressing the search button on device A, it shows some devices that hasn't the wifi enabled at that moment or that are out of range.

Here's my code:

MainActivity.java

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.ActionListener;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
import android.net.wifi.p2p.WifiP2pManager.PeerListListener;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.Toast;


public class MainActivity extends Activity implements OnItemClickListener, PeerListListener {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private BroadcastReceiver mReceiver;
    private IntentFilter mIntentFilter;
    private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>();
    private List<WifiP2pDevice> peersConnect = new ArrayList<WifiP2pDevice>();
    private ArrayList<String> peersName = new ArrayList<String>();
    private ListView list;
    private Button bSearch;
    private Button bConnect;
    private Button bDisconnect;
    private int nSelectedDevices = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mManager.initialize(this, getMainLooper(), null);

        try {
            Class<?> wifiManager = Class
                    .forName("android.net.wifi.p2p.WifiP2pManager");

            Method method = wifiManager
                    .getMethod(
                            "enableP2p",
                            new Class[] { android.net.wifi.p2p.WifiP2pManager.Channel.class });

            method.invoke(mManager, mChannel);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);

        bSearch = (Button) this.findViewById(R.id.searcher);
        bSearch.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                list.setVisibility(ListView.INVISIBLE);
                bConnect.setVisibility(View.INVISIBLE);
                bDisconnect.setVisibility(View.INVISIBLE);
                nSelectedDevices = 0;
                peersConnect.clear();
                peers.clear();
                peersName.clear();
                searchDevices();        
            }
        });

        bConnect = (Button) this.findViewById(R.id.connecter);
        bConnect.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                bDisconnect.setVisibility(View.VISIBLE);
                connectDevices();       
                bConnect.setVisibility(View.INVISIBLE);
                nSelectedDevices = 0;
                peersConnect.clear();
            }
        });

        bDisconnect = (Button) this.findViewById(R.id.disconnecter);
        bDisconnect.setOnClickListener(new OnClickListener() {
            public void onClick (View v) {
                disconnectDevices();
                peersConnect.clear();
                bDisconnect.setVisibility(View.INVISIBLE);
            }
        });

        list = (ListView) this.findViewById(R.id.list);
        list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
         //--   text filtering
        list.setTextFilterEnabled(true);


    }

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
        super.onResume();
        mReceiver = new WifiReceiver(mManager, mChannel, this);
        registerReceiver(mReceiver, mIntentFilter);
    }

    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        finish();
    }



    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        CheckedTextView item = (CheckedTextView) v;
        if(item.isChecked()) {
            nSelectedDevices++;
            peersConnect.add(peers.get(position));
        }
        else {
            nSelectedDevices--;
            peersConnect.remove(peers.get(position));
        }
        if(nSelectedDevices == 1)
            bConnect.setVisibility(View.VISIBLE);
        else if(nSelectedDevices == 0)
            bConnect.setVisibility(View.INVISIBLE);
    }

    @Override
    public void onPeersAvailable(WifiP2pDeviceList peerList) {

        // Out with the old, in with the new.
        peers.clear();
        peers.addAll(peerList.getDeviceList());
        getDeviceName();
        list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, peersName));
        list.setOnItemClickListener(this);
        list.setVisibility(ListView.VISIBLE);
        // If an AdapterView is backed by this data, notify it
        // of the change.  For instance, if you have a ListView of available
        // peers, trigger an update.
        //((ListAdapter) getListAdapter()).notifyDataSetChanged();
        if (peers.size() == 0) {
            return;
        }
    }

    private void searchDevices() {
        mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                //Toast.makeText(MainActivity.this, "Inizio ricerca...", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(int reasonCode) {
                //Toast.makeText(MainActivity.this, "Ricerca fallita!", Toast.LENGTH_SHORT).show();
            }
        });

    }

    private void connectDevices() {
        for(int i = 0; i < peersConnect.size(); i++) {

            // Picking the first device found on the network.
            WifiP2pDevice device = peersConnect.get(i);

            WifiP2pConfig config = new WifiP2pConfig();
            config.deviceAddress = device.deviceAddress;
            config.wps.setup = WpsInfo.PBC;

            mManager.connect(mChannel, config, new ActionListener() {

                @Override
                public void onSuccess() {
                    // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
                    Toast.makeText(MainActivity.this, "Connection requested...", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(int reason) {
                    Toast.makeText(MainActivity.this, "Connect failed. Retry.", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    public void disconnectDevices() {
        if (mManager != null && mChannel != null) {
            mManager.requestGroupInfo(mChannel, new GroupInfoListener() {
                @Override
                public void onGroupInfoAvailable(WifiP2pGroup group) {
                    if (group != null && mManager != null && mChannel != null && group.isGroupOwner()) {
                        mManager.removeGroup(mChannel, new ActionListener() {
                            @Override
                            public void onSuccess() {

                            }

                            @Override
                            public void onFailure(int reason) {

                            }
                        });
                    }
                }
            });
        }
    }


    private void getDeviceName() {
        int i = 0;
        peersName.clear();
        while(i < peers.size()) {
            peersName.add(peers.get(i).deviceName);
            i++;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

WifiReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.widget.Toast;

public class WifiReceiver extends BroadcastReceiver {

    private WifiP2pManager mManager;
    private Channel mChannel;
    private MainActivity mActivity;

    public WifiReceiver(WifiP2pManager manager, Channel channel, MainActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // check if wifi is enabled/disabled
            System.out.println("Connection changed");
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                //mActivity.setIsWifiP2pEnabled(true);
            } else {
                //mActivity.setIsWifiP2pEnabled(false);
            }
        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // Call WifiP2pManager.requestPeers() to get a list of current peers
            // request available peers from the wifi p2p manager. This is an
            // asynchronous call and the calling activity is notified with a
            // callback on PeerListListener.onPeersAvailable()
            System.out.println("Peers changed");
            if (mManager != null) {
                mManager.requestPeers(mChannel, mActivity);

            }
        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // Respond to new connection or disconnections
            System.out.println("Connection changed");
        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi state changing
            System.out.println("This device changed");
        }
        else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
            // Respond to this device's wifi state changing
            System.out.println("Search peers");
        }
    }

}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:id="@+id/linearlayout1" >
        <Button
            android:id="@+id/searcher"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_send" />

        <Button
            android:id="@+id/connecter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_connect"
            android:visibility="invisible" />

        <Button
            android:id="@+id/disconnecter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/button_disconnect"
            android:visibility="invisible" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/linearlayout1" >

        <ListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="380dp"
            android:layout_alignParentLeft="true"
            android:visibility="invisible" >
        </ListView>
    </LinearLayout>
"

</RelativeLayout>

I don't understand how works mManager.discoverPeers() and mManager.requestPeers().

Thank you for your attention!!

2

There are 2 answers

0
Dr.Jukka On BEST ANSWER

Actually you have valid observations, and that's the way the API actually works:

  1. So the device B is not visible until it doesn't start searching for other devices too

That's the way the API appears to work. Basically in order to be visible to other devices, the WiFi interface needs to be powered up and active, and so far I have seen it happening, when the device is either doing active discovery, or having active connection.

  1. The problem is that I want to establish a connection only if the app is running on both devices

Basically, I suppose best you can do, is to advertise the service while the app is running, and discover the service when connecting. This is Not 100% accurate alone, thus, you could also implement connection & handshake from client to Group owner to fully check that both ends are ok and present. If the handshake fails, then do disconnect.

  1. So when I start the searching on device B it receives the connection request from A, but nothing until then.

This one I don't have direct answer, Basically could be that something is not right there. I do know that if the device B is not active it should not be visible, and if the device B is not in actual Discovered peers list of the API, then all connection attempts to it will fail, so could be some combination of issues what happens here really.

  1. it shows some devices that hasn't the wifi enabled at that moment or that are out of range.

Supposing that the API does appear to cache some results some times, though have to admit that I have not seen this issue, I usually get peer changed events when I turn off a nearby device, and then Service discovery has not been giving any results on any device that would not really be there, so do try the service discovery always after you get Peers Changed event.

0
Cililing On

I had similar problem. Some of my devices couldn't find any other devices, but others could have connect with them.

The problem was that I haven't request location permission.

Manifest.permission.ACCESS_COARSE_LOCATION;