diff --git a/.classpath b/.classpath index 7bc01d9a..e5f25532 100644 --- a/.classpath +++ b/.classpath @@ -5,5 +5,6 @@ + diff --git a/gen/com/limelight/R.java b/gen/com/limelight/R.java index 47c9ea76..3395dcbb 100644 --- a/gen/com/limelight/R.java +++ b/gen/com/limelight/R.java @@ -35,15 +35,11 @@ or to a theme attribute in the form "?[package:][type:]na public static final int ic_launcher=0x7f020000; } public static final class id { -<<<<<<< HEAD - public static final int mDNSResultView=0x7f080000; - public static final int surfaceView=0x7f080001; -======= - public static final int hostTextView=0x7f080002; + public static final int hostTextView=0x7f080003; + public static final int mDNSResultView=0x7f080002; public static final int pairButton=0x7f080000; public static final int statusButton=0x7f080001; - public static final int surfaceView=0x7f080003; ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d + public static final int surfaceView=0x7f080004; } public static final class layout { public static final int activity_connection=0x7f030000; diff --git a/libs/dnsjava-2.1.5.jar b/libs/dnsjava-2.1.6.jar similarity index 81% rename from libs/dnsjava-2.1.5.jar rename to libs/dnsjava-2.1.6.jar index e928fad9..6bd9b2ee 100644 Binary files a/libs/dnsjava-2.1.5.jar and b/libs/dnsjava-2.1.6.jar differ diff --git a/res/layout/activity_connection.xml b/res/layout/activity_connection.xml index 4ef82e8f..c7ad9d47 100644 --- a/res/layout/activity_connection.xml +++ b/res/layout/activity_connection.xml @@ -16,7 +16,7 @@ android:layout_centerHorizontal="true" android:text="Pair with PC"/> -<<<<<<< HEAD + -======= + ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d + diff --git a/src/com/limelight/Connection.java b/src/com/limelight/Connection.java index fd398ad3..5e80d252 100644 --- a/src/com/limelight/Connection.java +++ b/src/com/limelight/Connection.java @@ -1,13 +1,8 @@ package com.limelight; import java.io.IOException; -<<<<<<< HEAD -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -======= + import java.net.SocketException; ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d import org.xmlpull.v1.XmlPullParserException; @@ -34,15 +29,13 @@ public class Connection extends Activity { private static final String DEFAULT_HOST = "192.168.1.240"; public static final String HOST_KEY = "hostText"; -<<<<<<< HEAD + @Override public void onResume() { super.onResume(); } -======= ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d @Override public void onPause() { SharedPreferences.Editor editor = prefs.edit(); @@ -64,41 +57,29 @@ public class Connection extends Activity { Log.v("NvmDNS", "onCreate"); - try { NvmDNS dns = new NvmDNS(); dns.execute(); - } catch (IOException e2) { - // TODO Auto-generated catch block - e2.printStackTrace(); - } setContentView(R.layout.activity_connection); -<<<<<<< HEAD - // this.statusButton = (Button) findViewById(R.id.statusButton); - // this.hostText = (TextView) findViewById(R.id.hostTextView); -======= this.statusButton = (Button) findViewById(R.id.statusButton); this.pairButton = (Button) findViewById(R.id.pairButton); this.hostText = (TextView) findViewById(R.id.hostTextView); ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d + //prefs = getPreferences(0); //this.hostText.setText(prefs.getString(Connection.HOST_KEY, Connection.DEFAULT_HOST)); - /*this.statusButton.setOnClickListener(new OnClickListener() { + this.statusButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { Intent intent = new Intent(Connection.this, Game.class); intent.putExtra("host", Connection.this.hostText.getText().toString()); Connection.this.startActivity(intent); } -<<<<<<< HEAD - });*/ -======= }); this.pairButton.setOnClickListener(new OnClickListener() { @@ -154,7 +135,7 @@ public class Connection extends Activity { }).start(); } }); ->>>>>>> d7062ac2e0b306c42144c84690a3735c5878e11d + } } diff --git a/src/com/limelight/nvstream/NvComputer.java b/src/com/limelight/nvstream/NvComputer.java index 99d77431..e60ff5bf 100644 --- a/src/com/limelight/nvstream/NvComputer.java +++ b/src/com/limelight/nvstream/NvComputer.java @@ -1,8 +1,8 @@ package com.limelight.nvstream; import java.net.InetAddress; -import java.util.HashSet; import java.util.Locale; +import java.util.UUID; public class NvComputer { private String mDNSResponse; @@ -12,15 +12,14 @@ public class NvComputer { private int numOfApps; private String gpuType; private String mac; - private String uniqueID; + private UUID uniqueID; - private HashSet games; private int sessionID; private boolean paired; private boolean isBusy; - public NvComputer(String mDNSResponse, InetAddress ipAddress, String ipAddressString, int state, int numOfApps, String gpuType, String mac, String uniqueID) { + public NvComputer(String mDNSResponse, InetAddress ipAddress, String ipAddressString, int state, int numOfApps, String gpuType, String mac, UUID uniqueID) { this.mDNSResponse = mDNSResponse; this.ipAddress = ipAddress; this.ipAddressString = ipAddressString; @@ -29,8 +28,6 @@ public class NvComputer { this.gpuType = gpuType; this.mac = mac; this.uniqueID = uniqueID; - - this.games = new HashSet(); } public String getmDNSResponse() { @@ -46,7 +43,7 @@ public class NvComputer { } public String getIPAddressString() { - return this.ipAddress.getCanonicalHostName().toLowerCase(); + return this.ipAddress.getCanonicalHostName().toLowerCase(Locale.getDefault()); } public int getState() { @@ -65,18 +62,10 @@ public class NvComputer { return this.mac; } - public String getUniqueID() { + public UUID getUniqueID() { return this.uniqueID; } - - public boolean addGame(NvComputerGame game) { - return this.games.add(game); - } - public HashSet getGames() { - return this.games; - } - public void updateAfterPairQuery(int sessionID, boolean paired, boolean isBusy) { this.sessionID = sessionID; @@ -127,53 +116,10 @@ public class NvComputer { } public boolean equals(Object obj) { - NvComputer otherComputer = (NvComputer)obj; - if (this.ipAddress == null && otherComputer.getIpAddress() == null) { - return true; - } else if (this.ipAddress == null || otherComputer.getIpAddress() == null) { - return false; + if (obj instanceof UUID) { + return this.uniqueID.equals(obj); } else { - return this.ipAddress.equals(otherComputer.getIpAddress()); - } - } - - public class NvComputerGame { - private Integer ID; - private String appTitle; - private Boolean isRunning; - private Integer gameSession; - private Integer winLogon; - - public NvComputerGame(int ID, String appTitle, boolean isRunning) { - this.ID = ID; - this.appTitle = appTitle; - this.isRunning = isRunning; - } - - public void launchedGame(int gameSession, int winLogon) { - this.isRunning = true; - this.gameSession = gameSession; - this.winLogon = winLogon; - } - - public Integer getID() { - return this.ID; - } - - public String getAppTitle() { - return this.appTitle; - } - - public Boolean getIsRunning() { - return this.isRunning; - } - - public Integer getGameSession() { - return this.gameSession; - } - - public Integer winLogon() { - return this.winLogon; + return false; } } } \ No newline at end of file diff --git a/src/com/limelight/nvstream/NvmDNS.java b/src/com/limelight/nvstream/NvmDNS.java index 62892f52..5efd2f55 100644 --- a/src/com/limelight/nvstream/NvmDNS.java +++ b/src/com/limelight/nvstream/NvmDNS.java @@ -7,7 +7,11 @@ import java.net.InetAddress; import java.net.MulticastSocket; import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import org.xbill.DNS.DClass; import org.xbill.DNS.Header; @@ -15,214 +19,228 @@ import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import org.xbill.DNS.Section; +import org.xbill.DNS.TXTRecord; +import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; -import android.content.Context; -import android.net.nsd.NsdManager; -import android.net.nsd.NsdManager.DiscoveryListener; -import android.net.nsd.NsdManager.ResolveListener; -import android.net.nsd.NsdServiceInfo; import android.os.AsyncTask; import android.util.Log; -/** - * NvmDNS implements a clone of the NVidia Shield mDNS service for use on Limelight - * @author yetanothername - * - */ public class NvmDNS extends AsyncTask { - public static String NVSTREAM_MDNS_QUERY = "_nvstream._tcp.local."; - public static String NVSTREAM_MDNS_MULTICAST_GROUP = "224.0.0.251"; - public static InetAddress NVSTREAM_MDNS_MULTICAST_ADDRESS; - public static final short NVSTREAM_MDNS_PORT = 5353; + public static String MDNS_QUERY = "_nvstream._tcp.local."; + public static String MDNS_MULTICAST_GROUP = "224.0.0.251"; + public static InetAddress MDNS_MULTICAST_ADDRESS; + public static final short MDNS_PORT = 5353; - private HashSet nvstream_mdns_responses; - - private MulticastSocket nvstream_socket; + public static final int WAIT_MS = 5000; + + private HashSet responses; + private MulticastSocket socket; static { try { - NVSTREAM_MDNS_MULTICAST_ADDRESS = InetAddress.getByName(NvmDNS.NVSTREAM_MDNS_MULTICAST_GROUP); + MDNS_MULTICAST_ADDRESS = InetAddress.getByName(NvmDNS.MDNS_MULTICAST_GROUP); } catch (UnknownHostException e) { - NVSTREAM_MDNS_MULTICAST_ADDRESS = null; + MDNS_MULTICAST_ADDRESS = null; } } - - - /** - * We need to convert the IP Address into an IP Object - */ - static { - try { - NVSTREAM_MDNS_MULTICAST_ADDRESS = InetAddress.getByName(NvmDNS.NVSTREAM_MDNS_MULTICAST_GROUP); - } catch (UnknownHostException e) { - NVSTREAM_MDNS_MULTICAST_ADDRESS = null; - } - } - - /** - * This sets up the query sockets and the list, as well as sends out the query and listens for responses - * @throws IOException When shit breaks - */ - public NvmDNS() throws IOException { - Log.v("NvmDNS", "Constructor entered"); - this.nvstream_mdns_responses = new HashSet(); - Log.v("NvmDNS", "Constructor exited"); - } - private void makeFakeData() { + public NvmDNS() { + this.responses = new HashSet(); + + // Create our Socket Connection try { - this.nvstream_mdns_responses.add(new NvComputer("127.0.0.1", - InetAddress.getByName("127.0.0.1"), - InetAddress.getByName("127.0.0.1").getCanonicalHostName(), - 0, - 4, - "Intel(R) Extreme Graphics 3", - "DE:AD:BE:EF:CA:FE", - "foo")); - this.nvstream_mdns_responses.add(new NvComputer("10.0.2.15", - InetAddress.getByName("10.0.2.15"), - InetAddress.getByName("10.0.2.15").getCanonicalHostName(), - 0, - 5, - "Intel(R) Extreme Graphics 2", - "DE:AD:BE:EF:CA:FE", - "bar")); - } catch (Exception e) { - + this.socket = new MulticastSocket(NvmDNS.MDNS_PORT); + this.socket.setLoopbackMode(false); + this.socket.joinGroup(NvmDNS.MDNS_MULTICAST_ADDRESS); + Log.v("NvmDNS Socket Constructor", "Created mDNS listening socket"); + } catch (IOException e) { + Log.e("NvmDNS Socket Constructor", "There was an error creating the DNS socket."); + Log.e("NvmDNS Socket Constructor", e.getMessage()); } } - public void sendQueryAndWait() { - this.makeFakeData(); + public Set getComputers() { + return Collections.unmodifiableSet(this.responses); + } + + private void sendQuery() { + Header queryHeader = new Header(); + + // If we set the RA (Recursion Available) flag and our message ID to 0 + // then the packet matches the real mDNS query packet as displayed in Wireshark + queryHeader.setFlag(org.xbill.DNS.Flags.RA); + queryHeader.setID(0); + + Record question = null; try { - Log.v("NvmDNS UDP Loop", "mDNS Loop Started"); + // We need to create our "Question" DNS query that is a pointer record to + // the mDNS Query "Name" + question = Record.newRecord(new Name(NvmDNS.MDNS_QUERY), Type.PTR, DClass.IN); + } catch (TextParseException e) { + Log.e("NvmDNS Query", e.getMessage()); + return; + } - this.nvstream_socket = new MulticastSocket(NvmDNS.NVSTREAM_MDNS_PORT); - this.nvstream_socket.setLoopbackMode(false); - this.nvstream_socket.joinGroup(NvmDNS.NVSTREAM_MDNS_MULTICAST_ADDRESS); + // We combine our header and our question into a single message + Message query = new Message(); + query.setHeader(queryHeader); + query.addRecord(question, Section.QUESTION); - Log.v("NvmDNS UDP Loop", "Multicast Socket Created @" + this.nvstream_socket.getLocalPort()); + // Convert the message into Network Byte Order + byte[] wireQuery = query.toWire(); + Log.v("NvmDNS Query", query.toString()); + + // Convert our byte array into a Packet + DatagramPacket transmitPacket = new DatagramPacket(wireQuery, wireQuery.length); + transmitPacket.setAddress(NvmDNS.MDNS_MULTICAST_ADDRESS); + transmitPacket.setPort(NvmDNS.MDNS_PORT); - Header queryHeader = new Header(); - queryHeader.setFlag(org.xbill.DNS.Flags.RA); - queryHeader.setID(0); + // And (attempt) to send the packet + try { + Log.v("NvmDNS Query", "Blocking on this.nvstream_socket.send(transmitPacket)"); + this.socket.send(transmitPacket); + Log.v("NvmDNS Query", "Passed this.nvstream_socket.send(transmitPacket)"); + } catch (IOException e) { + Log.e("NvmDNS Query", "There was an error sending the DNS query."); + Log.e("NvmDNS Query", e.getMessage()); + } + } - Record question = Record.newRecord(new Name(NvmDNS.NVSTREAM_MDNS_QUERY), Type.PTR, DClass.IN); + public void waitForResponses() { + Log.v("NvmDNS Response", "mDNS Loop Started"); + + // We support up to 1500 byte packets + byte[] data = new byte[1500]; + DatagramPacket packet = new DatagramPacket(data, data.length); + + Message message = null; - Message query = new Message(); - query.setHeader(queryHeader); - query.addRecord(question, Section.QUESTION); - - byte[] wireQuery = query.toWire(); - - Log.v("NvmDNS UDP Loop", "Query: " + query.toString()); - - DatagramPacket transmitPacket = new DatagramPacket(wireQuery, wireQuery.length); - transmitPacket.setAddress(NvmDNS.NVSTREAM_MDNS_MULTICAST_ADDRESS); - transmitPacket.setPort(NvmDNS.NVSTREAM_MDNS_PORT); - - - - this.nvstream_socket.send(transmitPacket); - Log.v("NvmDNS UDP Loop", "Query Sent"); - - byte[] data = new byte[1500]; - DatagramPacket packet = new DatagramPacket(data, data.length); - Message message = null; - - while (true) { - - Log.d("NvmDNS UDP Loop", "Blocking on this.nvstream_query_socket.recieve()"); - this.nvstream_socket.receive(packet); - Log.d("NvmDNS UDP Loop", "Blocking passed on this.nvstream_query_socket.recieve()"); - + while (!this.socket.isClosed()) { + // Attempt to receive a packet/response + try { + Log.d("NvmDNS Response", "Blocking on this.nvstream_query_socket.recieve()"); + this.socket.receive(packet); + Log.d("NvmDNS Response", "Blocking passed on this.nvstream_query_socket.recieve()"); message = new Message(packet.getData()); - Record[] responses = message.getSectionArray(Section.ADDITIONAL); - if (responses.length != 0 && message.getSectionArray(Section.ANSWER).length != 0 && - message.getSectionArray(Section.ANSWER)[0].getName().toString().equals(NvmDNS.NVSTREAM_MDNS_QUERY)) { - - Log.v("NvmDNS UDP Reply", "Got a packet from " + packet.getAddress().getCanonicalHostName()); - Log.v("NvmDNS UDP Reply", "Question: " + message.getSectionArray(Section.ANSWER)[0].getName().toString()); - Log.v("NvmDNS UDP Reply", "Response: " + responses[0].getName().toString()); + } catch (IOException e) { + if (this.socket.isClosed()) { + Log.e("NvmDNS Response", "The socket was closed on us. The timer must have been reached."); + return; + } else { + Log.e("NvmDNS Response", "There was an error receiving the response."); + Log.e("NvmDNS Response", e.getMessage()); + continue; + } + } + + // We really only care about the ADDITIONAL section (specifically the text records) + Record[] responses = message.getSectionArray(Section.ADDITIONAL); + + // We only want to process records that actually have a length, have an ANSWER + // section that has stuff in it and that the ANSWER to our query is what we sent + if (responses.length != 0 && + message.getSectionArray(Section.ANSWER).length != 0 && + message.getSectionArray(Section.ANSWER)[0].getName().toString().equals(NvmDNS.MDNS_QUERY)) { + + Log.v("NvmDNS Response", "Got a packet from " + packet.getAddress().getCanonicalHostName()); + Log.v("NvmDNS Response", "Question: " + message.getSectionArray(Section.ANSWER)[0].getName().toString()); + Log.v("NvmDNS Response", "Response: " + responses[0].getName().toString()); - String[] txtRecords = responses[0].rdataToString().split("\" \""); - - // No, but really, there has to be a better way of doing this... - txtRecords[0] = txtRecords[0].substring(1); - txtRecords[txtRecords.length - 1] = txtRecords[txtRecords.length - 1].split("\"")[0]; - - int state = -1; - int numOfApps = -1; - String gpuType = "Intel(R) Extreme Graphics 2"; - String mac = "DE:AD:BE:EF:CA:FE"; - String uniqueID = "4"; - - for (int i = 0; i < txtRecords.length; i++) { - if (i == 0) { - state = Integer.parseInt(txtRecords[i].split("=")[1]); - } else if (i == 1) { - numOfApps = Integer.parseInt(txtRecords[i].split("=")[1]); - } else if (i == 2) { - gpuType = txtRecords[i].split("=")[1]; - } else if (i == 3) { - mac = txtRecords[i].split("=")[1]; - } else if (i == 4) { - uniqueID = txtRecords[i].split("=")[1]; - } + // The DNS library we are using does not use inferred generics :( + + TXTRecord txtRecord = null; + + + for (Record record : responses) { + Log.v("NvmDNS Response", "We recieved a DNS repsonse with a " + record.getClass().getName() + " record."); + if (record instanceof TXTRecord) { + txtRecord = (TXTRecord)record; } - + } + + if (txtRecord == null) { + Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with no TXTRecord"); + continue; + } + + @SuppressWarnings("unchecked") + ArrayList txtRecordStringList = new ArrayList(txtRecord.getStrings()); + + if (txtRecordStringList.size() != 5) { + Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse with the improper amount of TXTRecord Entries."); + continue; + } + + // The general format of the text records is: + // SERVICE_STATE=1 + // SERVICE_NUMOFAPPS=5 + // SERVICE_GPUTYPE=GeForce GTX 760 x2 + // SERVICE_MAC=DE:AD:BE:EF:CA:FE + // SERVICE_UNIQUEID={A Wild UUID Appeared!} + // Every single record I've seen so far has been in this format + try { + int serviceState = Integer.parseInt(txtRecordStringList.get(0).toString().split("=")[1]); + int numberOfApps = Integer.parseInt(txtRecordStringList.get(1).toString().split("=")[1]); + String gpuType = txtRecordStringList.get(2).toString().split("=")[1]; + String mac = txtRecordStringList.get(3).toString().split("=")[1]; + UUID uniqueID = UUID.fromString(txtRecordStringList.get(4).toString().split("=")[1]); + + // We need to resolve the hostname in this thread so that we can use it in the GUI packet.getAddress().getCanonicalHostName(); - NvComputer computer = new NvComputer(responses[0].getName().toString(), packet.getAddress(), packet.getAddress().getCanonicalHostName(), state, numOfApps, gpuType, mac, uniqueID); - this.nvstream_mdns_responses.add(computer); - Log.v("NvmDNS NvComputer", computer.toString()); + NvComputer computer = new NvComputer(responses[0].getName().toString(), packet.getAddress(), packet.getAddress().getCanonicalHostName(), serviceState, numberOfApps, gpuType, mac, uniqueID); + this.responses.add(computer); + } catch (ArrayIndexOutOfBoundsException e) { + Log.e("NvmDNS Response", "We recieved a malformed DNS repsonse."); } - } - } catch (Exception e) { - e.printStackTrace(); - } + } + } } - @Override - protected Void doInBackground(Void... thisParameterIsUseless) { - Log.v("NvmDNS", "doInBackground init"); + protected Void doInBackground(Void... thisParameterIsUseless) { + Log.v("NvmDNS ASync", "doInBackground entered"); + + this.sendQuery(); + + + // We want to run our wait thread for an amount of time then close the socket. new Thread(new Runnable() { @Override public void run() { - Log.v("NvmDNS 1000 mS Wait", "going to sleep"); + Log.v("NvmDNS Wait", "Going to sleep for " + NvmDNS.WAIT_MS + "ms"); try { - - Thread.sleep(5000); + Thread.sleep(NvmDNS.WAIT_MS); } catch (InterruptedException e) { - e.printStackTrace(); + Log.e("NvmDNS Wait", "Woke up from sleep before time."); + Log.e("NvmDNS Wait", e.getMessage()); } - Log.v("NvmDNS 1000 mS Wait", "waking from sleep"); - NvmDNS.this.nvstream_socket.close(); - NvmDNS.this.nvstream_socket = null; - Log.v("NvmDNS 1000 mS Wait", "socket closed"); + Log.v("NvmDNS Wait", "Woke up from sleep"); + NvmDNS.this.socket.close(); + Log.v("NvmDNS Wait", "Socket Closed"); } }).start(); + - this.sendQueryAndWait(); - Log.v("NvmDNS", "doInBackground return"); + this.waitForResponses(); + + Log.v("NvmDNS ASync", "doInBackground exit"); return null; } @Override protected void onProgressUpdate(Integer... progress) { - Log.v("NvmDNS", "onProgressUpdate "); + Log.v("NvmDNS ASync", "onProgressUpdate"); } @Override protected void onPostExecute(Void moreUselessParameters) { - Log.v("NvmDNS", "onPostExecute"); - for (NvComputer computer : this.nvstream_mdns_responses) { - Log.i("NvmDNS NvComputer Printout", computer.toString()); + Log.v("NvmDNS ASync", "onPostExecute"); + for (NvComputer computer : this.responses) { + Log.i("NvmDNS NvComputer", computer.toString()); } - } }