Skip to content

Commit

Permalink
Discovery using Wifi P2P implemented.
Browse files Browse the repository at this point in the history
Need to do more extensive testing to make sure that it holds up over time.
  • Loading branch information
JayThomason committed May 26, 2014
1 parent e56ef74 commit a8ea911
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 179 deletions.
8 changes: 8 additions & 0 deletions src/com/stanford/tutti/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ public void ping(AsyncHttpResponseHandler responseHandler) {
client.get(url, responseHandler);
}

/*
* Sengs a jamActive message to the client to check if it is hosting a jam.
*/
public void isMaster(AsyncHttpResponseHandler responseHandler) {
String url = getUrl("/isMaster", "");
client.get(url, responseHandler);
}

private String getUrl(String path, String query) {
return "http://" + ipAddress + ":" + port + path + query;
}
Expand Down
93 changes: 66 additions & 27 deletions src/com/stanford/tutti/DiscoveryManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import java.util.HashMap;
import java.util.Map;

import org.apache.http.Header;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;

import android.content.Context;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pManager;
Expand All @@ -12,11 +17,22 @@
import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest;
import android.os.Handler;
import android.os.Message;

public class DiscoveryManager {
private Globals g;
private final WifiP2pManager mManager;
private final Channel mChannel;

private static final String DNS_SERVICE_NAME = "_tutti_jam";
private static final String DNS_PROTOCOL_NAME = "_presence";
private static final String DNS_TRANSPORT_NAME = "_tcp";
private static final String DNS_FULL_NAME = DNS_SERVICE_NAME + "."
+ DNS_PROTOCOL_NAME + "." + DNS_TRANSPORT_NAME + ".local.";

private static final String IP_PORT_KEY = "ip_port";
private static final String JAM_NAME_KEY = "jam_name";


public DiscoveryManager(Globals g) {
Expand All @@ -32,18 +48,19 @@ public DiscoveryManager(Globals g) {
*/
public void makeJamDiscoverable() {
Map<String, String> record = new HashMap<String, String>();
int port = g.getServerPort();
record.put("ipPort", g.getIpAddr() + ":" + String.valueOf(port));
record.put("TuttiJam", "true");
record.put("JamName", g.jam.getJamName());


String ipPortValue = g.getIpAddr() + ":" + String.valueOf(g.getServerPort());
String jamNameValue = g.jam.getJamName();
System.out.println("jam name: " + jamNameValue + " ip port: " + ipPortValue);
record.put(IP_PORT_KEY, ipPortValue);
record.put(JAM_NAME_KEY, jamNameValue);

// Service information. Pass it an instance name, service type
// _protocol._transportlayer , and the map containing
// information other devices will want once they connect to this one.
WifiP2pDnsSdServiceInfo serviceInfo =
WifiP2pDnsSdServiceInfo.newInstance("_tutti_jam", "_presence._tcp", record);
WifiP2pDnsSdServiceInfo.newInstance(DNS_SERVICE_NAME,
DNS_PROTOCOL_NAME + "." + DNS_TRANSPORT_NAME, record);
System.out.println("DNS_FULL_NAME: " + DNS_FULL_NAME);

// Add the local service, sending the service info, network channel,
// and listener that will be used to indicate success or failure of
Expand All @@ -57,7 +74,6 @@ public void onSuccess() {
@Override
public void onFailure(int arg0) {
System.out.println("failed to create local service :(((");

// What to do if we can't broadcast? -- display message to user saying unable to host jam...
}
});
Expand All @@ -69,17 +85,13 @@ public void onFailure(int arg0) {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
@Override
public void onDnsSdTxtRecordAvailable(String arg0,
Map<String, String> arg1, WifiP2pDevice arg2) {
System.out.println("DnsSdTxtRecord available -" + arg1.toString());
}
Map<String, String> arg1, WifiP2pDevice arg2) {}
};

DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
@Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType,
WifiP2pDevice resourceType) {
System.out.println("device: " + resourceType);
}
WifiP2pDevice resourceType) {}
};

mManager.setDnsSdResponseListeners(mChannel, servListener, txtListener);
Expand All @@ -97,11 +109,11 @@ public void onSuccess() {
@Override
public void onFailure(int code) {
System.out.println("failed to add service request for discovery :(");
// what to do here? -- display message to user unable to broadcast jam?
}
});

mManager.discoverServices(mChannel, new ActionListener() {

@Override
public void onSuccess() {
System.out.println("success discover services...");
Expand Down Expand Up @@ -129,27 +141,54 @@ public void onFailure(int arg0) {
});
}

public void startJamDiscovery() {

// map from name to ip:port
final HashMap<String, String> jams = new HashMap<String, String>();

public void startJamDiscovery () {
DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {

@Override
public void onDnsSdTxtRecordAvailable(String arg0,
Map<String, String> arg1, WifiP2pDevice arg2) {
System.out.println("DnsSdTxtRecord available -" + arg1.toString());
// should ping /exists endpoint here before adding to list
jams.put(arg1.get("name"), arg1.get("ipAddr") + arg1.get("port"));
public void onDnsSdTxtRecordAvailable(String dnsName,
final Map<String, String> record, WifiP2pDevice arg2) {

if (dnsName.equals(DNS_FULL_NAME)) {
String ipPort = record.get(IP_PORT_KEY);
if (ipPort == null) {
return;
}
String split[] = ipPort.split(":");
final String ip = split[0];
final String port = split[1];
final String jamName = record.get(JAM_NAME_KEY);
if (ip == null || port == null || jamName == null) {
return;
}
Client client = new Client(g, null, ip, Integer.parseInt(port));
client.isMaster(new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
System.out.println("response from jam active...: " + statusCode);
if (statusCode == 200) { // jam is hosted here
System.out.println("discovered jam: " + jamName);
if (g.joinJamHandler != null) {
Message msg = g.joinJamHandler.obtainMessage(1);
msg.obj = jamName + ":" + ip + ":" + port;
g.joinJamHandler.sendMessage(msg);
}
}
}

@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
System.out.println("statusCode: " + statusCode);
System.out.println("error: " + error);
}
});
System.out.println("checking if jam is active...");
}
}
};

DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
@Override
public void onDnsSdServiceAvailable(String instanceName, String registrationType,
WifiP2pDevice resourceType) {
System.out.println("device: " + resourceType);
}
};

Expand Down
145 changes: 61 additions & 84 deletions src/com/stanford/tutti/JoinJamActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import org.apache.http.Header;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;

import android.app.Activity;
Expand All @@ -24,8 +23,6 @@
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.net.Uri;
import android.net.Uri.Builder;

public class JoinJamActivity extends Activity {
private ListView jamListView;
Expand All @@ -34,6 +31,8 @@ public class JoinJamActivity extends Activity {
private final String path = "/discoverJams";
private Map<String, String> ipMap;
private Map<String, String> requestedMap;
private ArrayList<String> nameList = new ArrayList<String>();


@Override
protected void onCreate(Bundle savedInstanceState) {
Expand All @@ -46,7 +45,7 @@ protected void onCreate(Bundle savedInstanceState) {
requestedMap = new HashMap<String, String>();

server = new Server(g);

try {
server.start();
g.db.updatePortForLocalSongs();
Expand All @@ -60,71 +59,30 @@ protected void onCreate(Bundle savedInstanceState) {
configureJamListView();
g.discoveryManager.startJamDiscovery();
}

/*
private void requestLocalJams() {
String serverHostname = getString(R.string.ec2_server);
Builder builder = Uri.parse("http://" + serverHostname).buildUpon();
builder.path(path);
builder.appendQueryParameter("ssid", g.getWifiSSID());
builder.appendQueryParameter("gateway", g.getGatewayIpAddr());
AsyncHttpClient httpClient = new AsyncHttpClient();
httpClient.get(builder.build().toString(), new AsyncHttpResponseHandler() {
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
System.out.println("Server response: " + statusCode);
if (statusCode == 200) {
String jamListStr = new String(responseBody);
if (jamListStr.length() <= 1)
return;
// the server returns a list of triples delimited by newlines
// each triple is consists of a name, port number, and ip address, delimited by spaces
final ArrayList<String> nameList = new ArrayList<String>();
String nameIpPairList[] = jamListStr.split("\n");
for (String str : nameIpPairList) {
String nameIpPair[] = str.split(" ");
nameList.add(nameIpPair[0]);
ipMap.put(nameIpPair[0], nameIpPair[2] + ":" + nameIpPair[1]);
}
ListView jamListView = (ListView) findViewById(R.id.jamListView);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
JoinJamActivity.this, android.R.layout.simple_list_item_1,
nameList);
jamListView.setAdapter(arrayAdapter);
}
else {
System.out.println("Unable to connect to server.");
System.out.println("Response: " + responseBody);
}
}
});
}
*/

private void configureJamListView() {
jamListView = (ListView) this.findViewById(R.id.jamListView);
jamListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {

Globals g = (Globals) getApplication();
String jamName = ((TextView) arg1).getText().toString();
final String ipPortString = ipMap.get(jamName);

if (requestedMap.containsKey(ipPortString)) {
Toast.makeText(g, "Already sent request to join " + jamName, Toast.LENGTH_SHORT).show();
return;
} else {
requestedMap.put(ipPortString, "true");
Toast.makeText(g, "Requested to join " + jamName, Toast.LENGTH_SHORT).show();
}

String split[] = ipPortString.split(":");
String ip = split[0];
String port = split[1];

final Client masterClient = new Client(g, "", ip, Integer.parseInt(port));
masterClient.requestJoinJam(g.getUsername(), g.getServerPort(), new AsyncHttpResponseHandler() {
@Override
Expand All @@ -141,44 +99,63 @@ private void setupHandler() {
g.joinJamHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String message = (String)msg.obj;
if (message != null) {
String[] tokens = message.split("//");
final String ipAddr = tokens[1];
if (tokens[0].equals("ACCEPTED")) {
System.out.println("tokens: ");
for (String str : tokens) System.out.println(str);
final String username = tokens[1];
final int masterPort = Integer.parseInt(tokens[2]);
final String jamName = tokens[3];

g.jam.setJamName(jamName);

g.jam.setMaster(false);
g.jam.setMasterIp(ipAddr);
g.jam.setIPUsername(ipAddr, username);
g.jam.setMasterPort(masterPort);

Client masterClient = new Client(g, username, ipAddr, masterPort);
g.jam.addClient(masterClient);

try {
g.localLoaderThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
if (msg.what == 0) { // accepted or rejected
String message = (String) msg.obj;
if (message != null) {
String[] tokens = message.split("//");
final String ipAddr = tokens[1];
if (tokens[0].equals("ACCEPTED")) {
System.out.println("tokens: ");
for (String str : tokens) System.out.println(str);
final String username = tokens[1];
final int masterPort = Integer.parseInt(tokens[2]);
final String jamName = tokens[3];

g.jam.setJamName(jamName);

g.jam.setMaster(false);
g.jam.setMasterIp(ipAddr);
g.jam.setIPUsername(ipAddr, username);
g.jam.setMasterPort(masterPort);

Client masterClient = new Client(g, username, ipAddr, masterPort);
g.jam.addClient(masterClient);

try {
g.localLoaderThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

Thread getLibraryThread = new RequestLibraryThread(g, ipAddr, masterPort);
getLibraryThread.start();

Intent intent = new Intent(JoinJamActivity.this, BrowseMusicActivity.class);
startActivity(intent);
finish();
}
else if (tokens[0].equals("REJECTED")) {
requestedMap.remove(ipAddr);
}

Thread getLibraryThread = new RequestLibraryThread(g, ipAddr, masterPort);
getLibraryThread.start();

Intent intent = new Intent(JoinJamActivity.this, BrowseMusicActivity.class);
startActivity(intent);
finish();
}
else if (tokens[0].equals("REJECTED")) {
requestedMap.remove(ipAddr);
}
}
else if (msg.what == 1) { // new jam discovered -> refresh list
String message = (String) msg.obj;
// message has format jamName:ipAdddr:port
String split[] = message.split(":");
String jamName = split[0];
String ipAddr = split[1];
String port = split[2];
ipMap.put(jamName, ipAddr + ":" + port);

// update list
nameList.add(jamName);
ListView jamListView = (ListView) findViewById(R.id.jamListView);
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
JoinJamActivity.this, android.R.layout.simple_list_item_1,
nameList);
jamListView.setAdapter(arrayAdapter);
}
}
};
}
Expand Down
Loading

1 comment on commit a8ea911

@JBB
Copy link

@JBB JBB commented on a8ea911 May 27, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bravo!

Please sign in to comment.