check if thread was interrupted before doing operations on socket
This commit is contained in:
parent
1820b163a1
commit
3bf2876e09
|
@ -2897,8 +2897,6 @@ public class XmppConnectionService extends Service {
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
connection = createConnection(account);
|
connection = createConnection(account);
|
||||||
account.setXmppConnection(connection);
|
account.setXmppConnection(connection);
|
||||||
} else {
|
|
||||||
connection.interrupt();
|
|
||||||
}
|
}
|
||||||
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
if (!account.isOptionSet(Account.OPTION_DISABLED)) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
|
@ -2907,6 +2905,7 @@ public class XmppConnectionService extends Service {
|
||||||
Thread thread = new Thread(connection);
|
Thread thread = new Thread(connection);
|
||||||
connection.setInteractive(interactive);
|
connection.setInteractive(interactive);
|
||||||
connection.prepareNewConnection();
|
connection.prepareNewConnection();
|
||||||
|
connection.interrupt();
|
||||||
thread.start();
|
thread.start();
|
||||||
scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
|
scheduleWakeUpCall(Config.CONNECT_DISCO_TIMEOUT, account.getUuid().hashCode());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -218,7 +218,10 @@ public class XmppConnection implements Runnable {
|
||||||
mXmppConnectionService = service;
|
mXmppConnectionService = service;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void changeStatus(final Account.State nextStatus) {
|
protected synchronized void changeStatus(final Account.State nextStatus) {
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": not changing status to "+nextStatus+" because thread was interrupted");
|
||||||
|
}
|
||||||
if (account.getStatus() != nextStatus) {
|
if (account.getStatus() != nextStatus) {
|
||||||
if ((nextStatus == Account.State.OFFLINE)
|
if ((nextStatus == Account.State.OFFLINE)
|
||||||
&& (account.getStatus() != Account.State.CONNECTING)
|
&& (account.getStatus() != Account.State.CONNECTING)
|
||||||
|
@ -262,6 +265,7 @@ public class XmppConnection implements Runnable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
Socket localSocket;
|
||||||
shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
|
shouldAuthenticate = needsBinding = !account.isOptionSet(Account.OPTION_REGISTER);
|
||||||
tagReader = new XmlReader(wakeLock);
|
tagReader = new XmlReader(wakeLock);
|
||||||
tagWriter = new TagWriter();
|
tagWriter = new TagWriter();
|
||||||
|
@ -276,8 +280,15 @@ public class XmppConnection implements Runnable {
|
||||||
destination = account.getHostname();
|
destination = account.getHostname();
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + destination + " via Tor");
|
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": connect to " + destination + " via Tor");
|
||||||
socket = SocksSocketFactory.createSocketOverTor(destination, account.getPort());
|
localSocket = SocksSocketFactory.createSocketOverTor(destination, account.getPort());
|
||||||
startXmpp();
|
try {
|
||||||
|
startXmpp(localSocket);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
} else if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) {
|
} else if (extended && account.getHostname() != null && !account.getHostname().isEmpty()) {
|
||||||
|
|
||||||
InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
|
InetSocketAddress address = new InetSocketAddress(account.getHostname(), account.getPort());
|
||||||
|
@ -288,33 +299,47 @@ public class XmppConnection implements Runnable {
|
||||||
if (features.encryptionEnabled) {
|
if (features.encryptionEnabled) {
|
||||||
try {
|
try {
|
||||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||||
socket = tlsFactoryVerifier.factory.createSocket();
|
localSocket = tlsFactoryVerifier.factory.createSocket();
|
||||||
socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
|
localSocket.connect(address, Config.SOCKET_TIMEOUT * 1000);
|
||||||
final SSLSession session = ((SSLSocket) socket).getSession();
|
final SSLSession session = ((SSLSocket) localSocket).getSession();
|
||||||
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
|
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), session)) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
|
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
|
||||||
throw new SecurityException();
|
throw new SecurityException();
|
||||||
}
|
}
|
||||||
} catch (KeyManagementException e) {
|
} catch (KeyManagementException e) {
|
||||||
features.encryptionEnabled = false;
|
features.encryptionEnabled = false;
|
||||||
socket = new Socket();
|
localSocket = new Socket();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
socket = new Socket();
|
localSocket = new Socket();
|
||||||
socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
|
localSocket.connect(address, Config.SOCKET_TIMEOUT * 1000);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UnknownHostException();
|
throw new UnknownHostException();
|
||||||
}
|
}
|
||||||
startXmpp();
|
|
||||||
} else if (DNSHelper.isIp(account.getServer().toString())) {
|
|
||||||
socket = new Socket();
|
|
||||||
try {
|
try {
|
||||||
socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
|
startXmpp(localSocket);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
|
} else if (DNSHelper.isIp(account.getServer().toString())) {
|
||||||
|
localSocket = new Socket();
|
||||||
|
try {
|
||||||
|
localSocket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new UnknownHostException();
|
throw new UnknownHostException();
|
||||||
}
|
}
|
||||||
startXmpp();
|
try {
|
||||||
|
startXmpp(localSocket);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IOException(e.getMessage());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
|
final Bundle result = DNSHelper.getSRVRecord(account.getServer(), mXmppConnectionService);
|
||||||
final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
|
final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
|
||||||
|
@ -350,32 +375,34 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!features.encryptionEnabled) {
|
if (!features.encryptionEnabled) {
|
||||||
socket = new Socket();
|
localSocket = new Socket();
|
||||||
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||||
} else {
|
} else {
|
||||||
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
final TlsFactoryVerifier tlsFactoryVerifier = getTlsFactoryVerifier();
|
||||||
socket = tlsFactoryVerifier.factory.createSocket();
|
localSocket = tlsFactoryVerifier.factory.createSocket();
|
||||||
|
|
||||||
if (socket == null) {
|
if (localSocket == null) {
|
||||||
throw new IOException("could not initialize ssl socket");
|
throw new IOException("could not initialize ssl socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
SSLSocketHelper.setSecurity((SSLSocket) socket);
|
SSLSocketHelper.setSecurity((SSLSocket) localSocket);
|
||||||
SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) socket, account.getServer().getDomainpart());
|
SSLSocketHelper.setSNIHost(tlsFactoryVerifier.factory, (SSLSocket) localSocket, account.getServer().getDomainpart());
|
||||||
SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) socket, "xmpp-client");
|
SSLSocketHelper.setAlpnProtocol(tlsFactoryVerifier.factory, (SSLSocket) localSocket, "xmpp-client");
|
||||||
|
|
||||||
socket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
localSocket.connect(addr, Config.SOCKET_TIMEOUT * 1000);
|
||||||
|
|
||||||
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) socket).getSession())) {
|
if (!tlsFactoryVerifier.verifier.verify(account.getServer().getDomainpart(), ((SSLSocket) localSocket).getSession())) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
|
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": TLS certificate verification failed");
|
||||||
throw new SecurityException();
|
throw new SecurityException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (startXmpp(localSocket))
|
||||||
if (startXmpp())
|
|
||||||
break; // successfully connected to server that speaks xmpp
|
break; // successfully connected to server that speaks xmpp
|
||||||
} catch (final SecurityException e) {
|
} catch (final SecurityException e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": thread was interrupted before beginning stream");
|
||||||
|
return;
|
||||||
} catch (final Throwable e) {
|
} catch (final Throwable e) {
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")");
|
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage() + "(" + e.getClass().getName() + ")");
|
||||||
if (!iterator.hasNext()) {
|
if (!iterator.hasNext()) {
|
||||||
|
@ -385,8 +412,10 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processStream();
|
processStream();
|
||||||
} catch (final java.lang.SecurityException e) {
|
} catch (final java.lang.SecurityException e) {
|
||||||
this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
|
this.changeStatus(Account.State.MISSING_INTERNET_PERMISSION);
|
||||||
|
} catch (final RegistrationNotSupportedException e) {
|
||||||
|
this.changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
|
||||||
} catch (final IncompatibleServerException e) {
|
} catch (final IncompatibleServerException e) {
|
||||||
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
|
this.changeStatus(Account.State.INCOMPATIBLE_SERVER);
|
||||||
} catch (final SecurityException e) {
|
} catch (final SecurityException e) {
|
||||||
|
@ -410,12 +439,16 @@ public class XmppConnection implements Runnable {
|
||||||
this.changeStatus(Account.State.OFFLINE);
|
this.changeStatus(Account.State.OFFLINE);
|
||||||
this.attempt = Math.max(0, this.attempt - 1);
|
this.attempt = Math.max(0, this.attempt - 1);
|
||||||
} finally {
|
} finally {
|
||||||
forceCloseSocket();
|
if (!Thread.currentThread().isInterrupted()) {
|
||||||
if (wakeLock.isHeld()) {
|
forceCloseSocket();
|
||||||
try {
|
if (wakeLock.isHeld()) {
|
||||||
wakeLock.release();
|
try {
|
||||||
} catch (final RuntimeException ignored) {
|
wakeLock.release();
|
||||||
|
} catch (final RuntimeException ignored) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": not force closing socket and releasing wake lock because thread was interrupted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,27 +456,18 @@ public class XmppConnection implements Runnable {
|
||||||
/**
|
/**
|
||||||
* Starts xmpp protocol, call after connecting to socket
|
* Starts xmpp protocol, call after connecting to socket
|
||||||
* @return true if server returns with valid xmpp, false otherwise
|
* @return true if server returns with valid xmpp, false otherwise
|
||||||
* @throws IOException Unknown tag on connect
|
|
||||||
* @throws XmlPullParserException Bad Xml
|
|
||||||
* @throws NoSuchAlgorithmException Other error
|
|
||||||
*/
|
*/
|
||||||
private boolean startXmpp() throws IOException, XmlPullParserException, NoSuchAlgorithmException {
|
private boolean startXmpp(Socket socket) throws Exception {
|
||||||
|
if (Thread.currentThread().isInterrupted()) {
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
this.socket = socket;
|
||||||
tagWriter.setOutputStream(socket.getOutputStream());
|
tagWriter.setOutputStream(socket.getOutputStream());
|
||||||
tagReader.setInputStream(socket.getInputStream());
|
tagReader.setInputStream(socket.getInputStream());
|
||||||
tagWriter.beginDocument();
|
tagWriter.beginDocument();
|
||||||
sendStartStream();
|
sendStartStream();
|
||||||
Tag nextTag;
|
final Tag tag = tagReader.readTag();
|
||||||
while ((nextTag = tagReader.readTag()) != null) {
|
return tag != null && tag.isStart("stream");
|
||||||
if (nextTag.isStart("stream")) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new IOException("unknown tag on connect");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (socket.isConnected()) {
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TlsFactoryVerifier {
|
private static class TlsFactoryVerifier {
|
||||||
|
@ -812,10 +836,8 @@ public class XmppConnection implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
throw new IncompatibleServerException();
|
throw new IncompatibleServerException();
|
||||||
}
|
}
|
||||||
} else if (!this.streamFeatures.hasChild("register")
|
} else if (!this.streamFeatures.hasChild("register") && account.isOptionSet(Account.OPTION_REGISTER)) {
|
||||||
&& account.isOptionSet(Account.OPTION_REGISTER)) {
|
throw new RegistrationNotSupportedException();
|
||||||
forceCloseSocket();
|
|
||||||
changeStatus(Account.State.REGISTRATION_NOT_SUPPORTED);
|
|
||||||
} else if (this.streamFeatures.hasChild("mechanisms")
|
} else if (this.streamFeatures.hasChild("mechanisms")
|
||||||
&& shouldAuthenticate
|
&& shouldAuthenticate
|
||||||
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
|
&& (features.encryptionEnabled || Config.ALLOW_NON_TLS_CONNECTIONS)) {
|
||||||
|
@ -1359,30 +1381,6 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitForPush() {
|
|
||||||
if (tagWriter.isActive()) {
|
|
||||||
tagWriter.finish();
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
while(!tagWriter.finished()) {
|
|
||||||
Thread.sleep(10);
|
|
||||||
}
|
|
||||||
socket.close();
|
|
||||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
|
|
||||||
changeStatus(Account.State.OFFLINE);
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": error while closing socket for waitForPush()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
} else {
|
|
||||||
forceCloseSocket();
|
|
||||||
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void forceCloseSocket() {
|
private void forceCloseSocket() {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1569,6 +1567,10 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RegistrationNotSupportedException extends IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public enum Identity {
|
public enum Identity {
|
||||||
FACEBOOK,
|
FACEBOOK,
|
||||||
SLACK,
|
SLACK,
|
||||||
|
|
Loading…
Reference in a new issue