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()) { | ||||||
| @ -387,6 +414,8 @@ 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,6 +439,7 @@ 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 { | ||||||
|  | 			if (!Thread.currentThread().isInterrupted()) { | ||||||
| 				forceCloseSocket(); | 				forceCloseSocket(); | ||||||
| 				if (wakeLock.isHeld()) { | 				if (wakeLock.isHeld()) { | ||||||
| 					try { | 					try { | ||||||
| @ -417,33 +447,27 @@ public class XmppConnection implements Runnable { | |||||||
| 					} catch (final RuntimeException ignored) { | 					} catch (final RuntimeException ignored) { | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 			} else { | ||||||
|  | 				Log.d(Config.LOGTAG,account.getJid().toBareJid()+": not force closing socket and releasing wake lock because thread was interrupted"); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * 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 New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch