Android build is not receiving UDP broadcasts

I’m using UdpClient from System.Net.Sockets to listen to UDP broadcasts. This seems to work fine on OS X and Windows, however, on Android, the machine only sees itself. The HTC I’m testing this on is in the same wireless LAN as all the other machines and the other machines see the broadcast of the Android. It’s just the Android that doesn’t see anybody else.

This is the code that listens. It obviously works for itself (i.e. it receives its own broadcasts) but not anybody elses. Am I missing some peculiar socket options that need to be set on mobile devices to allow broadcast reception? Searched for that, but couldn’t find anything.

public void StartListeningForUDPBroadcast () {
	UdpClient udpClient = new UdpClient(portnum);
	udpClient.BeginReceive(new AsyncCallback(ReceiveDataCallback), udpClient);
}

// ReceiveDataCallback
private void ReceiveDataCallback(IAsyncResult ar) {
	string receiveBytes = "";
	UdpClient udp = (UdpClient)ar.AsyncState;
	IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, portnum);
	
	if (udp != null) {
    	byte[] receive = udp.EndReceive(ar, ref ipEndPoint);
		receiveBytes = Encoding.ASCII.GetString(receive);
    }
	
    Debug.Log("Received this: " + receivedBytes;
    // restart listener
udp.BeginReceive(new AsyncCallback(ReceiveDataCallback), udp);

}

The reason this doesn’t work is that Android by default doesn’t listen to broadcasts unless they’re directed at the device address itself. I’m not sure what part of that anyone then would consider broadcast, but anyhow… here’s how to get around this:

In a piece of Java code in the android SDK, acquire a multicast lock, and disable it when your app is done:

WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("mydebuginfo");
multicastLock.acquire();

Also add this manifest to your Plugins/Android/AndroidManifest.xml file:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

That’s the theory anyway. After putting the manifest there I’m now getting a build errror:

Error building Player: XmlException: ‘android’ is undeclared namespace. file:///…/Unity3D/Multiplayer Framework/Network/broadcast/Temp/StagingArea/AndroidManifest.xml Line 1, position 81.

And I’ve only just started to look into how to write a java plugin. Perhaps it could be done by accessing the Java classes directly from Unity?

I’m having the same problem with a Google Pixel 2 XL. I’ve added acquiring a multicastLock in Unity through Java calls:

public bool getMulticastLock(string lockTag)
{
	using (AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity"))
	{
		try
		{
			using (var wifiManager = activity.Call<AndroidJavaObject>("getSystemService", "wifi"))
			{
				multicastLock = wifiManager.Call<AndroidJavaObject>("createMulticastLock", lockTag);
				multicastLock.Call("acquire");
				bool isHeld = multicastLock.Call<bool>("isHeld");
				return isHeld;
			}
		}
		catch (Exception err)
		{
			Debug.Log(err.ToString ());
		}
	}
	return false;
}

And added the necessary permission in the manifest:

<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>

But this doesn’t seem to make a difference. Other Android devices receive the UDP broadcast packets fine, but the Pixel 2 XL doesn’t, only very occasionally.

@Ruud3DV, thanks for the tip, your code worked great. The one thing that I realized though after looking at the Logcat is that the Multicast Lock was automatically released after about 1.5 seconds after it was acquired through code. I then just put the code in a coroutine that starts again every second.

public class MultiCastLockPixel : MonoBehaviour {

    public float multicastDelay; // Set it to 1 or less in the editor
    [HideInInspector]
    public bool stopAcquiringLock = false;

    public void Start()
    {
        StartCoroutine(acquireMultiCastPeriodically());
    }

    private IEnumerator acquireMultiCastPeriodically()
    {
        while (!stopAcquiringLock)
        {
            getMulticastLock("debugMulticast");
            yield return new WaitForSeconds(multicastDelay);
        }
    }

    public bool getMulticastLock(string lockTag)
    {
        try
        {
            using (AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity"))
            {
            
                using (var wifiManager = activity.Call<AndroidJavaObject>("getSystemService", "wifi"))
                {
                    AndroidJavaObject multicastLock = wifiManager.Call<AndroidJavaObject>("createMulticastLock", lockTag);
                    multicastLock.Call("acquire");
                    bool isHeld = multicastLock.Call<bool>("isHeld");
                    return isHeld;
                }
            
            }
        }
        catch (Exception err)
        {
            Debug.Log(err.ToString());
        }
        return false;
    }
}

I spent some time with solving same problem. Most important what I found, android is not supporting async receive. I don’t know why. I ended up with separated background Thread, where I call standart Receive(). It works well. Also I used multicast instead of broadcast. Because I had more network adapters in PC.

You can take a look on it here MulticastDiscovery