The tricky bit is that sending a broadcast packet that includes the mobile network will silently fail, so you need to restrict yourself to the wifi network. Since the Android emulator doesn't support wifi, you have to test this all on a real device.

Getting the Broadcast Address

You need to access the wifi manager to get the DHCP info and construct a broadcast address from that:

InetAddress getBroadcastAddress() throws IOException {
    WifiManager wifi = mContext.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    // handle null somehow

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}

Sending and Receiving UDP Broadcast Packets

Having constructed the broadcast address, things work as normal. The following code would send the string data over broadcast and then wait for a response:

DatagramSocket socket = new DatagramSocket(PORT);
socket.setBroadcast(true);
DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
    getBroadcastAddress(), DISCOVERY_PORT);
socket.send(packet);

byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);

Another example

WifiManager wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
MulticastLock lock = wifi.createMulticastLock("dk.aboaya.pingpong");
lock.acquire();
serverSocket = new DatagramSocket(19876);
serverSocket.setSoTimeout(15000); //15 sec wait for the client to connect
byte[] data = new byte[UDPBatPositionUpdater.secretWord.length()]; 
DatagramPacket packet = new DatagramPacket(data, data.length);
serverSocket.receive(packet);
lock.release();
String s = new String(packet.getData());
System.out.println(s);

Discoverer.java

by Andrew Chatham (thanks!)

package com.andrewchatham;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import android.net.DhcpInfo;
import android.net.wifi.WifiManager;
import android.util.Log;

//import android.util.Log;

/*
 * This class tries to send a broadcast UDP packet over your wifi network to discover the boxee service. 
 */

public class Discoverer extends Thread {
  private static final String TAG = "Discovery";
  private static final String REMOTE_KEY = "b0xeeRem0tE!";
  private static final int DISCOVERY_PORT = 2562;
  private static final int TIMEOUT_MS = 500;

  // TODO: Vary the challenge, or it's not much of a challenge :)
  private static final String mChallenge = "myvoice";
  private WifiManager mWifi;

  interface DiscoveryReceiver {
    void addAnnouncedServers(InetAddress[] host, int port[]);
  }

  Discoverer(WifiManager wifi) {
    mWifi = wifi;
  }

  public void run() {
    try {
      DatagramSocket socket = new DatagramSocket(DISCOVERY_PORT);
      socket.setBroadcast(true);
      socket.setSoTimeout(TIMEOUT_MS);

      sendDiscoveryRequest(socket);
      listenForResponses(socket);
    } catch (IOException e) {
      Log.e(TAG, "Could not send discovery request", e);
    }
  }

  /**
   * Send a broadcast UDP packet containing a request for boxee services to
   * announce themselves.
   * 
   * @throws IOException
   */
  private void sendDiscoveryRequest(DatagramSocket socket) throws IOException {
    String data = String
        .format(
            "<bdp1 cmd=\"discover\" application=\"iphone_remote\" challenge=\"%s\" signature=\"%s\"/>",
            mChallenge, getSignature(mChallenge));
    Log.d(TAG, "Sending data " + data);

    DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(),
        getBroadcastAddress(), DISCOVERY_PORT);
    socket.send(packet);
  }

  /**
   * Calculate the broadcast IP we need to send the packet along. If we send it
   * to 255.255.255.255, it never gets sent. I guess this has something to do
   * with the mobile network not wanting to do broadcast.
   */
  private InetAddress getBroadcastAddress() throws IOException {
    DhcpInfo dhcp = mWifi.getDhcpInfo();
    if (dhcp == null) {
      Log.d(TAG, "Could not get dhcp info");
      return null;
    }

    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
      quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
  }

  /**
   * Listen on socket for responses, timing out after TIMEOUT_MS
   * 
   * @param socket
   *          socket on which the announcement request was sent
   * @throws IOException
   */
  private void listenForResponses(DatagramSocket socket) throws IOException {
    byte[] buf = new byte[1024];
    try {
      while (true) {
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);
        String s = new String(packet.getData(), 0, packet.getLength());
        Log.d(TAG, "Received response " + s);
      }
    } catch (SocketTimeoutException e) {
      Log.d(TAG, "Receive timed out");
    }
  }

  /**
   * Calculate the signature we need to send with the request. It is a string
   * containing the hex md5sum of the challenge and REMOTE_KEY.
   * 
   * @return signature string
   */
  private String getSignature(String challenge) {
    MessageDigest digest;
    byte[] md5sum = null;
    try {
      digest = java.security.MessageDigest.getInstance("MD5");
      digest.update(challenge.getBytes());
      digest.update(REMOTE_KEY.getBytes());
      md5sum = digest.digest();
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }

    StringBuffer hexString = new StringBuffer();
    for (int k = 0; k < md5sum.length; ++k) {
      String s = Integer.toHexString((int) md5sum[k] & 0xFF);
      if (s.length() == 1)
        hexString.append('0');
      hexString.append(s);
    }
    return hexString.toString();
  }

  public static void main(String[] args) {
    new Discoverer(null).start();
    while (true) {
    }
  }
}

AndroidUDPBroadcast (last edited 2014-01-01 12:28:56 by jaromil)