Search code examples
androidxamarin.androidwifi-directgoogle-nearbywifip2p

WiFiP2PManager, OnPeersAvailable, DeviceList.Count zero - always


I've been struggling for days now with 2 Samsung S8 phones running a Xamarin test app, with the bear minimum of WiFiP2PManager code to discover peers. I've poured through community forums and documentation - it can't be this hard, there must be something I'm missing.

I've checked the common mistakes:

  • App permission set (ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, CHANGE_NETWORK_STATE, INTERNET)
  • Is WiFi direct turned on
  • Are both phone's in discovery mode (or not, same result)

Still, OnPeersAvailable is called with an empty collection for DeviceList.

The sequence of calls, in the debug window, turns out to be:

  1. WifiP2pManager.Initialize
  2. MainActivity.RegisterReceiver
  3. WifiP2pManager.DiscoverPeers
  4. WifiP2pManager.RequestPeers
  5. OnPeersAvailable (DeviceList.Count == 0)

Here's my code, maybe you can see something?

using System;

using Android.App;
using Android.OS;
using Android.Support.V7.App;
using Android.Runtime;
using Android.Net.Wifi.P2p;
using Android.Content;
using static Android.Net.Wifi.P2p.WifiP2pManager;

namespace WiFiDirectTest
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
    public class MainActivity : AppCompatActivity, IChannelListener, IPeerListListener
    {
        private WifiP2pManager manager;
        Channel channel;
        MyBroadcastReceiver receiver;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            SetContentView(Resource.Layout.activity_main);

            System.Diagnostics.Debug.WriteLine($"Wifi - initialize");
            manager = (WifiP2pManager)GetSystemService(WifiP2pService);
            channel = manager.Initialize(this, MainLooper, null);
        }


        protected override void OnResume()
        {
            base.OnResume();

            System.Diagnostics.Debug.WriteLine($"Wifi - register receiver");
            receiver = new MyBroadcastReceiver(manager, channel, this);
            receiver.Register(this);

            System.Diagnostics.Debug.WriteLine($"Wifi - discover peers");
            manager.DiscoverPeers(channel, new MyActionListner());
        }


        protected override void OnPause()
        {
            base.OnPause();

            System.Diagnostics.Debug.WriteLine($"Wifi - unregister receiver");
            receiver.Unregister(this);
            receiver = null;
        }

        public void OnPeersAvailable(WifiP2pDeviceList peers)
        {
            var count = peers.DeviceList.Count;
            System.Diagnostics.Debug.WriteLine($"Wifi - peers available: {count}");
        }

        public void OnChannelDisconnected()
        {
            System.Diagnostics.Debug.WriteLine("OnChannelDisconnected");
        }

        private class MyActionListner : Java.Lang.Object, WifiP2pManager.IActionListener
        {
            public MyActionListner()
            {
            }

            public void OnFailure(WifiP2pFailureReason reason)
            {
                // breakpoint placed here, never reached.
            }

            public void OnSuccess()
            {
            }
        }
    }


    public class MyBroadcastReceiver : BroadcastReceiver
    {
        WifiP2pManager manager;
        WifiP2pManager.Channel channel;
        MainActivity activity;

        public MyBroadcastReceiver(WifiP2pManager manager, Channel channel, MainActivity activity)
        {
            this.manager = manager;
            this.channel = channel;
            this.activity = activity;
        }

        public void Register(MainActivity context)
        {
            var intentFilter = new IntentFilter();

            intentFilter.AddAction(WifiP2pManager.WifiP2pStateChangedAction);
            intentFilter.AddAction(WifiP2pManager.WifiP2pPeersChangedAction);
            intentFilter.AddAction(WifiP2pManager.WifiP2pConnectionChangedAction);
            intentFilter.AddAction(WifiP2pManager.WifiP2pThisDeviceChangedAction);

            context.RegisterReceiver(this, intentFilter);
        }

        public void Unregister(MainActivity context)
        {
            context.UnregisterReceiver(this);
        }

        public override void OnReceive(Context context, Intent intent)
        {
            var action = intent.Action;

            if (WifiP2pManager.WifiP2pStateChangedAction.Equals(action))
            {
                 System.Diagnostics.Debug.WriteLine("WifiP2pStateChangedAction");
            }
            else if (WifiP2pManager.WifiP2pPeersChangedAction.Equals(action))
            {
                System.Diagnostics.Debug.WriteLine($"Wifi - request peers");
                manager.RequestPeers(channel, activity);
            }
            else if (WifiP2pManager.WifiP2pConnectionChangedAction.Equals(action))
            {
                System.Diagnostics.Debug.WriteLine("WifiP2pConnectionChangedAction");
            }
            else if (WifiP2pManager.WifiP2pThisDeviceChangedAction.Equals(action))
            {
                System.Diagnostics.Debug.WriteLine("WifiP2pThisDeviceChangedAction");
            }
        }
    }
}

Any ideas?

-John


Solution

  • SOLVED!

    Turns out the WiFiDirectSample I was using was built with an old API, prior to the new runtime permissions features in v6.0 (API 23). When I mimicked the WiFiP2PManager orchestration in my new app I was building for v9 (API 28).

    With that I needed to explicitly request permissions at runtime, despite them being listed in the Android Manifest. None of the documentation I read (over and over) mentioned to request permissions.

            string[] permissions = {
                Manifest.Permission.AccessFineLocation,
                Manifest.Permission.AccessCoarseLocation,
                Manifest.Permission.AccessWifiState,
                Manifest.Permission.ChangeWifiState,
                Manifest.Permission.Internet
            };
            RequestPermissions(permissions, 0);
    

    So I just added this code to my OnCreate() and I now get peers back in my OnPeersAvailable callback!