Compare commits

...

19 Commits

Author SHA1 Message Date
Cameron Gutman 997898c99d Version 6.1.3 2019-01-04 18:20:28 -08:00
Cameron Gutman 1174e03885 Fix incorrectly persisting host with missing server cert 2019-01-04 18:18:32 -08:00
Cameron Gutman ff0f54d541 Switch to using stun.moonlight-stream.org for STUN 2019-01-04 18:05:28 -08:00
Cameron Gutman 814964a100 Fix exception adding PCs 2019-01-01 23:32:16 -08:00
Cameron Gutman 7e154292a9 Stop suppressing exceptions 2019-01-01 23:31:38 -08:00
Cameron Gutman 0f9cba1053 Fix crash due to a null computer uuid 2019-01-01 22:34:27 -08:00
Cameron Gutman a4e134589d Version 6.1.1 2018-12-27 23:58:30 -08:00
Cameron Gutman cd80a94f28 Fix IllegalStateException caused by making HTTPS request without a pinned cert 2018-12-27 23:55:59 -08:00
Cameron Gutman 57c645a291 Change uuid field to String type due to new format UUIDs that fail to parse on GFE 3.16 2018-12-27 23:48:12 -08:00
Cameron Gutman 0cba200207 Version 6.1 2018-12-24 19:58:51 -08:00
Cameron Gutman 81582d7343 Revert "Hide the mouse cursor during pointer capture to work around DeX bug"
It doesn't actually fix the bug.

This reverts commit 16b845ab84.
2018-12-24 19:58:19 -08:00
Cameron Gutman 04e561fd54 Update common-c with bitrate fix 2018-12-24 19:56:42 -08:00
Cameron Gutman 5efbb5229d Fix up French translation 2018-12-24 19:09:16 -08:00
bubuleur 541e43eb18 Update Translation french Moonlight (#648)
* Update Translation french Moonlight

Hello
Herewith updated French language for your next Moonlight update
If you want you can contact me on igorlachaudarobaseaol.fr to update your application in French before an exit
cordially
Merci pour tous

* Update strings.xml
2018-12-24 19:07:38 -08:00
Cameron Gutman 7e679ff4c6 Fix short window where newly added PC could be incorrectly marked as unpaired 2018-12-23 21:34:20 -08:00
Cameron Gutman 486b4b4c4c Use a shared UID for all Moonlight clients 2018-12-22 21:03:42 -08:00
Cameron Gutman 7d76bf7868 Require cert pinning for HTTPS 2018-12-22 20:13:11 -08:00
Cameron Gutman db49077b9b Add cert pinning during pairing 2018-12-21 21:00:53 -08:00
Cameron Gutman 16b845ab84 Hide the mouse cursor during pointer capture to work around DeX bug 2018-12-19 15:06:46 +05:00
19 changed files with 229 additions and 152 deletions
+2 -2
View File
@@ -8,8 +8,8 @@ android {
minSdkVersion 16
targetSdkVersion 28
versionName "6.0.1"
versionCode = 181
versionName "6.1.3"
versionCode = 185
}
flavorDimensions "root"
+11 -9
View File
@@ -1,8 +1,8 @@
package com.limelight;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.UUID;
import com.limelight.computers.ComputerManagerListener;
import com.limelight.computers.ComputerManagerService;
@@ -28,7 +28,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -45,6 +44,8 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import org.xmlpull.v1.XmlPullParserException;
public class AppView extends Activity implements AdapterFragmentCallbacks {
private AppGridAdapter appGridAdapter;
private String uuidString;
@@ -82,7 +83,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
localBinder.waitForReady();
// Get the computer object
computer = localBinder.getComputer(UUID.fromString(uuidString));
computer = localBinder.getComputer(uuidString);
if (computer == null) {
finish();
return;
@@ -156,7 +157,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
}
// Don't care about other computers
if (!details.uuid.toString().equalsIgnoreCase(uuidString)) {
if (!details.uuid.equalsIgnoreCase(uuidString)) {
return;
}
@@ -180,7 +181,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
@Override
public void run() {
// Disable shortcuts referencing this PC for now
shortcutHelper.disableShortcut(details.uuid.toString(),
shortcutHelper.disableShortcut(details.uuid,
getResources().getString(R.string.scut_not_paired));
// Display a toast to the user and quit the activity
@@ -216,7 +217,9 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
blockingLoadSpinner.dismiss();
blockingLoadSpinner = null;
}
} catch (Exception ignored) {}
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
}
});
@@ -280,7 +283,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
List<NvApp> applist = NvHTTP.getAppListByReader(new StringReader(lastRawApplist));
updateUiWithAppList(applist);
LimeLog.info("Loaded applist from cache");
} catch (Exception e) {
} catch (IOException | XmlPullParserException e) {
if (lastRawApplist != null) {
LimeLog.warning("Saved applist corrupted: "+lastRawApplist);
e.printStackTrace();
@@ -390,8 +393,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
@Override
public void run() {
suspendGridUpdates = true;
ServerHelper.doQuit(AppView.this,
ServerHelper.getCurrentAddressFromComputer(computer),
ServerHelper.doQuit(AppView.this, computer,
app.app, managerBinder, new Runnable() {
@Override
public void run() {
+18 -1
View File
@@ -68,6 +68,11 @@ import android.widget.FrameLayout;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class Game extends Activity implements SurfaceHolder.Callback,
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
@@ -135,6 +140,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
public static final String EXTRA_PC_UUID = "UUID";
public static final String EXTRA_PC_NAME = "PcName";
public static final String EXTRA_APP_HDR = "HDR";
public static final String EXTRA_SERVER_CERT = "ServerCert";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -228,6 +234,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
String uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
String pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME);
boolean willStreamHdr = Game.this.getIntent().getBooleanExtra(EXTRA_APP_HDR, false);
byte[] derCertData = Game.this.getIntent().getByteArrayExtra(EXTRA_SERVER_CERT);
X509Certificate serverCert = null;
try {
if (derCertData != null) {
serverCert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(derCertData));
}
} catch (CertificateException e) {
e.printStackTrace();
}
if (appId == StreamConfiguration.INVALID_APP_ID) {
finish();
@@ -386,7 +403,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
.build();
// Initialize the connection
conn = new NvConnection(host, uniqueId, config, PlatformBinding.getCryptoProvider(this));
conn = new NvConnection(host, uniqueId, config, PlatformBinding.getCryptoProvider(this), serverCert);
controllerHandler = new ControllerHandler(this, conn, this, prefConfig);
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
+22 -15
View File
@@ -53,6 +53,8 @@ import android.widget.RelativeLayout;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
import org.xmlpull.v1.XmlPullParserException;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -373,9 +375,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
managerBinder.getUniqueId(),
PlatformBinding.getDeviceName(),
computer.serverCert,
PlatformBinding.getCryptoProvider(PcView.this));
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
if (httpConn.getPairState() == PairState.PAIRED) {
// Don't display any toast, but open the app list
message = null;
success = true;
@@ -387,21 +389,26 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
Dialog.displayDialog(PcView.this, getResources().getString(R.string.pair_pairing_title),
getResources().getString(R.string.pair_pairing_msg)+" "+pinStr, false);
PairingManager.PairState pairState = httpConn.pair(httpConn.getServerInfo(), pinStr);
if (pairState == PairingManager.PairState.PIN_WRONG) {
PairingManager pm = httpConn.getPairingManager();
PairState pairState = pm.pair(httpConn.getServerInfo(), pinStr);
if (pairState == PairState.PIN_WRONG) {
message = getResources().getString(R.string.pair_incorrect_pin);
}
else if (pairState == PairingManager.PairState.FAILED) {
else if (pairState == PairState.FAILED) {
message = getResources().getString(R.string.pair_fail);
}
else if (pairState == PairingManager.PairState.ALREADY_IN_PROGRESS) {
else if (pairState == PairState.ALREADY_IN_PROGRESS) {
message = getResources().getString(R.string.pair_already_in_progress);
}
else if (pairState == PairingManager.PairState.PAIRED) {
else if (pairState == PairState.PAIRED) {
// Just navigate to the app view without displaying a toast
message = null;
success = true;
// Pin this certificate for later HTTPS use
managerBinder.getComputer(computer.uuid).serverCert = pm.getPairedCert();
// Invalidate reachability information after pairing to force
// a refresh before reading pair state again
managerBinder.invalidateStateForComputer(computer.uuid);
@@ -415,7 +422,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
message = getResources().getString(R.string.error_unknown_host);
} catch (FileNotFoundException e) {
message = getResources().getString(R.string.error_404);
} catch (Exception e) {
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
message = e.getMessage();
}
@@ -498,7 +505,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
try {
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
managerBinder.getUniqueId(),
PlatformBinding.getDeviceName(),
computer.serverCert,
PlatformBinding.getCryptoProvider(PcView.this));
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
httpConn.unpair();
@@ -516,8 +523,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
message = getResources().getString(R.string.error_unknown_host);
} catch (FileNotFoundException e) {
message = getResources().getString(R.string.error_404);
} catch (Exception e) {
} catch (XmlPullParserException | IOException e) {
message = e.getMessage();
e.printStackTrace();
}
final String toastMessage = message;
@@ -543,7 +551,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
Intent i = new Intent(this, AppView.class);
i.putExtra(AppView.NAME_EXTRA, computer.name);
i.putExtra(AppView.UUID_EXTRA, computer.uuid.toString());
i.putExtra(AppView.UUID_EXTRA, computer.uuid);
startActivity(i);
}
@@ -605,8 +613,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
UiHelper.displayQuitConfirmationDialog(this, new Runnable() {
@Override
public void run() {
ServerHelper.doQuit(PcView.this,
ServerHelper.getCurrentAddressFromComputer(computer.details),
ServerHelper.doQuit(PcView.this, computer.details,
new NvApp("app", 0, false), managerBinder, null);
}
}, null);
@@ -627,7 +634,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
if (details.equals(computer.details)) {
// Disable or delete shortcuts referencing this PC
shortcutHelper.disableShortcut(details.uuid.toString(),
shortcutHelper.disableShortcut(details.uuid,
getResources().getString(R.string.scut_deleted_pc));
pcGridAdapter.removeComputer(computer);
@@ -658,7 +665,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
// Add a launcher shortcut for this PC
if (details.pairState == PairState.PAIRED) {
shortcutHelper.createAppViewShortcut(details.uuid.toString(), details, false);
shortcutHelper.createAppViewShortcut(details.uuid, details, false);
}
if (existingEntry != null) {
@@ -48,7 +48,7 @@ public class ShortcutTrampoline extends Activity {
managerBinder = localBinder;
// Get the computer object
computer = managerBinder.getComputer(UUID.fromString(uuidString));
computer = managerBinder.getComputer(uuidString);
if (computer == null) {
Dialog.displayDialog(ShortcutTrampoline.this,
@@ -77,7 +77,7 @@ public class ShortcutTrampoline extends Activity {
@Override
public void notifyComputerUpdated(final ComputerDetails details) {
// Don't care about other computers
if (!details.uuid.toString().equalsIgnoreCase(uuidString)) {
if (!details.uuid.equalsIgnoreCase(uuidString)) {
return;
}
@@ -12,7 +12,6 @@ import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
@@ -155,7 +154,7 @@ public class AndroidCryptoProvider implements LimelightCryptoProvider {
} catch (Exception e) {
// Nothing should go wrong here
e.printStackTrace();
return false;
throw new RuntimeException(e);
}
LimeLog.info("Generated a new key pair");
@@ -167,26 +167,22 @@ public class VirtualController {
}
void sendControllerInputContext() {
try {
_DBG("INPUT_MAP + " + inputContext.inputMap);
_DBG("LEFT_TRIGGER " + inputContext.leftTrigger);
_DBG("RIGHT_TRIGGER " + inputContext.rightTrigger);
_DBG("LEFT STICK X: " + inputContext.leftStickX + " Y: " + inputContext.leftStickY);
_DBG("RIGHT STICK X: " + inputContext.rightStickX + " Y: " + inputContext.rightStickY);
_DBG("INPUT_MAP + " + inputContext.inputMap);
_DBG("LEFT_TRIGGER " + inputContext.leftTrigger);
_DBG("RIGHT_TRIGGER " + inputContext.rightTrigger);
_DBG("LEFT STICK X: " + inputContext.leftStickX + " Y: " + inputContext.leftStickY);
_DBG("RIGHT STICK X: " + inputContext.rightStickX + " Y: " + inputContext.rightStickY);
if (connection != null) {
connection.sendControllerInput(
inputContext.inputMap,
inputContext.leftTrigger,
inputContext.rightTrigger,
inputContext.leftStickX,
inputContext.leftStickY,
inputContext.rightStickX,
inputContext.rightStickY
);
}
} catch (Exception e) {
e.printStackTrace();
if (connection != null) {
connection.sendControllerInput(
inputContext.inputMap,
inputContext.leftTrigger,
inputContext.rightTrigger,
inputContext.leftStickX,
inputContext.leftStickY,
inputContext.rightStickX,
inputContext.rightStickY
);
}
}
}
@@ -166,58 +166,54 @@ public abstract class VirtualControllerElement extends View {
}
protected void showConfigurationDialog() {
try {
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getContext());
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getContext());
alertBuilder.setTitle("Configuration");
alertBuilder.setTitle("Configuration");
CharSequence functions[] = new CharSequence[]{
"Move",
"Resize",
/*election
"Set n
Disable color sormal color",
"Set pressed color",
CharSequence functions[] = new CharSequence[]{
"Move",
"Resize",
/*election
"Set n
Disable color sormal color",
"Set pressed color",
*/
"Cancel"
};
alertBuilder.setItems(functions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0: { // move
actionEnableMove();
break;
}
case 1: { // resize
actionEnableResize();
break;
}
/*
case 2: { // set default color
actionShowNormalColorChooser();
break;
}
case 3: { // set pressed color
actionShowPressedColorChooser();
break;
}
*/
"Cancel"
};
alertBuilder.setItems(functions, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0: { // move
actionEnableMove();
break;
}
case 1: { // resize
actionEnableResize();
break;
}
/*
case 2: { // set default color
actionShowNormalColorChooser();
default: { // cancel
actionCancel();
break;
}
case 3: { // set pressed color
actionShowPressedColorChooser();
break;
}
*/
default: { // cancel
actionCancel();
break;
}
}
}
});
AlertDialog alert = alertBuilder.create();
// show menu
alert.show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
AlertDialog alert = alertBuilder.create();
// show menu
alert.show();
}
@Override
@@ -1,11 +1,14 @@
package com.limelight.computers;
import java.io.ByteArrayInputStream;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import com.limelight.LimeLog;
import com.limelight.nvstream.http.ComputerDetails;
import android.content.ContentValues;
@@ -23,6 +26,7 @@ public class ComputerDatabaseManager {
private static final String REMOTE_ADDRESS_COLUMN_NAME = "RemoteAddress";
private static final String MANUAL_ADDRESS_COLUMN_NAME = "ManualAddress";
private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress";
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
private SQLiteDatabase computerDb;
@@ -43,13 +47,21 @@ public class ComputerDatabaseManager {
}
private void initializeDb(Context c) {
// Add cert column to the table if not present
try {
computerDb.execSQL(String.format((Locale)null,
"ALTER TABLE %s ADD COLUMN %s TEXT",
COMPUTER_TABLE_NAME, SERVER_CERT_COLUMN_NAME));
} catch (SQLiteException e) {}
// Create tables if they aren't already there
computerDb.execSQL(String.format((Locale)null,
"CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY, %s TEXT NOT NULL, %s TEXT, %s TEXT, %s TEXT, %s TEXT)",
"CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY, %s TEXT NOT NULL, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT)",
COMPUTER_TABLE_NAME,
COMPUTER_UUID_COLUMN_NAME, COMPUTER_NAME_COLUMN_NAME,
LOCAL_ADDRESS_COLUMN_NAME, REMOTE_ADDRESS_COLUMN_NAME, MANUAL_ADDRESS_COLUMN_NAME,
MAC_ADDRESS_COLUMN_NAME));
MAC_ADDRESS_COLUMN_NAME, SERVER_CERT_COLUMN_NAME));
// Move all computers from the old DB (if any) to the new one
List<ComputerDetails> oldComputers = LegacyDatabaseReader.migrateAllComputers(c);
@@ -64,32 +76,47 @@ public class ComputerDatabaseManager {
public boolean updateComputer(ComputerDetails details) {
ContentValues values = new ContentValues();
values.put(COMPUTER_UUID_COLUMN_NAME, details.uuid.toString());
values.put(COMPUTER_UUID_COLUMN_NAME, details.uuid);
values.put(COMPUTER_NAME_COLUMN_NAME, details.name);
values.put(LOCAL_ADDRESS_COLUMN_NAME, details.localAddress);
values.put(REMOTE_ADDRESS_COLUMN_NAME, details.remoteAddress);
values.put(MANUAL_ADDRESS_COLUMN_NAME, details.manualAddress);
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
try {
if (details.serverCert != null) {
values.put(SERVER_CERT_COLUMN_NAME, details.serverCert.getEncoded());
}
else {
values.put(SERVER_CERT_COLUMN_NAME, (byte[])null);
}
} catch (CertificateEncodingException e) {
values.put(SERVER_CERT_COLUMN_NAME, (byte[])null);
e.printStackTrace();
}
return -1 != computerDb.insertWithOnConflict(COMPUTER_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
private ComputerDetails getComputerFromCursor(Cursor c) {
ComputerDetails details = new ComputerDetails();
String uuidStr = c.getString(0);
try {
details.uuid = UUID.fromString(uuidStr);
} catch (IllegalArgumentException e) {
// We'll delete this entry
LimeLog.severe("DB: Corrupted UUID for "+details.name);
}
details.uuid = c.getString(0);
details.name = c.getString(1);
details.localAddress = c.getString(2);
details.remoteAddress = c.getString(3);
details.manualAddress = c.getString(4);
details.macAddress = c.getString(5);
try {
byte[] derCertData = c.getBlob(6);
if (derCertData != null) {
details.serverCert = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(derCertData));
}
} catch (CertificateException e) {
e.printStackTrace();
}
// This signifies we don't have dynamic state (like pair state)
details.state = ComputerDetails.State.UNKNOWN;
@@ -115,8 +142,8 @@ public class ComputerDatabaseManager {
return computerList;
}
public ComputerDetails getComputerByUUID(UUID uuid) {
Cursor c = computerDb.query(COMPUTER_TABLE_NAME, null, COMPUTER_UUID_COLUMN_NAME+"=?", new String[]{ uuid.toString() }, null, null, null);
public ComputerDetails getComputerByUUID(String uuid) {
Cursor c = computerDb.query(COMPUTER_TABLE_NAME, null, COMPUTER_UUID_COLUMN_NAME+"=?", new String[]{ uuid }, null, null, null);
if (!c.moveToFirst()) {
// No matching computer
c.close();
@@ -8,7 +8,6 @@ import java.net.Socket;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import com.limelight.LimeLog;
@@ -18,6 +17,7 @@ import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.http.ComputerDetails;
import com.limelight.nvstream.http.NvApp;
import com.limelight.nvstream.http.NvHTTP;
import com.limelight.nvstream.http.PairingManager;
import com.limelight.nvstream.mdns.MdnsComputer;
import com.limelight.nvstream.mdns.MdnsDiscoveryListener;
import com.limelight.utils.CacheHelper;
@@ -241,7 +241,7 @@ public class ComputerManagerService extends Service {
return idManager.getUniqueId();
}
public ComputerDetails getComputer(UUID uuid) {
public ComputerDetails getComputer(String uuid) {
synchronized (pollingTuples) {
for (PollingTuple tuple : pollingTuples) {
if (uuid.equals(tuple.computer.uuid)) {
@@ -253,7 +253,7 @@ public class ComputerManagerService extends Service {
return null;
}
public void invalidateStateForComputer(UUID uuid) {
public void invalidateStateForComputer(String uuid) {
synchronized (pollingTuples) {
for (PollingTuple tuple : pollingTuples) {
if (uuid.equals(tuple.computer.uuid)) {
@@ -360,12 +360,28 @@ public class ComputerManagerService extends Service {
// Since we're on the same network, we can use STUN to find
// our WAN address, which is also very likely the WAN address
// of the PC. We can use this later to connect remotely.
fakeDetails.remoteAddress = NvConnection.findExternalAddressForMdns();
fakeDetails.remoteAddress = NvConnection.findExternalAddressForMdns("stun.moonlight-stream.org", 3478);
}
// Block while we try to fill the details
try {
runPoll(fakeDetails, true, 0);
// We cannot use runPoll() here because it will attempt to persist the state of the machine
// in the database, which would be bad because we don't have our pinned cert loaded yet.
if (pollComputer(fakeDetails)) {
// See if we have record of this PC to pull its pinned cert
synchronized (pollingTuples) {
for (PollingTuple tuple : pollingTuples) {
if (tuple.computer.uuid.equals(fakeDetails.uuid)) {
fakeDetails.serverCert = tuple.computer.serverCert;
break;
}
}
}
// Poll again, possibly with the pinned cert, to get accurate pairing information.
// This will insert the host into the database too.
runPoll(fakeDetails, true, 0);
}
} catch (InterruptedException e) {
return false;
}
@@ -435,14 +451,18 @@ public class ComputerManagerService extends Service {
}
try {
NvHTTP http = new NvHTTP(address, idManager.getUniqueId(),
null, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
NvHTTP http = new NvHTTP(address, idManager.getUniqueId(), details.serverCert,
PlatformBinding.getCryptoProvider(ComputerManagerService.this));
ComputerDetails newDetails = http.getComputerDetails();
// Check if this is the PC we expected
if (details.uuid != null && newDetails.uuid != null &&
!details.uuid.equals(newDetails.uuid)) {
if (newDetails.uuid == null) {
LimeLog.severe("Polling returned no UUID!");
return null;
}
// details.uuid can be null on initial PC add
else if (details.uuid != null && !details.uuid.equals(newDetails.uuid)) {
// We got the wrong PC!
LimeLog.info("Polling returned the wrong PC!");
return null;
@@ -452,7 +472,8 @@ public class ComputerManagerService extends Service {
newDetails.activeAddress = address;
return newDetails;
} catch (Exception e) {
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
return null;
}
}
@@ -681,8 +702,9 @@ public class ComputerManagerService extends Service {
public void run() {
int emptyAppListResponses = 0;
do {
// Can't poll if it's not online
if (computer.state != ComputerDetails.State.ONLINE) {
// Can't poll if it's not online or paired
if (computer.state != ComputerDetails.State.ONLINE ||
computer.pairState != PairingManager.PairState.PAIRED) {
if (listener != null) {
listener.notifyComputerUpdated(computer);
}
@@ -698,7 +720,7 @@ public class ComputerManagerService extends Service {
try {
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), idManager.getUniqueId(),
null, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
computer.serverCert, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
String appList;
if (tuple != null) {
@@ -722,12 +744,12 @@ public class ComputerManagerService extends Service {
// in a row, we'll go ahead and believe it.
emptyAppListResponses++;
}
if (appList != null && !appList.isEmpty() &&
if (!appList.isEmpty() &&
(!list.isEmpty() || emptyAppListResponses >= EMPTY_LIST_THRESHOLD)) {
// Open the cache file
OutputStream cacheOut = null;
try {
cacheOut = CacheHelper.openCacheFileForOutput(getCacheDir(), "applist", computer.uuid.toString());
cacheOut = CacheHelper.openCacheFileForOutput(getCacheDir(), "applist", computer.uuid);
CacheHelper.writeStringToOutputStream(cacheOut, appList);
} catch (IOException e) {
e.printStackTrace();
@@ -754,7 +776,7 @@ public class ComputerManagerService extends Service {
listener.notifyComputerUpdated(computer);
}
}
else if (appList == null || appList.isEmpty()) {
else if (appList.isEmpty()) {
LimeLog.warning("Null app list received from "+computer.uuid);
}
} catch (IOException e) {
@@ -12,7 +12,6 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
public class LegacyDatabaseReader {
private static final String COMPUTER_DB_NAME = "computers.db";
@@ -24,14 +23,7 @@ public class LegacyDatabaseReader {
ComputerDetails details = new ComputerDetails();
details.name = c.getString(0);
String uuidStr = c.getString(1);
try {
details.uuid = UUID.fromString(uuidStr);
} catch (IllegalArgumentException e) {
// We'll delete this entry
LimeLog.severe("DB: Corrupted UUID for " + details.name);
}
details.uuid = c.getString(1);
// An earlier schema defined addresses as byte blobs. We'll
// gracefully migrate those to strings so we can store DNS names
@@ -39,7 +39,7 @@ public class DiskAssetLoader {
}
public boolean checkCacheExists(CachedAppAssetLoader.LoaderTuple tuple) {
return CacheHelper.cacheFileExists(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png");
return CacheHelper.cacheFileExists(cacheDir, "boxart", tuple.computer.uuid, tuple.app.getAppId() + ".png");
}
// https://developer.android.com/topic/performance/graphics/load-bitmap.html
@@ -65,7 +65,7 @@ public class DiskAssetLoader {
}
public Bitmap loadBitmapFromCache(CachedAppAssetLoader.LoaderTuple tuple, int sampleSize) {
File file = CacheHelper.openPath(false, cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png");
File file = CacheHelper.openPath(false, cacheDir, "boxart", tuple.computer.uuid, tuple.app.getAppId() + ".png");
// Don't bother with anything if it doesn't exist
if (!file.exists()) {
@@ -137,7 +137,7 @@ public class DiskAssetLoader {
OutputStream out = null;
boolean success = false;
try {
out = CacheHelper.openCacheFileForOutput(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png");
out = CacheHelper.openCacheFileForOutput(cacheDir, "boxart", tuple.computer.uuid, tuple.app.getAppId() + ".png");
CacheHelper.writeInputStreamToOutputStream(input, out, MAX_ASSET_SIZE);
success = true;
} catch (IOException e) {
@@ -151,7 +151,7 @@ public class DiskAssetLoader {
if (!success) {
LimeLog.warning("Unable to populate cache with tuple: "+tuple);
CacheHelper.deleteCacheFile(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png");
CacheHelper.deleteCacheFile(cacheDir, "boxart", tuple.computer.uuid, tuple.app.getAppId() + ".png");
}
}
}
@@ -16,7 +16,7 @@ public class MemoryAssetLoader {
};
private static String constructKey(CachedAppAssetLoader.LoaderTuple tuple) {
return tuple.computer.uuid.toString()+"-"+tuple.app.getAppId();
return tuple.computer.uuid+"-"+tuple.app.getAppId();
}
public Bitmap loadBitmapFromCache(CachedAppAssetLoader.LoaderTuple tuple) {
@@ -22,7 +22,8 @@ public class NetworkAssetLoader {
public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) {
InputStream in = null;
try {
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer), uniqueId, null, PlatformBinding.getCryptoProvider(context));
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer), uniqueId,
tuple.computer.serverCert, PlatformBinding.getCryptoProvider(context));
in = http.getBoxArt(tuple.app);
} catch (IOException ignored) {}
@@ -3,7 +3,6 @@ package com.limelight.preferences;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.DialogPreference;
import android.util.AttributeSet;
@@ -13,8 +13,12 @@ import com.limelight.nvstream.http.GfeHttpResponseException;
import com.limelight.nvstream.http.NvApp;
import com.limelight.nvstream.http.NvHTTP;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.cert.CertificateEncodingException;
public class ServerHelper {
public static String getCurrentAddressFromComputer(ComputerDetails computer) {
@@ -29,8 +33,15 @@ public class ServerHelper {
intent.putExtra(Game.EXTRA_APP_ID, app.getAppId());
intent.putExtra(Game.EXTRA_APP_HDR, app.isHdrSupported());
intent.putExtra(Game.EXTRA_UNIQUEID, managerBinder.getUniqueId());
intent.putExtra(Game.EXTRA_PC_UUID, computer.uuid.toString());
intent.putExtra(Game.EXTRA_PC_UUID, computer.uuid);
intent.putExtra(Game.EXTRA_PC_NAME, computer.name);
try {
if (computer.serverCert != null) {
intent.putExtra(Game.EXTRA_SERVER_CERT, computer.serverCert.getEncoded());
}
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
return intent;
}
@@ -45,7 +56,7 @@ public class ServerHelper {
}
public static void doQuit(final Activity parent,
final String address,
final ComputerDetails computer,
final NvApp app,
final ComputerManagerService.ComputerManagerBinder managerBinder,
final Runnable onComplete) {
@@ -56,8 +67,8 @@ public class ServerHelper {
NvHTTP httpConn;
String message;
try {
httpConn = new NvHTTP(address,
managerBinder.getUniqueId(), null, PlatformBinding.getCryptoProvider(parent));
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
managerBinder.getUniqueId(), computer.serverCert, PlatformBinding.getCryptoProvider(parent));
if (httpConn.quitApp()) {
message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName();
} else {
@@ -76,8 +87,9 @@ public class ServerHelper {
message = parent.getResources().getString(R.string.error_unknown_host);
} catch (FileNotFoundException e) {
message = parent.getResources().getString(R.string.error_404);
} catch (Exception e) {
} catch (IOException | XmlPullParserException e) {
message = e.getMessage();
e.printStackTrace();
} finally {
if (onComplete != null) {
onComplete.run();
@@ -125,7 +125,7 @@ public class ShortcutHelper {
}
public void createAppViewShortcut(String id, ComputerDetails details, boolean forceAdd) {
createAppViewShortcut(id, details.name, details.uuid.toString(), forceAdd);
createAppViewShortcut(id, details.name, details.uuid, forceAdd);
}
@TargetApi(Build.VERSION_CODES.O)
@@ -158,7 +158,7 @@ public class ShortcutHelper {
}
public boolean createPinnedGameShortcut(String id, Bitmap iconBits, ComputerDetails cDetails, NvApp app) {
return createPinnedGameShortcut(id, iconBits, cDetails.name, cDetails.uuid.toString(), app.getAppName(), Integer.valueOf(app.getAppId()).toString());
return createPinnedGameShortcut(id, iconBits, cDetails.name, cDetails.uuid, app.getAppName(), Integer.valueOf(app.getAppId()).toString());
}
public void disableShortcut(String id, CharSequence reason) {
+8 -1
View File
@@ -111,11 +111,15 @@
<!-- Preferences -->
<string name="category_basic_settings">Paramètres de base</string>
<string name="title_resolution_list">Sélectionner la résolution et les FPS à atteindre</string>
<string name="title_resolution_list">Résolution vidéo</string>
<string name="summary_resolution_list">Le réglage de valeurs trop élevées pour votre appareil peut provoquer un retard ou un plantage</string>
<string name="title_fps_list">Fréquence d\'images vidéo</string>
<string name="summary_fps_list">Augmenter pour un flux vidéo plus lisse. Diminution pour de meilleures performances sur les périphériques bas de gamme.</string>
<string name="title_seekbar_bitrate">Sélectionnez le bitrate vidéo à obtenir</string>
<string name="summary_seekbar_bitrate">Bitrate inférieur pour réduire la saccade. Augmentez le bitrate pour augmenter la qualité de l\'image.</string>
<string name="suffix_seekbar_bitrate">Kbps</string>
<string name="title_unlock_fps">Débloquer tous les taux d\'images possibles</string>
<string name="summary_unlock_fps">La diffusion en continu à 90 ou 120 FPS peut réduire la latence sur les périphériques haut de gamme, mais peut provoquer des retards ou des blocages sur les périphériques qui ne peuvent \pas le prendre en charge</string>
<string name="title_checkbox_stretch_video">Étirez la vidéo en plein écran</string>
<string name="title_checkbox_disable_warnings">Désactiver les messages d\'avertissement</string>
<string name="summary_checkbox_disable_warnings">Désactiver les messages d\'avertissement de connexion à l\'écran pendant le streaming</string>
@@ -126,6 +130,7 @@
<string name="title_checkbox_51_surround">Activer son surround 5.1</string>
<string name="summary_checkbox_51_surround">Décochez si vous rencontrez des problèmes audio. Nécessite GFE 2.7 ou supérieur.</string>
<string name="category_input_settings">Paramètres d\'entrée</string>
<string name="title_checkbox_multi_controller">Prise en charge de plusieurs contrôleurs</string>
<string name="summary_checkbox_multi_controller">Lorsqu\'elle n\'est pas cochée, tous les contrôleurs sont regroupés</string>
<string name="title_seekbar_deadzone">Régler la zone morte du stick analogique</string>
@@ -136,6 +141,8 @@
<string name="summary_checkbox_usb_bind_all">Force le pilote USB de Moonlight à prendre en charge tous les gamepads Xbox pris en charge</string>
<string name="title_checkbox_mouse_emulation">Emulation de la souris via le gamepad</string>
<string name="summary_checkbox_mouse_emulation">Appuyez longuement sur le bouton Start pour faire basculer la manette de jeu en mode souris.</string>
<string name="title_checkbox_mouse_nav_buttons">Activer les boutons de souris arrière et avant</string>
<string name="summary_checkbox_mouse_nav_buttons">L\'activation de cette option peut entraîner un clic droit sur certains périphériques.</string>
<string name="category_on_screen_controls_settings">Paramètres des contrôles à l\'écran</string>
<string name="title_checkbox_show_onscreen_controls">Afficher les commandes à l\'écran</string>