mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-31 22:25:08 +00:00
Re-use existing link instead of creating a new one
This commit is contained in:
@@ -34,10 +34,6 @@ public abstract class BaseLink {
|
||||
|
||||
protected final Context context;
|
||||
|
||||
public enum ConnectionStarted {
|
||||
Locally, Remotely;
|
||||
};
|
||||
|
||||
public interface PackageReceiver {
|
||||
void onPackageReceived(NetworkPackage np);
|
||||
}
|
||||
@@ -47,16 +43,10 @@ public abstract class BaseLink {
|
||||
private final ArrayList<PackageReceiver> receivers = new ArrayList<>();
|
||||
protected PrivateKey privateKey;
|
||||
|
||||
protected ConnectionStarted connectionSource; // If the other device sent me a broadcast,
|
||||
// I should not close the connection with it
|
||||
// because it's probably trying to find me and
|
||||
// potentially ask for pairing.
|
||||
|
||||
protected BaseLink(Context context, String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
|
||||
protected BaseLink(Context context, String deviceId, BaseLinkProvider linkProvider) {
|
||||
this.context = context;
|
||||
this.linkProvider = linkProvider;
|
||||
this.deviceId = deviceId;
|
||||
this.connectionSource = connectionSource;
|
||||
}
|
||||
|
||||
/* To be implemented by each link for pairing handlers */
|
||||
@@ -75,8 +65,9 @@ public abstract class BaseLink {
|
||||
return linkProvider;
|
||||
}
|
||||
|
||||
public ConnectionStarted getConnectionSource() {
|
||||
return connectionSource;
|
||||
//The daemon will periodically destroy unpaired links if this returns false
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPackageReceiver(PackageReceiver pr) {
|
||||
|
@@ -21,6 +21,7 @@
|
||||
package org.kde.kdeconnect.Backends.LanBackend;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@@ -50,17 +51,32 @@ import io.netty.channel.ChannelFuture;
|
||||
|
||||
public class LanLink extends BaseLink {
|
||||
|
||||
public enum ConnectionStarted {
|
||||
Locally, Remotely;
|
||||
};
|
||||
|
||||
protected ConnectionStarted connectionSource; // If the other device sent me a broadcast,
|
||||
// I should not close the connection with it
|
||||
// because it's probably trying to find me and
|
||||
// potentially ask for pairing.
|
||||
|
||||
private Channel channel = null;
|
||||
private boolean onSsl = false;
|
||||
|
||||
// Using time stamp, because if both devices emit their identity package at the same time, both the links tends to cancel each other
|
||||
private long startTime = 0;
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//Returns the old channel
|
||||
public Channel reset(Channel channel, ConnectionStarted connectionSource, boolean onSsl) {
|
||||
Channel oldChannel = this.channel;
|
||||
this.channel = channel;
|
||||
this.connectionSource = connectionSource;
|
||||
return oldChannel;
|
||||
}
|
||||
|
||||
public void closeSocket() {
|
||||
if (channel == null) {
|
||||
Log.e("KDE/LanLink", "Not yet connected");
|
||||
@@ -69,19 +85,11 @@ public class LanLink extends BaseLink {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
public void setOnSsl(boolean value) {
|
||||
this.onSsl = value;
|
||||
public LanLink(Context context, String deviceId, BaseLinkProvider linkProvider, Channel channel, ConnectionStarted connectionSource, boolean onSsl) {
|
||||
super(context, deviceId, linkProvider);
|
||||
reset(channel, connectionSource, onSsl);
|
||||
}
|
||||
|
||||
public LanLink(Context context,Channel channel, String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
|
||||
super(context, deviceId, linkProvider, connectionSource);
|
||||
this.channel = channel;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -306,4 +314,22 @@ public class LanLink extends BaseLink {
|
||||
return candidateServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
|
||||
//We keep the remotely initiated connections, since the remotes require them if they want to request
|
||||
//pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
|
||||
|
||||
if (connectionSource == ConnectionStarted.Remotely) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
if (preferences.contains(getDeviceId())) {
|
||||
return true; //Already paired
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -40,6 +40,7 @@ import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.cert.Certificate;
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Timer;
|
||||
@@ -167,9 +168,8 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
|
||||
Log.i("KDE/LanLinkProvider", "Identity package received from " + np.getString("deviceName"));
|
||||
|
||||
final LanLink link = new LanLink(context, ctx.channel(), np.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Locally);
|
||||
nioLinks.put(ctx.channel().hashCode(), link);
|
||||
//Log.i("KDE/LanLinkProvider","nioLinks.size(): " + nioLinks.size());
|
||||
final Channel channel = ctx.channel();
|
||||
final LanLink.ConnectionStarted connectionStarted = LanLink.ConnectionStarted.Locally;
|
||||
|
||||
// Add ssl handler if device uses new protocol
|
||||
try {
|
||||
@@ -185,8 +185,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
Log.i("KDE/LanLinkProvider","Handshake successful with " + np.getString("deviceName") + " secured with " + sslEngine.getSession().getCipherSuite());
|
||||
Certificate certificate = sslEngine.getSession().getPeerCertificates()[0];
|
||||
np.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
|
||||
link.setOnSsl(true);
|
||||
addLink(np, link);
|
||||
addLink(np, channel, connectionStarted, true);
|
||||
} else {
|
||||
// Unpair if handshake failed
|
||||
Log.e("KDE/LanLinkProvider", "Handshake failed with " + np.getString("deviceName"));
|
||||
@@ -206,7 +205,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addLink(np, link);
|
||||
addLink(np, channel, connectionStarted, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -286,61 +285,52 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
channel.pipeline().addFirst(sslHandler);
|
||||
}
|
||||
|
||||
final LanLink link = new LanLink(context, channel, identityPackage.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Remotely);
|
||||
final LanLink.ConnectionStarted connectionStarted = LanLink.ConnectionStarted.Remotely;
|
||||
|
||||
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
||||
link.sendPackage(np2,new Device.SendPackageStatusCallback() {
|
||||
@Override
|
||||
protected void onSuccess() {
|
||||
nioLinks.put(channel.hashCode(), link);
|
||||
Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size());
|
||||
|
||||
// If ssl handler is in channel, add link after handshake is completed
|
||||
final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
|
||||
if (sslHandler != null) {
|
||||
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<? super Channel> future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
try {
|
||||
Log.i("KDE/LanLinkProvider", "Handshake successfully completed with " + identityPackage.getString("deviceName") + ", session secured with " + sslHandler.engine().getSession().getCipherSuite());
|
||||
Certificate certificate = sslHandler.engine().getSession().getPeerCertificates()[0];
|
||||
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
|
||||
link.setOnSsl(true);
|
||||
addLink(identityPackage, link);
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// Unpair if handshake failed
|
||||
// Any exception or handshake exception ?
|
||||
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPackage.getString("deviceName"));
|
||||
future.cause().printStackTrace();
|
||||
if (future.cause() instanceof SSLHandshakeException) {
|
||||
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(identityPackage.getString("deviceId"));
|
||||
if (device == null) return;
|
||||
device.unpair();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
ChannelFuture future2 = channel.writeAndFlush(np2.serialize()).sync();
|
||||
if (!future2.isSuccess()) {
|
||||
Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back");
|
||||
return;
|
||||
}
|
||||
|
||||
// If ssl handler is in channel, add link after handshake is completed
|
||||
final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
|
||||
if (sslHandler != null) {
|
||||
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
|
||||
@Override
|
||||
public void operationComplete(Future<? super Channel> future) throws Exception {
|
||||
if (future.isSuccess()) {
|
||||
try {
|
||||
Log.i("KDE/LanLinkProvider", "Handshake successfully completed with " + identityPackage.getString("deviceName") + ", session secured with " + sslHandler.engine().getSession().getCipherSuite());
|
||||
Certificate certificate = sslHandler.engine().getSession().getPeerCertificates()[0];
|
||||
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
|
||||
addLink(identityPackage, channel, connectionStarted, true);
|
||||
} catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addLink(identityPackage, link);
|
||||
} else {
|
||||
// Unpair if handshake failed
|
||||
// Any exception or handshake exception ?
|
||||
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPackage.getString("deviceName"));
|
||||
future.cause().printStackTrace();
|
||||
if (future.cause() instanceof SSLHandshakeException) {
|
||||
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(identityPackage.getString("deviceId"));
|
||||
if (device == null) return;
|
||||
device.unpair();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFailure(Throwable e) {
|
||||
Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back");
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
} else {
|
||||
addLink(identityPackage, channel, connectionStarted, false);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -369,28 +359,26 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void addLink(NetworkPackage identityPackage, LanLink link) {
|
||||
private void addLink(NetworkPackage identityPackage, Channel channel, LanLink.ConnectionStarted connectionOrigin, boolean useSsl) {
|
||||
String deviceId = identityPackage.getString("deviceId");
|
||||
Log.i("KDE/LanLinkProvider","addLink to "+deviceId);
|
||||
LanLink oldLink = visibleComputers.get(deviceId);
|
||||
if (oldLink == link) {
|
||||
Log.e("KDE/LanLinkProvider", "oldLink == link. This should not happen!");
|
||||
LanLink currentLink = visibleComputers.get(deviceId);
|
||||
if (currentLink != null) {
|
||||
Log.e("KDE/LanLinkProvider", "Reusing same link for device " + deviceId);
|
||||
Channel oldChannel = currentLink.reset(channel, connectionOrigin, useSsl);
|
||||
nioLinks.remove(oldChannel.hashCode());
|
||||
nioLinks.put(channel.hashCode(), currentLink);
|
||||
return;
|
||||
}
|
||||
|
||||
//Let's create the link
|
||||
|
||||
LanLink link = new LanLink(context, deviceId, this, channel, connectionOrigin, useSsl);
|
||||
|
||||
nioLinks.put(channel.hashCode(), link);
|
||||
visibleComputers.put(deviceId, link);
|
||||
|
||||
connectionAccepted(identityPackage, link);
|
||||
if (oldLink != null) {
|
||||
if (link.getStartTime() < oldLink.getStartTime()) {
|
||||
// New link is not so new, it just took more time to be establish successfully and get here
|
||||
link.closeSocket();
|
||||
connectionLost(link);
|
||||
} else {
|
||||
Log.i("KDE/LanLinkProvider", "Removing old connection to same device");
|
||||
oldLink.closeSocket();
|
||||
connectionLost(oldLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LanLinkProvider(Context context) {
|
||||
|
@@ -35,7 +35,7 @@ import java.util.ArrayList;
|
||||
public class LoopbackLink extends BaseLink {
|
||||
|
||||
public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
|
||||
super(context, "loopback", linkProvider, ConnectionStarted.Remotely);
|
||||
super(context, "loopback", linkProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -163,7 +163,7 @@ public class BackgroundService extends Service {
|
||||
|
||||
private void cleanDevices() {
|
||||
for(Device d : devices.values()) {
|
||||
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && d.getConnectionSource() == BaseLink.ConnectionStarted.Remotely) {
|
||||
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
|
||||
d.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ public class BackgroundService extends Service {
|
||||
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
|
||||
device = new Device(BackgroundService.this, identityPackage, link);
|
||||
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|
||||
|| link.getConnectionSource() == BaseLink.ConnectionStarted.Locally
|
||||
|| link.linkShouldBeKeptAlive()
|
||||
||!discoveryModeAcquisitions.isEmpty() )
|
||||
{
|
||||
devices.put(deviceId, device);
|
||||
|
@@ -874,13 +874,13 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public BaseLink.ConnectionStarted getConnectionSource() {
|
||||
public boolean deviceShouldBeKeptAlive() {
|
||||
for(BaseLink l : links) {
|
||||
if (l.getConnectionSource() == BaseLink.ConnectionStarted.Locally) {
|
||||
return BaseLink.ConnectionStarted.Locally;
|
||||
if (l.linkShouldBeKeptAlive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return BaseLink.ConnectionStarted.Remotely;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user