Wake on LAN support. Many fixes for Limelight Android 2.5.

This commit is contained in:
Cameron Gutman 2014-07-03 23:30:29 -07:00
parent a4dceb0b74
commit 894110ba08
5 changed files with 189 additions and 7 deletions

View File

@ -35,6 +35,7 @@ public class NvConnection {
private NvConnectionListener listener;
private StreamConfiguration config;
private LimelightCryptoProvider cryptoProvider;
private String uniqueId;
private InetAddress hostAddr;
private ControlStream controlStream;
@ -52,12 +53,13 @@ public class NvConnection {
private ThreadPoolExecutor threadPool;
public NvConnection(String host, NvConnectionListener listener, StreamConfiguration config, LimelightCryptoProvider cryptoProvider)
public NvConnection(String host, String uniqueId, NvConnectionListener listener, StreamConfiguration config, LimelightCryptoProvider cryptoProvider)
{
this.host = host;
this.listener = listener;
this.config = config;
this.cryptoProvider = cryptoProvider;
this.uniqueId = uniqueId;
try {
// This is unique per connection
@ -162,7 +164,7 @@ public class NvConnection {
private boolean startApp() throws XmlPullParserException, IOException
{
NvHTTP h = new NvHTTP(hostAddr, getMacAddressString(), localDeviceName, cryptoProvider);
NvHTTP h = new NvHTTP(hostAddr, uniqueId, localDeviceName, cryptoProvider);
if (h.getServerVersion().startsWith("1.")) {
listener.displayMessage("Limelight now requires GeForce Experience 2.0.1 or later. Please upgrade GFE on your PC and try again.");

View File

@ -0,0 +1,68 @@
package com.limelight.nvstream.http;
import java.net.InetAddress;
import java.util.UUID;
public class ComputerDetails {
public enum State {
ONLINE, OFFLINE, UNKNOWN
}
public enum Reachability {
LOCAL, REMOTE, OFFLINE, UNKNOWN
}
public State state;
public Reachability reachability;
public String name;
public UUID uuid;
public InetAddress localIp;
public InetAddress remoteIp;
public PairingManager.PairState pairState;
public String macAddress;
public int runningGameId;
public void update(ComputerDetails details) {
this.state = details.state;
this.name = details.name;
this.uuid = details.uuid;
this.localIp = details.localIp;
this.remoteIp = details.remoteIp;
this.macAddress = details.macAddress;
this.pairState = details.pairState;
this.runningGameId = details.runningGameId;
}
@Override
public boolean equals(Object o) {
if (o instanceof ComputerDetails) {
ComputerDetails other = (ComputerDetails)o;
// Use UUIDs if they both have them
if (other.uuid != null && this.uuid != null)
{
return other.uuid.equals(this.uuid);
}
// Otherwise use local IP
return other.localIp.equals(this.localIp);
}
return false;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
str.append("State: ").append(state).append("\n");
str.append("Reachability: ").append(reachability).append("\n");
str.append("Name: ").append(name).append("\n");
str.append("UUID: ").append(uuid).append("\n");
str.append("Local IP: ").append(localIp).append("\n");
str.append("Remote IP: ").append(remoteIp).append("\n");
str.append("MAC Address: ").append(macAddress).append("\n");
str.append("Pair State: ").append(pairState).append("\n");
str.append("Running Game ID: ").append(runningGameId).append("\n");
return str.toString();
}
}

View File

@ -13,6 +13,7 @@ import java.net.URLConnection;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.Stack;
import java.util.UUID;
import javax.crypto.SecretKey;
@ -20,6 +21,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import com.limelight.nvstream.http.PairingManager.PairState;
public class NvHTTP {
private String uniqueId;
@ -27,7 +30,7 @@ public class NvHTTP {
private LimelightCryptoProvider cryptoProvider;
public static final int PORT = 47984;
public static final int CONNECTION_TIMEOUT = 5000;
public static final int CONNECTION_TIMEOUT = 2000;
private final boolean verbose = false;
@ -97,6 +100,35 @@ public class NvHTTP {
}
}
public ComputerDetails getComputerDetails() throws MalformedURLException, IOException, XmlPullParserException {
ComputerDetails details = new ComputerDetails();
String serverInfo = openHttpConnectionToString(baseUrl + "/serverinfo?uniqueid=" + uniqueId);
details.name = getXmlString(serverInfo, "hostname");
details.uuid = UUID.fromString(getXmlString(serverInfo, "uniqueid"));
details.localIp = InetAddress.getByName(getXmlString(serverInfo, "LocalIP"));
details.remoteIp = InetAddress.getByName(getXmlString(serverInfo, "ExternalIP"));
details.macAddress = getXmlString(serverInfo, "mac");
try {
details.pairState = Integer.parseInt(getXmlString(serverInfo, "PairStatus")) == 1 ?
PairState.PAIRED : PairState.NOT_PAIRED;
} catch (NumberFormatException e) {
details.pairState = PairState.FAILED;
}
try {
details.runningGameId = Integer.parseInt(getXmlString(serverInfo, "currentgame"));
} catch (NumberFormatException e) {
details.runningGameId = 0;
}
// We could reach it so it's online
details.state = ComputerDetails.State.ONLINE;
return details;
}
private InputStream openHttpConnection(String url) throws IOException {
URLConnection conn = new URL(url).openConnection();
if (verbose) {
@ -201,6 +233,10 @@ public class NvHTTP {
return appList;
}
public void unpair() throws IOException {
openHttpConnection(baseUrl + "/unpair?uniqueid=" + uniqueId);
}
public int launchApp(int appId, int width, int height, int refreshRate, SecretKey inputKey) throws IOException, XmlPullParserException {
InputStream in = openHttpConnection(baseUrl +
"/launch?uniqueid=" + uniqueId +

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -127,7 +128,6 @@ public class MdnsDiscoveryAgent {
private MdnsDiscoveryAgent(MdnsDiscoveryListener listener) {
computers = new HashMap<InetAddress, MdnsComputer>();
discoveryTimer = new Timer();
pendingResolution = new ArrayList<String>();
this.listener = listener;
}
@ -135,7 +135,7 @@ public class MdnsDiscoveryAgent {
public static MdnsDiscoveryAgent createDiscoveryAgent(MdnsDiscoveryListener listener) throws IOException {
MdnsDiscoveryAgent agent = new MdnsDiscoveryAgent(listener);
agent.resolver = JmDNS.create();
agent.resolver = JmDNS.create(new InetSocketAddress(0).getAddress());
return agent;
}
@ -143,6 +143,7 @@ public class MdnsDiscoveryAgent {
public void startDiscovery(final int discoveryIntervalMs) {
resolver.addServiceListener(SERVICE_TYPE, nvstreamListener);
discoveryTimer = new Timer();
discoveryTimer.schedule(new TimerTask() {
@Override
public void run() {
@ -162,7 +163,10 @@ public class MdnsDiscoveryAgent {
public void stopDiscovery() {
resolver.removeServiceListener(SERVICE_TYPE, nvstreamListener);
if (discoveryTimer != null) {
discoveryTimer.cancel();
discoveryTimer = null;
}
}
public List<MdnsComputer> getComputerSet() {

View File

@ -0,0 +1,72 @@
package com.limelight.nvstream.wol;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
import com.limelight.nvstream.http.ComputerDetails;
public class WakeOnLanSender {
private static final int[] PORTS_TO_TRY = new int[] {
7, 9, // Standard WOL ports
47998, 47999, 48000 // Ports opened by GFE
};
public static void sendWolPacket(ComputerDetails computer) throws IOException {
DatagramSocket sock = new DatagramSocket(0);
byte[] payload = createWolPayload(computer);
// Try both remote and local addresses
for (int i = 0; i < 2; i++) {
InetAddress addr;
if (i == 0) {
addr = computer.localIp;
}
else {
addr = computer.remoteIp;
}
// Try all the ports for each address
for (int port : PORTS_TO_TRY) {
DatagramPacket dp = new DatagramPacket(payload, payload.length);
dp.setAddress(addr);
dp.setPort(port);
sock.send(dp);
}
}
sock.close();
}
private static byte[] macStringToBytes(String macAddress) {
byte[] macBytes = new byte[6];
@SuppressWarnings("resource")
Scanner scan = new Scanner(macAddress).useDelimiter(":");
for (int i = 0; i < macBytes.length && scan.hasNext(); i++) {
macBytes[i] = (byte) Integer.parseInt(scan.next(), 16);
}
scan.close();
return macBytes;
}
private static byte[] createWolPayload(ComputerDetails computer) {
byte[] payload = new byte[102];
byte[] macAddress = macStringToBytes(computer.macAddress);
int i;
// 6 bytes of FF
for (i = 0; i < 6; i++) {
payload[i] = (byte)0xFF;
}
// 16 repetitions of the MAC address
for (int j = 0; j < 16; j++) {
System.arraycopy(macAddress, 0, payload, i, macAddress.length);
i += macAddress.length;
}
return payload;
}
}