mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-21 16:00:10 +00:00
@@ -22,6 +22,7 @@ import com.limelight.nvstream.NvConnectionListener;
|
|||||||
import com.limelight.nvstream.StreamConfiguration;
|
import com.limelight.nvstream.StreamConfiguration;
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
import com.limelight.nvstream.http.NvApp;
|
import com.limelight.nvstream.http.NvApp;
|
||||||
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
import com.limelight.nvstream.input.KeyboardPacket;
|
import com.limelight.nvstream.input.KeyboardPacket;
|
||||||
import com.limelight.nvstream.input.MouseButtonPacket;
|
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||||
import com.limelight.nvstream.jni.MoonBridge;
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
@@ -166,6 +167,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
};
|
};
|
||||||
|
|
||||||
public static final String EXTRA_HOST = "Host";
|
public static final String EXTRA_HOST = "Host";
|
||||||
|
public static final String EXTRA_PORT = "Port";
|
||||||
public static final String EXTRA_APP_NAME = "AppName";
|
public static final String EXTRA_APP_NAME = "AppName";
|
||||||
public static final String EXTRA_APP_ID = "AppId";
|
public static final String EXTRA_APP_ID = "AppId";
|
||||||
public static final String EXTRA_UNIQUEID = "UniqueId";
|
public static final String EXTRA_UNIQUEID = "UniqueId";
|
||||||
@@ -311,6 +313,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME);
|
pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME);
|
||||||
|
|
||||||
String host = Game.this.getIntent().getStringExtra(EXTRA_HOST);
|
String host = Game.this.getIntent().getStringExtra(EXTRA_HOST);
|
||||||
|
int port = Game.this.getIntent().getIntExtra(EXTRA_PORT, NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
int appId = Game.this.getIntent().getIntExtra(EXTRA_APP_ID, StreamConfiguration.INVALID_APP_ID);
|
int appId = Game.this.getIntent().getIntExtra(EXTRA_APP_ID, StreamConfiguration.INVALID_APP_ID);
|
||||||
String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID);
|
String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID);
|
||||||
String uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
|
String uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
|
||||||
@@ -472,7 +475,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Initialize the connection
|
// Initialize the connection
|
||||||
conn = new NvConnection(getApplicationContext(), host, uniqueId, config, PlatformBinding.getCryptoProvider(this), serverCert, needsInputBatching);
|
conn = new NvConnection(getApplicationContext(),
|
||||||
|
new ComputerDetails.AddressTuple(host, port),
|
||||||
|
uniqueId, config,
|
||||||
|
PlatformBinding.getCryptoProvider(this), serverCert,
|
||||||
|
needsInputBatching);
|
||||||
controllerHandler = new ControllerHandler(this, conn, this, prefConfig);
|
controllerHandler = new ControllerHandler(this, conn, this, prefConfig);
|
||||||
keyboardTranslator = new KeyboardTranslator();
|
keyboardTranslator = new KeyboardTranslator();
|
||||||
|
|
||||||
|
|||||||
@@ -400,8 +400,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
stopComputerUpdates(true);
|
stopComputerUpdates(true);
|
||||||
|
|
||||||
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
||||||
managerBinder.getUniqueId(),
|
computer.httpsPort, managerBinder.getUniqueId(), computer.serverCert,
|
||||||
computer.serverCert,
|
|
||||||
PlatformBinding.getCryptoProvider(PcView.this));
|
PlatformBinding.getCryptoProvider(PcView.this));
|
||||||
if (httpConn.getPairState() == PairState.PAIRED) {
|
if (httpConn.getPairState() == PairState.PAIRED) {
|
||||||
// Don't display any toast, but open the app list
|
// Don't display any toast, but open the app list
|
||||||
@@ -534,8 +533,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
||||||
managerBinder.getUniqueId(),
|
computer.httpsPort, managerBinder.getUniqueId(), computer.serverCert,
|
||||||
computer.serverCert,
|
|
||||||
PlatformBinding.getCryptoProvider(PcView.this));
|
PlatformBinding.getCryptoProvider(PcView.this));
|
||||||
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
|
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
|
||||||
httpConn.unpair();
|
httpConn.unpair();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -27,6 +28,7 @@ public class ComputerDatabaseManager {
|
|||||||
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
|
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
|
||||||
|
|
||||||
private static final char ADDRESS_DELIMITER = ';';
|
private static final char ADDRESS_DELIMITER = ';';
|
||||||
|
private static final char PORT_DELIMITER = '_';
|
||||||
|
|
||||||
private SQLiteDatabase computerDb;
|
private SQLiteDatabase computerDb;
|
||||||
|
|
||||||
@@ -74,10 +76,10 @@ public class ComputerDatabaseManager {
|
|||||||
values.put(COMPUTER_NAME_COLUMN_NAME, details.name);
|
values.put(COMPUTER_NAME_COLUMN_NAME, details.name);
|
||||||
|
|
||||||
StringBuilder addresses = new StringBuilder();
|
StringBuilder addresses = new StringBuilder();
|
||||||
addresses.append(details.localAddress != null ? details.localAddress : "");
|
addresses.append(details.localAddress != null ? splitTupleToAddress(details.localAddress) : "");
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.remoteAddress != null ? details.remoteAddress : "");
|
addresses.append(ADDRESS_DELIMITER).append(details.remoteAddress != null ? splitTupleToAddress(details.remoteAddress) : "");
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.manualAddress != null ? details.manualAddress : "");
|
addresses.append(ADDRESS_DELIMITER).append(details.manualAddress != null ? splitTupleToAddress(details.manualAddress) : "");
|
||||||
addresses.append(ADDRESS_DELIMITER).append(details.ipv6Address != null ? details.ipv6Address : "");
|
addresses.append(ADDRESS_DELIMITER).append(details.ipv6Address != null ? splitTupleToAddress(details.ipv6Address) : "");
|
||||||
|
|
||||||
values.put(ADDRESSES_COLUMN_NAME, addresses.toString());
|
values.put(ADDRESSES_COLUMN_NAME, addresses.toString());
|
||||||
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
|
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
|
||||||
@@ -103,6 +105,24 @@ public class ComputerDatabaseManager {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ComputerDetails.AddressTuple splitAddressToTuple(String input) {
|
||||||
|
if (input == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = input.split(""+PORT_DELIMITER, -1);
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return new ComputerDetails.AddressTuple(parts[0], NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new ComputerDetails.AddressTuple(parts[0], Integer.parseInt(parts[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String splitTupleToAddress(ComputerDetails.AddressTuple tuple) {
|
||||||
|
return tuple.address+PORT_DELIMITER+tuple.port;
|
||||||
|
}
|
||||||
|
|
||||||
private ComputerDetails getComputerFromCursor(Cursor c) {
|
private ComputerDetails getComputerFromCursor(Cursor c) {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
|
|
||||||
@@ -111,10 +131,18 @@ public class ComputerDatabaseManager {
|
|||||||
|
|
||||||
String[] addresses = c.getString(2).split(""+ADDRESS_DELIMITER, -1);
|
String[] addresses = c.getString(2).split(""+ADDRESS_DELIMITER, -1);
|
||||||
|
|
||||||
details.localAddress = readNonEmptyString(addresses[0]);
|
details.localAddress = splitAddressToTuple(readNonEmptyString(addresses[0]));
|
||||||
details.remoteAddress = readNonEmptyString(addresses[1]);
|
details.remoteAddress = splitAddressToTuple(readNonEmptyString(addresses[1]));
|
||||||
details.manualAddress = readNonEmptyString(addresses[2]);
|
details.manualAddress = splitAddressToTuple(readNonEmptyString(addresses[2]));
|
||||||
details.ipv6Address = readNonEmptyString(addresses[3]);
|
details.ipv6Address = splitAddressToTuple(readNonEmptyString(addresses[3]));
|
||||||
|
|
||||||
|
// External port is persisted in the remote address field
|
||||||
|
if (details.remoteAddress != null) {
|
||||||
|
details.externalPort = details.remoteAddress.port;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
details.externalPort = NvHTTP.DEFAULT_HTTP_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
details.macAddress = c.getString(3);
|
details.macAddress = c.getString(3);
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public class ComputerManagerService extends Service {
|
|||||||
// then use STUN to populate the external address field if
|
// then use STUN to populate the external address field if
|
||||||
// it's not set already.
|
// it's not set already.
|
||||||
if (details.remoteAddress == null) {
|
if (details.remoteAddress == null) {
|
||||||
InetAddress addr = InetAddress.getByName(details.activeAddress);
|
InetAddress addr = InetAddress.getByName(details.activeAddress.address);
|
||||||
if (addr.isSiteLocalAddress()) {
|
if (addr.isSiteLocalAddress()) {
|
||||||
populateExternalAddress(details);
|
populateExternalAddress(details);
|
||||||
}
|
}
|
||||||
@@ -369,7 +369,12 @@ public class ComputerManagerService extends Service {
|
|||||||
|
|
||||||
// Perform the STUN request if we're not on a VPN or if we bound to a network
|
// Perform the STUN request if we're not on a VPN or if we bound to a network
|
||||||
if (!activeNetworkIsVpn || boundToNetwork) {
|
if (!activeNetworkIsVpn || boundToNetwork) {
|
||||||
details.remoteAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
|
String stunResolvedAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
|
||||||
|
if (stunResolvedAddress != null) {
|
||||||
|
// We don't know for sure what the external port is, so we will have to guess.
|
||||||
|
// When we contact the PC (if we haven't already), it will update the port.
|
||||||
|
details.remoteAddress = new ComputerDetails.AddressTuple(stunResolvedAddress, details.guessExternalPort());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind from the network
|
// Unbind from the network
|
||||||
@@ -396,7 +401,7 @@ public class ComputerManagerService extends Service {
|
|||||||
|
|
||||||
// Populate the computer template with mDNS info
|
// Populate the computer template with mDNS info
|
||||||
if (computer.getLocalAddress() != null) {
|
if (computer.getLocalAddress() != null) {
|
||||||
details.localAddress = computer.getLocalAddress().getHostAddress();
|
details.localAddress = new ComputerDetails.AddressTuple(computer.getLocalAddress().getHostAddress(), computer.getPort());
|
||||||
|
|
||||||
// Since we're on the same network, we can use STUN to find
|
// Since we're on the same network, we can use STUN to find
|
||||||
// our WAN address, which is also very likely the WAN address
|
// our WAN address, which is also very likely the WAN address
|
||||||
@@ -406,7 +411,7 @@ public class ComputerManagerService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (computer.getIpv6Address() != null) {
|
if (computer.getIpv6Address() != null) {
|
||||||
details.ipv6Address = computer.getIpv6Address().getHostAddress();
|
details.ipv6Address = new ComputerDetails.AddressTuple(computer.getIpv6Address().getHostAddress(), computer.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -543,9 +548,9 @@ public class ComputerManagerService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ComputerDetails tryPollIp(ComputerDetails details, String address) {
|
private ComputerDetails tryPollIp(ComputerDetails details, ComputerDetails.AddressTuple address) {
|
||||||
try {
|
try {
|
||||||
NvHTTP http = new NvHTTP(address, idManager.getUniqueId(), details.serverCert,
|
NvHTTP http = new NvHTTP(address, 0, idManager.getUniqueId(), details.serverCert,
|
||||||
PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
||||||
|
|
||||||
ComputerDetails newDetails = http.getComputerDetails();
|
ComputerDetails newDetails = http.getComputerDetails();
|
||||||
@@ -572,14 +577,14 @@ public class ComputerManagerService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class ParallelPollTuple {
|
private static class ParallelPollTuple {
|
||||||
public String address;
|
public ComputerDetails.AddressTuple address;
|
||||||
public ComputerDetails existingDetails;
|
public ComputerDetails existingDetails;
|
||||||
|
|
||||||
public boolean complete;
|
public boolean complete;
|
||||||
public Thread pollingThread;
|
public Thread pollingThread;
|
||||||
public ComputerDetails returnedDetails;
|
public ComputerDetails returnedDetails;
|
||||||
|
|
||||||
public ParallelPollTuple(String address, ComputerDetails existingDetails) {
|
public ParallelPollTuple(ComputerDetails.AddressTuple address, ComputerDetails existingDetails) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.existingDetails = existingDetails;
|
this.existingDetails = existingDetails;
|
||||||
}
|
}
|
||||||
@@ -591,7 +596,7 @@ public class ComputerManagerService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startParallelPollThread(ParallelPollTuple tuple, HashSet<String> uniqueAddresses) {
|
private void startParallelPollThread(ParallelPollTuple tuple, HashSet<ComputerDetails.AddressTuple> uniqueAddresses) {
|
||||||
// Don't bother starting a polling thread for an address that doesn't exist
|
// Don't bother starting a polling thread for an address that doesn't exist
|
||||||
// or if the address has already been polled with an earlier tuple
|
// or if the address has already been polled with an earlier tuple
|
||||||
if (tuple.address == null || !uniqueAddresses.add(tuple.address)) {
|
if (tuple.address == null || !uniqueAddresses.add(tuple.address)) {
|
||||||
@@ -625,7 +630,7 @@ public class ComputerManagerService extends Service {
|
|||||||
|
|
||||||
// These must be started in order of precedence for the deduplication algorithm
|
// These must be started in order of precedence for the deduplication algorithm
|
||||||
// to result in the correct behavior.
|
// to result in the correct behavior.
|
||||||
HashSet<String> uniqueAddresses = new HashSet<>();
|
HashSet<ComputerDetails.AddressTuple> uniqueAddresses = new HashSet<>();
|
||||||
startParallelPollThread(localInfo, uniqueAddresses);
|
startParallelPollThread(localInfo, uniqueAddresses);
|
||||||
startParallelPollThread(manualInfo, uniqueAddresses);
|
startParallelPollThread(manualInfo, uniqueAddresses);
|
||||||
startParallelPollThread(remoteInfo, uniqueAddresses);
|
startParallelPollThread(remoteInfo, uniqueAddresses);
|
||||||
@@ -821,7 +826,7 @@ public class ComputerManagerService extends Service {
|
|||||||
PollingTuple tuple = getPollingTuple(computer);
|
PollingTuple tuple = getPollingTuple(computer);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), idManager.getUniqueId(),
|
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), computer.httpsPort, idManager.getUniqueId(),
|
||||||
computer.serverCert, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
computer.serverCert, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
||||||
|
|
||||||
String appList;
|
String appList;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteException;
|
|||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -30,26 +31,26 @@ public class LegacyDatabaseReader {
|
|||||||
// too. To disambiguate, we'll need to prefix them with a string
|
// too. To disambiguate, we'll need to prefix them with a string
|
||||||
// greater than the allowable IP address length.
|
// greater than the allowable IP address length.
|
||||||
try {
|
try {
|
||||||
details.localAddress = InetAddress.getByAddress(c.getBlob(2)).getHostAddress();
|
details.localAddress = new ComputerDetails.AddressTuple(InetAddress.getByAddress(c.getBlob(2)).getHostAddress(), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
LimeLog.warning("DB: Legacy local address for " + details.name);
|
LimeLog.warning("DB: Legacy local address for " + details.name);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
// This is probably a hostname/address with the prefix string
|
// This is probably a hostname/address with the prefix string
|
||||||
String stringData = c.getString(2);
|
String stringData = c.getString(2);
|
||||||
if (stringData.startsWith(ADDRESS_PREFIX)) {
|
if (stringData.startsWith(ADDRESS_PREFIX)) {
|
||||||
details.localAddress = c.getString(2).substring(ADDRESS_PREFIX.length());
|
details.localAddress = new ComputerDetails.AddressTuple(c.getString(2).substring(ADDRESS_PREFIX.length()), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
} else {
|
} else {
|
||||||
LimeLog.severe("DB: Corrupted local address for " + details.name);
|
LimeLog.severe("DB: Corrupted local address for " + details.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
details.remoteAddress = InetAddress.getByAddress(c.getBlob(3)).getHostAddress();
|
details.remoteAddress = new ComputerDetails.AddressTuple(InetAddress.getByAddress(c.getBlob(3)).getHostAddress(), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
LimeLog.warning("DB: Legacy remote address for " + details.name);
|
LimeLog.warning("DB: Legacy remote address for " + details.name);
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
// This is probably a hostname/address with the prefix string
|
// This is probably a hostname/address with the prefix string
|
||||||
String stringData = c.getString(3);
|
String stringData = c.getString(3);
|
||||||
if (stringData.startsWith(ADDRESS_PREFIX)) {
|
if (stringData.startsWith(ADDRESS_PREFIX)) {
|
||||||
details.remoteAddress = c.getString(3).substring(ADDRESS_PREFIX.length());
|
details.remoteAddress = new ComputerDetails.AddressTuple(c.getString(3).substring(ADDRESS_PREFIX.length()), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
} else {
|
} else {
|
||||||
LimeLog.severe("DB: Corrupted remote address for " + details.name);
|
LimeLog.severe("DB: Corrupted remote address for " + details.name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
|
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
import com.limelight.nvstream.http.NvHTTP;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
@@ -23,9 +24,9 @@ public class LegacyDatabaseReader2 {
|
|||||||
|
|
||||||
details.uuid = c.getString(0);
|
details.uuid = c.getString(0);
|
||||||
details.name = c.getString(1);
|
details.name = c.getString(1);
|
||||||
details.localAddress = c.getString(2);
|
details.localAddress = new ComputerDetails.AddressTuple(c.getString(2), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
details.remoteAddress = c.getString(3);
|
details.remoteAddress = new ComputerDetails.AddressTuple(c.getString(3), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
details.manualAddress = c.getString(4);
|
details.manualAddress = new ComputerDetails.AddressTuple(c.getString(4), NvHTTP.DEFAULT_HTTP_PORT);
|
||||||
details.macAddress = c.getString(5);
|
details.macAddress = c.getString(5);
|
||||||
|
|
||||||
// This column wasn't always present in the old schema
|
// This column wasn't always present in the old schema
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ public class NetworkAssetLoader {
|
|||||||
public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) {
|
public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer), uniqueId,
|
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer),
|
||||||
tuple.computer.serverCert, PlatformBinding.getCryptoProvider(context));
|
tuple.computer.httpsPort, uniqueId, tuple.computer.serverCert,
|
||||||
|
PlatformBinding.getCryptoProvider(context));
|
||||||
in = http.getBoxArt(tuple.app);
|
in = http.getBoxArt(tuple.app);
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.limelight.nvstream;
|
package com.limelight.nvstream;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
public class ConnectionContext {
|
public class ConnectionContext {
|
||||||
public String serverAddress;
|
public ComputerDetails.AddressTuple serverAddress;
|
||||||
public X509Certificate serverCert;
|
public X509Certificate serverCert;
|
||||||
public StreamConfiguration streamConfig;
|
public StreamConfiguration streamConfig;
|
||||||
public NvConnectionListener connListener;
|
public NvConnectionListener connListener;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||||
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
import com.limelight.nvstream.http.GfeHttpResponseException;
|
import com.limelight.nvstream.http.GfeHttpResponseException;
|
||||||
import com.limelight.nvstream.http.LimelightCryptoProvider;
|
import com.limelight.nvstream.http.LimelightCryptoProvider;
|
||||||
import com.limelight.nvstream.http.NvApp;
|
import com.limelight.nvstream.http.NvApp;
|
||||||
@@ -42,7 +43,7 @@ import com.limelight.nvstream.jni.MoonBridge;
|
|||||||
|
|
||||||
public class NvConnection {
|
public class NvConnection {
|
||||||
// Context parameters
|
// Context parameters
|
||||||
private String host;
|
private ComputerDetails.AddressTuple host;
|
||||||
private LimelightCryptoProvider cryptoProvider;
|
private LimelightCryptoProvider cryptoProvider;
|
||||||
private String uniqueId;
|
private String uniqueId;
|
||||||
private ConnectionContext context;
|
private ConnectionContext context;
|
||||||
@@ -57,7 +58,7 @@ public class NvConnection {
|
|||||||
private short relMouseX, relMouseY, relMouseWidth, relMouseHeight;
|
private short relMouseX, relMouseY, relMouseWidth, relMouseHeight;
|
||||||
private short absMouseX, absMouseY, absMouseWidth, absMouseHeight;
|
private short absMouseX, absMouseY, absMouseWidth, absMouseHeight;
|
||||||
|
|
||||||
public NvConnection(Context appContext, String host, String uniqueId, StreamConfiguration config, LimelightCryptoProvider cryptoProvider, X509Certificate serverCert, boolean batchMouseInput)
|
public NvConnection(Context appContext, ComputerDetails.AddressTuple host, String uniqueId, StreamConfiguration config, LimelightCryptoProvider cryptoProvider, X509Certificate serverCert, boolean batchMouseInput)
|
||||||
{
|
{
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
@@ -134,11 +135,11 @@ public class NvConnection {
|
|||||||
|
|
||||||
private InetAddress resolveServerAddress() throws IOException {
|
private InetAddress resolveServerAddress() throws IOException {
|
||||||
// Try to find an address that works for this host
|
// Try to find an address that works for this host
|
||||||
InetAddress[] addrs = InetAddress.getAllByName(context.serverAddress);
|
InetAddress[] addrs = InetAddress.getAllByName(context.serverAddress.address);
|
||||||
for (InetAddress addr : addrs) {
|
for (InetAddress addr : addrs) {
|
||||||
try (Socket s = new Socket()) {
|
try (Socket s = new Socket()) {
|
||||||
s.setSoLinger(true, 0);
|
s.setSoLinger(true, 0);
|
||||||
s.connect(new InetSocketAddress(addr, 47989), 1000);
|
s.connect(new InetSocketAddress(addr, context.serverAddress.port), 1000);
|
||||||
return addr;
|
return addr;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -252,7 +253,7 @@ public class NvConnection {
|
|||||||
|
|
||||||
private boolean startApp() throws XmlPullParserException, IOException
|
private boolean startApp() throws XmlPullParserException, IOException
|
||||||
{
|
{
|
||||||
NvHTTP h = new NvHTTP(context.serverAddress, uniqueId, context.serverCert, cryptoProvider);
|
NvHTTP h = new NvHTTP(context.serverAddress, 0, uniqueId, context.serverCert, cryptoProvider);
|
||||||
|
|
||||||
String serverInfo = h.getServerInfo();
|
String serverInfo = h.getServerInfo();
|
||||||
|
|
||||||
@@ -452,7 +453,7 @@ public class NvConnection {
|
|||||||
// we must not invoke that functionality in parallel.
|
// we must not invoke that functionality in parallel.
|
||||||
synchronized (MoonBridge.class) {
|
synchronized (MoonBridge.class) {
|
||||||
MoonBridge.setupBridge(videoDecoderRenderer, audioRenderer, connectionListener);
|
MoonBridge.setupBridge(videoDecoderRenderer, audioRenderer, connectionListener);
|
||||||
int ret = MoonBridge.startConnection(context.serverAddress,
|
int ret = MoonBridge.startConnection(context.serverAddress.address,
|
||||||
context.serverAppVersion, context.serverGfeVersion, context.rtspSessionUrl,
|
context.serverAppVersion, context.serverGfeVersion, context.rtspSessionUrl,
|
||||||
context.negotiatedWidth, context.negotiatedHeight,
|
context.negotiatedWidth, context.negotiatedHeight,
|
||||||
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
||||||
|
|||||||
@@ -8,19 +8,57 @@ public class ComputerDetails {
|
|||||||
ONLINE, OFFLINE, UNKNOWN
|
ONLINE, OFFLINE, UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AddressTuple {
|
||||||
|
public String address;
|
||||||
|
public int port;
|
||||||
|
|
||||||
|
public AddressTuple(String address, int port) {
|
||||||
|
if (address == null) {
|
||||||
|
throw new IllegalArgumentException("Address cannot be null");
|
||||||
|
}
|
||||||
|
if (port <= 0) {
|
||||||
|
throw new IllegalArgumentException("Invalid port");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.address = address;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return address.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof AddressTuple)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressTuple that = (AddressTuple) obj;
|
||||||
|
return address.equals(that.address) && port == that.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return address + ":" + port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Persistent attributes
|
// Persistent attributes
|
||||||
public String uuid;
|
public String uuid;
|
||||||
public String name;
|
public String name;
|
||||||
public String localAddress;
|
public AddressTuple localAddress;
|
||||||
public String remoteAddress;
|
public AddressTuple remoteAddress;
|
||||||
public String manualAddress;
|
public AddressTuple manualAddress;
|
||||||
public String ipv6Address;
|
public AddressTuple ipv6Address;
|
||||||
public String macAddress;
|
public String macAddress;
|
||||||
public X509Certificate serverCert;
|
public X509Certificate serverCert;
|
||||||
|
|
||||||
// Transient attributes
|
// Transient attributes
|
||||||
public State state;
|
public State state;
|
||||||
public String activeAddress;
|
public AddressTuple activeAddress;
|
||||||
|
public int httpsPort;
|
||||||
|
public int externalPort;
|
||||||
public PairingManager.PairState pairState;
|
public PairingManager.PairState pairState;
|
||||||
public int runningGameId;
|
public int runningGameId;
|
||||||
public String rawAppList;
|
public String rawAppList;
|
||||||
@@ -35,6 +73,27 @@ public class ComputerDetails {
|
|||||||
update(details);
|
update(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int guessExternalPort() {
|
||||||
|
if (externalPort != 0) {
|
||||||
|
return externalPort;
|
||||||
|
}
|
||||||
|
else if (remoteAddress != null) {
|
||||||
|
return remoteAddress.port;
|
||||||
|
}
|
||||||
|
else if (activeAddress != null) {
|
||||||
|
return activeAddress.port;
|
||||||
|
}
|
||||||
|
else if (ipv6Address != null) {
|
||||||
|
return ipv6Address.port;
|
||||||
|
}
|
||||||
|
else if (localAddress != null) {
|
||||||
|
return localAddress.port;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NvHTTP.DEFAULT_HTTP_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void update(ComputerDetails details) {
|
public void update(ComputerDetails details) {
|
||||||
this.state = details.state;
|
this.state = details.state;
|
||||||
this.name = details.name;
|
this.name = details.name;
|
||||||
@@ -43,11 +102,16 @@ public class ComputerDetails {
|
|||||||
this.activeAddress = details.activeAddress;
|
this.activeAddress = details.activeAddress;
|
||||||
}
|
}
|
||||||
// We can get IPv4 loopback addresses with GS IPv6 Forwarder
|
// We can get IPv4 loopback addresses with GS IPv6 Forwarder
|
||||||
if (details.localAddress != null && !details.localAddress.startsWith("127.")) {
|
if (details.localAddress != null && !details.localAddress.address.startsWith("127.")) {
|
||||||
this.localAddress = details.localAddress;
|
this.localAddress = details.localAddress;
|
||||||
}
|
}
|
||||||
if (details.remoteAddress != null) {
|
if (details.remoteAddress != null) {
|
||||||
this.remoteAddress = details.remoteAddress;
|
this.remoteAddress = details.remoteAddress;
|
||||||
|
|
||||||
|
// If the port is unknown, populate it from the external port field
|
||||||
|
if (this.remoteAddress.port == 0) {
|
||||||
|
this.remoteAddress.port = externalPort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (details.manualAddress != null) {
|
if (details.manualAddress != null) {
|
||||||
this.manualAddress = details.manualAddress;
|
this.manualAddress = details.manualAddress;
|
||||||
@@ -61,6 +125,8 @@ public class ComputerDetails {
|
|||||||
if (details.serverCert != null) {
|
if (details.serverCert != null) {
|
||||||
this.serverCert = details.serverCert;
|
this.serverCert = details.serverCert;
|
||||||
}
|
}
|
||||||
|
this.externalPort = details.externalPort;
|
||||||
|
this.httpsPort = details.httpsPort;
|
||||||
this.pairState = details.pairState;
|
this.pairState = details.pairState;
|
||||||
this.runningGameId = details.runningGameId;
|
this.runningGameId = details.runningGameId;
|
||||||
this.rawAppList = details.rawAppList;
|
this.rawAppList = details.rawAppList;
|
||||||
@@ -80,6 +146,7 @@ public class ComputerDetails {
|
|||||||
str.append("MAC Address: ").append(macAddress).append("\n");
|
str.append("MAC Address: ").append(macAddress).append("\n");
|
||||||
str.append("Pair State: ").append(pairState).append("\n");
|
str.append("Pair State: ").append(pairState).append("\n");
|
||||||
str.append("Running Game ID: ").append(runningGameId).append("\n");
|
str.append("Running Game ID: ").append(runningGameId).append("\n");
|
||||||
|
str.append("HTTPS Port: ").append(httpsPort).append("\n");
|
||||||
return str.toString();
|
return str.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class NvHTTP {
|
|||||||
private PairingManager pm;
|
private PairingManager pm;
|
||||||
|
|
||||||
private static final int DEFAULT_HTTPS_PORT = 47984;
|
private static final int DEFAULT_HTTPS_PORT = 47984;
|
||||||
public static final int HTTP_PORT = 47989;
|
public static final int DEFAULT_HTTP_PORT = 47989;
|
||||||
public static final int CONNECTION_TIMEOUT = 3000;
|
public static final int CONNECTION_TIMEOUT = 3000;
|
||||||
public static final int READ_TIMEOUT = 5000;
|
public static final int READ_TIMEOUT = 5000;
|
||||||
|
|
||||||
@@ -190,7 +190,7 @@ public class NvHTTP {
|
|||||||
return new HttpUrl.Builder().scheme("https").host(baseUrlHttp.host()).port(httpsPort).build();
|
return new HttpUrl.Builder().scheme("https").host(baseUrlHttp.host()).port(httpsPort).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NvHTTP(String address, String uniqueId, X509Certificate serverCert, LimelightCryptoProvider cryptoProvider) throws IOException {
|
public NvHTTP(ComputerDetails.AddressTuple address, int httpsPort, String uniqueId, X509Certificate serverCert, LimelightCryptoProvider cryptoProvider) throws IOException {
|
||||||
// Use the same UID for all Moonlight clients so we can quit games
|
// Use the same UID for all Moonlight clients so we can quit games
|
||||||
// started by other Moonlight clients.
|
// started by other Moonlight clients.
|
||||||
this.uniqueId = "0123456789ABCDEF";
|
this.uniqueId = "0123456789ABCDEF";
|
||||||
@@ -199,11 +199,13 @@ public class NvHTTP {
|
|||||||
|
|
||||||
initializeHttpState(cryptoProvider);
|
initializeHttpState(cryptoProvider);
|
||||||
|
|
||||||
|
this.httpsPort = httpsPort;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.baseUrlHttp = new HttpUrl.Builder()
|
this.baseUrlHttp = new HttpUrl.Builder()
|
||||||
.scheme("http")
|
.scheme("http")
|
||||||
.host(address)
|
.host(address.address)
|
||||||
.port(HTTP_PORT)
|
.port(address.port)
|
||||||
.build();
|
.build();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// Encapsulate IllegalArgumentException into IOException for callers to handle more easily
|
// Encapsulate IllegalArgumentException into IOException for callers to handle more easily
|
||||||
@@ -323,6 +325,14 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ComputerDetails.AddressTuple makeTuple(String address, int port) {
|
||||||
|
if (address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComputerDetails.AddressTuple(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
public ComputerDetails getComputerDetails() throws IOException, XmlPullParserException {
|
public ComputerDetails getComputerDetails() throws IOException, XmlPullParserException {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
String serverInfo = getServerInfo();
|
String serverInfo = getServerInfo();
|
||||||
@@ -335,11 +345,16 @@ public class NvHTTP {
|
|||||||
// UUID is mandatory to determine which machine is responding
|
// UUID is mandatory to determine which machine is responding
|
||||||
details.uuid = getXmlString(serverInfo, "uniqueid", true);
|
details.uuid = getXmlString(serverInfo, "uniqueid", true);
|
||||||
|
|
||||||
details.macAddress = getXmlString(serverInfo, "mac", false);
|
details.httpsPort = getHttpsPort(serverInfo);
|
||||||
details.localAddress = getXmlString(serverInfo, "LocalIP", false);
|
|
||||||
|
|
||||||
// This is missing on on recent GFE versions
|
details.macAddress = getXmlString(serverInfo, "mac", false);
|
||||||
details.remoteAddress = getXmlString(serverInfo, "ExternalIP", false);
|
|
||||||
|
// FIXME: Do we want to use the current port?
|
||||||
|
details.localAddress = makeTuple(getXmlString(serverInfo, "LocalIP", false), baseUrlHttp.port());
|
||||||
|
|
||||||
|
// This is missing on on recent GFE versions, but it's present on Sunshine
|
||||||
|
details.externalPort = getExternalPort(serverInfo);
|
||||||
|
details.remoteAddress = makeTuple(getXmlString(serverInfo, "ExternalIP", false), details.externalPort);
|
||||||
|
|
||||||
details.pairState = getPairState(serverInfo);
|
details.pairState = getPairState(serverInfo);
|
||||||
details.runningGameId = getCurrentGame(serverInfo);
|
details.runningGameId = getCurrentGame(serverInfo);
|
||||||
@@ -543,6 +558,20 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getExternalPort(String serverInfo) {
|
||||||
|
// This is an extension which is not present in GFE. It is present for Sunshine to be able
|
||||||
|
// to support dynamic HTTP WAN ports without requiring the user to manually enter the port.
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(getXmlString(serverInfo, "ExternalPort", true));
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
// Expected on non-Sunshine servers
|
||||||
|
return DEFAULT_HTTP_PORT;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return DEFAULT_HTTP_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public NvApp getAppById(int appId) throws IOException, XmlPullParserException {
|
public NvApp getAppById(int appId) throws IOException, XmlPullParserException {
|
||||||
LinkedList<NvApp> appList = getAppList();
|
LinkedList<NvApp> appList = getAppList();
|
||||||
for (NvApp appFromList : appList) {
|
for (NvApp appFromList : appList) {
|
||||||
|
|||||||
@@ -10,38 +10,75 @@ import com.limelight.LimeLog;
|
|||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
|
||||||
public class WakeOnLanSender {
|
public class WakeOnLanSender {
|
||||||
private static final int[] PORTS_TO_TRY = new int[] {
|
// These ports will always be tried as-is.
|
||||||
|
private static final int[] STATIC_PORTS_TO_TRY = new int[] {
|
||||||
9, // Standard WOL port (privileged port)
|
9, // Standard WOL port (privileged port)
|
||||||
47998, 47999, 48000, 48002, 48010, // Ports opened by GFE
|
|
||||||
47009, // Port opened by Moonlight Internet Hosting Tool for WoL (non-privileged port)
|
47009, // Port opened by Moonlight Internet Hosting Tool for WoL (non-privileged port)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// These ports will be offset by the base port number (47989) to support alternate ports.
|
||||||
|
private static final int[] DYNAMIC_PORTS_TO_TRY = new int[] {
|
||||||
|
47998, 47999, 48000, 48002, 48010, // Ports opened by GFE
|
||||||
|
};
|
||||||
|
|
||||||
|
private static void sendPacketsForAddress(InetAddress address, int httpPort, DatagramSocket sock, byte[] payload) throws IOException {
|
||||||
|
IOException lastException = null;
|
||||||
|
boolean sentWolPacket = false;
|
||||||
|
|
||||||
|
// Try the static ports
|
||||||
|
for (int port : STATIC_PORTS_TO_TRY) {
|
||||||
|
try {
|
||||||
|
DatagramPacket dp = new DatagramPacket(payload, payload.length);
|
||||||
|
dp.setAddress(address);
|
||||||
|
dp.setPort(port);
|
||||||
|
sock.send(dp);
|
||||||
|
sentWolPacket = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
lastException = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the dynamic ports
|
||||||
|
for (int port : DYNAMIC_PORTS_TO_TRY) {
|
||||||
|
try {
|
||||||
|
DatagramPacket dp = new DatagramPacket(payload, payload.length);
|
||||||
|
dp.setAddress(address);
|
||||||
|
dp.setPort((port - 47989) + httpPort);
|
||||||
|
sock.send(dp);
|
||||||
|
sentWolPacket = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
lastException = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sentWolPacket) {
|
||||||
|
throw lastException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void sendWolPacket(ComputerDetails computer) throws IOException {
|
public static void sendWolPacket(ComputerDetails computer) throws IOException {
|
||||||
byte[] payload = createWolPayload(computer);
|
byte[] payload = createWolPayload(computer);
|
||||||
IOException lastException = null;
|
IOException lastException = null;
|
||||||
boolean sentWolPacket = false;
|
boolean sentWolPacket = false;
|
||||||
|
|
||||||
try (final DatagramSocket sock = new DatagramSocket(0)) {
|
try (final DatagramSocket sock = new DatagramSocket(0)) {
|
||||||
// Try all resolved remote and local addresses and IPv4 broadcast address.
|
// Try all resolved remote and local addresses and broadcast addresses.
|
||||||
// The broadcast address is required to avoid stale ARP cache entries
|
// The broadcast address is required to avoid stale ARP cache entries
|
||||||
// making the sleeping machine unreachable.
|
// making the sleeping machine unreachable.
|
||||||
for (String unresolvedAddress : new String[] {
|
for (ComputerDetails.AddressTuple address : new ComputerDetails.AddressTuple[] {
|
||||||
computer.localAddress, computer.remoteAddress, computer.manualAddress, computer.ipv6Address, "255.255.255.255"
|
computer.localAddress, computer.remoteAddress,
|
||||||
|
computer.manualAddress, computer.ipv6Address,
|
||||||
}) {
|
}) {
|
||||||
if (unresolvedAddress == null) {
|
if (address == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (InetAddress resolvedAddress : InetAddress.getAllByName(unresolvedAddress)) {
|
sendPacketsForAddress(InetAddress.getByName("255.255.255.255"), address.port, sock, payload);
|
||||||
// Try all the ports for each resolved address
|
for (InetAddress resolvedAddress : InetAddress.getAllByName(address.address)) {
|
||||||
for (int port : PORTS_TO_TRY) {
|
sendPacketsForAddress(resolvedAddress, address.port, sock, payload);
|
||||||
DatagramPacket dp = new DatagramPacket(payload, payload.length);
|
|
||||||
dp.setAddress(resolvedAddress);
|
|
||||||
dp.setPort(port);
|
|
||||||
sock.send(dp);
|
|
||||||
sentWolPacket = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// We may have addresses that don't resolve on this subnet,
|
// We may have addresses that don't resolve on this subnet,
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.InterfaceAddress;
|
import java.net.InterfaceAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
@@ -96,7 +98,7 @@ public class AddComputerManually extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doAddPc(String host) throws InterruptedException {
|
private void doAddPc(String rawUserInput) throws InterruptedException {
|
||||||
boolean wrongSiteLocal = false;
|
boolean wrongSiteLocal = false;
|
||||||
boolean success;
|
boolean success;
|
||||||
int portTestResult;
|
int portTestResult;
|
||||||
@@ -106,8 +108,28 @@ public class AddComputerManually extends Activity {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
ComputerDetails details = new ComputerDetails();
|
ComputerDetails details = new ComputerDetails();
|
||||||
details.manualAddress = host;
|
|
||||||
|
// Use URI-style parsing for the host address input
|
||||||
|
URI uri = new URI("moonlight://" + rawUserInput);
|
||||||
|
|
||||||
|
String host = uri.getHost();
|
||||||
|
int port = uri.getPort();
|
||||||
|
|
||||||
|
// URI allows empty hosts, but we don't want that
|
||||||
|
if (host == null || host.isEmpty()) {
|
||||||
|
throw new URISyntaxException(rawUserInput, "Host failed to parse");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a port was not specified, use the default
|
||||||
|
if (port == -1) {
|
||||||
|
port = NvHTTP.DEFAULT_HTTP_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
details.manualAddress = new ComputerDetails.AddressTuple(host, port);
|
||||||
success = managerBinder.addComputerBlocking(details);
|
success = managerBinder.addComputerBlocking(details);
|
||||||
|
if (!success){
|
||||||
|
wrongSiteLocal = isWrongSubnetSiteLocalAddress(host);
|
||||||
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// Propagate the InterruptedException to the caller for proper handling
|
// Propagate the InterruptedException to the caller for proper handling
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
@@ -117,12 +139,12 @@ public class AddComputerManually extends Activity {
|
|||||||
// https://github.com/square/okhttp/blob/okhttp_27/okhttp/src/main/java/com/squareup/okhttp/HttpUrl.java#L705
|
// https://github.com/square/okhttp/blob/okhttp_27/okhttp/src/main/java/com/squareup/okhttp/HttpUrl.java#L705
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
success = false;
|
success = false;
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the SpinnerDialog open while testing connectivity
|
// Keep the SpinnerDialog open while testing connectivity
|
||||||
if (!success){
|
|
||||||
wrongSiteLocal = isWrongSubnetSiteLocalAddress(host);
|
|
||||||
}
|
|
||||||
if (!success && !wrongSiteLocal) {
|
if (!success && !wrongSiteLocal) {
|
||||||
// Run the test before dismissing the spinner because it can take a few seconds.
|
// Run the test before dismissing the spinner because it can take a few seconds.
|
||||||
portTestResult = MoonBridge.testClientConnectivity(ServerHelper.CONNECTION_TEST_SERVER, 443,
|
portTestResult = MoonBridge.testClientConnectivity(ServerHelper.CONNECTION_TEST_SERVER, 443,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import java.security.cert.CertificateEncodingException;
|
|||||||
public class ServerHelper {
|
public class ServerHelper {
|
||||||
public static final String CONNECTION_TEST_SERVER = "android.conntest.moonlight-stream.org";
|
public static final String CONNECTION_TEST_SERVER = "android.conntest.moonlight-stream.org";
|
||||||
|
|
||||||
public static String getCurrentAddressFromComputer(ComputerDetails computer) throws IOException {
|
public static ComputerDetails.AddressTuple getCurrentAddressFromComputer(ComputerDetails computer) throws IOException {
|
||||||
if (computer.activeAddress == null) {
|
if (computer.activeAddress == null) {
|
||||||
throw new IOException("No active address for "+computer.name);
|
throw new IOException("No active address for "+computer.name);
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,8 @@ public class ServerHelper {
|
|||||||
public static Intent createStartIntent(Activity parent, NvApp app, ComputerDetails computer,
|
public static Intent createStartIntent(Activity parent, NvApp app, ComputerDetails computer,
|
||||||
ComputerManagerService.ComputerManagerBinder managerBinder) {
|
ComputerManagerService.ComputerManagerBinder managerBinder) {
|
||||||
Intent intent = new Intent(parent, Game.class);
|
Intent intent = new Intent(parent, Game.class);
|
||||||
intent.putExtra(Game.EXTRA_HOST, computer.activeAddress);
|
intent.putExtra(Game.EXTRA_HOST, computer.activeAddress.address);
|
||||||
|
intent.putExtra(Game.EXTRA_PORT, computer.activeAddress.port);
|
||||||
intent.putExtra(Game.EXTRA_APP_NAME, app.getAppName());
|
intent.putExtra(Game.EXTRA_APP_NAME, app.getAppName());
|
||||||
intent.putExtra(Game.EXTRA_APP_ID, app.getAppId());
|
intent.putExtra(Game.EXTRA_APP_ID, app.getAppId());
|
||||||
intent.putExtra(Game.EXTRA_APP_HDR, app.isHdrSupported());
|
intent.putExtra(Game.EXTRA_APP_HDR, app.isHdrSupported());
|
||||||
@@ -126,7 +127,7 @@ public class ServerHelper {
|
|||||||
NvHTTP httpConn;
|
NvHTTP httpConn;
|
||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), computer.httpsPort,
|
||||||
managerBinder.getUniqueId(), computer.serverCert, PlatformBinding.getCryptoProvider(parent));
|
managerBinder.getUniqueId(), computer.serverCert, PlatformBinding.getCryptoProvider(parent));
|
||||||
if (httpConn.quitApp()) {
|
if (httpConn.quitApp()) {
|
||||||
message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName();
|
message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName();
|
||||||
|
|||||||
Reference in New Issue
Block a user