catch dead object exceptions when acquiring wake locks

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java       |   3 
src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java         |   5 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java    |  10 
src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java              |  59 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java | 194 
5 files changed, 157 insertions(+), 114 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java 🔗

@@ -26,6 +26,7 @@ import eu.siacs.conversations.services.AbstractConnectionManager;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.FileWriterException;
+import eu.siacs.conversations.utils.WakeLockHelper;
 
 public class HttpDownloadConnection implements Transferable {
 
@@ -365,7 +366,7 @@ public class HttpDownloadConnection implements Transferable {
 				if (connection != null) {
 					connection.disconnect();
 				}
-				wakeLock.release();
+				WakeLockHelper.release(wakeLock);
 			}
 		}
 

src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java 🔗

@@ -27,6 +27,7 @@ import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.services.AbstractConnectionManager;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.WakeLockHelper;
 import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@@ -237,9 +238,7 @@ public class HttpUploadConnection implements Transferable {
 			if (connection != null) {
 				connection.disconnect();
 			}
-			if (wakeLock.isHeld()) {
-				wakeLock.release();
-			}
+			WakeLockHelper.release(wakeLock);
 		}
 	}
 }

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -110,6 +110,7 @@ import eu.siacs.conversations.utils.ReplacingSerialSingleThreadExecutor;
 import eu.siacs.conversations.utils.ReplacingTaskManager;
 import eu.siacs.conversations.utils.Resolver;
 import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
+import eu.siacs.conversations.utils.WakeLockHelper;
 import eu.siacs.conversations.xml.Namespace;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xml.Element;
@@ -659,7 +660,7 @@ public class XmppConnectionService extends Service {
 			}
 		}
 		synchronized (this) {
-			this.wakeLock.acquire();
+			WakeLockHelper.acquire(wakeLock);
 			boolean pingNow = ConnectivityManager.CONNECTIVITY_ACTION.equals(action);
 			HashSet<Account> pingCandidates = new HashSet<>();
 			for (Account account : accounts) {
@@ -677,12 +678,7 @@ public class XmppConnectionService extends Service {
 					scheduleWakeUpCall(lowTimeout ? Config.LOW_PING_TIMEOUT : Config.PING_TIMEOUT, account.getUuid().hashCode());
 				}
 			}
-			if (wakeLock.isHeld()) {
-				try {
-					wakeLock.release();
-				} catch (final RuntimeException ignored) {
-				}
-			}
+			WakeLockHelper.release(wakeLock);
 		}
 		if (SystemClock.elapsedRealtime() - mLastExpiryRun.get() >= Config.EXPIRY_INTERVAL) {
 			expireOldMessages();

src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java 🔗

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018, Daniel Gultsch All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package eu.siacs.conversations.utils;
+
+import android.os.PowerManager;
+import android.util.Log;
+
+import eu.siacs.conversations.Config;
+
+public class WakeLockHelper {
+
+	public static void acquire(PowerManager.WakeLock wakeLock) {
+		try {
+			wakeLock.acquire(2000);
+		} catch (RuntimeException e) {
+			Log.d(Config.LOGTAG, "unable to acquire wake lock", e);
+		}
+	}
+
+	public static void release(PowerManager.WakeLock wakeLock) {
+		if (wakeLock == null) {
+			return;
+		}
+		try {
+			if (wakeLock.isHeld()) {
+				wakeLock.release();
+			}
+		} catch (RuntimeException e) {
+			Log.d(Config.LOGTAG, "unable to release wake lock", e);
+		}
+	}
+}

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java 🔗

@@ -17,6 +17,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
 import eu.siacs.conversations.persistance.FileBackend;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.SocksSocketFactory;
+import eu.siacs.conversations.utils.WakeLockHelper;
 import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
 
 public class JingleSocks5Transport extends JingleTransport {
@@ -27,17 +28,16 @@ public class JingleSocks5Transport extends JingleTransport {
 	private InputStream inputStream;
 	private boolean isEstablished = false;
 	private boolean activated = false;
-	protected Socket socket;
+	private Socket socket;
 
-	public JingleSocks5Transport(JingleConnection jingleConnection,
-			JingleCandidate candidate) {
+	JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) {
 		this.candidate = candidate;
 		this.connection = jingleConnection;
 		try {
 			MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
 			StringBuilder destBuilder = new StringBuilder();
 			if (jingleConnection.getFtVersion() == Content.Version.FT_3) {
-				Log.d(Config.LOGTAG,this.connection.getAccount().getJid().asBareJid()+": using session Id instead of transport Id for proxy destination");
+				Log.d(Config.LOGTAG, this.connection.getAccount().getJid().asBareJid() + ": using session Id instead of transport Id for proxy destination");
 				destBuilder.append(jingleConnection.getSessionId());
 			} else {
 				destBuilder.append(jingleConnection.getTransportId());
@@ -58,124 +58,112 @@ public class JingleSocks5Transport extends JingleTransport {
 	}
 
 	public void connect(final OnTransportConnected callback) {
-		new Thread(new Runnable() {
-
-			@Override
-			public void run() {
-				try {
-					final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
-					if (useTor) {
-						socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(),candidate.getPort());
-					} else {
-						socket = new Socket();
-						SocketAddress address = new InetSocketAddress(candidate.getHost(),candidate.getPort());
-						socket.connect(address,Config.SOCKET_TIMEOUT * 1000);
-					}
-					inputStream = socket.getInputStream();
-					outputStream = socket.getOutputStream();
-					SocksSocketFactory.createSocksConnection(socket,destination,0);
-					isEstablished = true;
-					callback.established();
-				} catch (IOException e) {
-					callback.failed();
+		new Thread(() -> {
+			try {
+				final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
+				if (useTor) {
+					socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort());
+				} else {
+					socket = new Socket();
+					SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort());
+					socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
 				}
+				inputStream = socket.getInputStream();
+				outputStream = socket.getOutputStream();
+				SocksSocketFactory.createSocksConnection(socket, destination, 0);
+				isEstablished = true;
+				callback.established();
+			} catch (IOException e) {
+				callback.failed();
 			}
 		}).start();
 
 	}
 
 	public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
-		new Thread(new Runnable() {
-
-			@Override
-			public void run() {
-				InputStream fileInputStream = null;
-				final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId());
-				try {
-					wakeLock.acquire();
-					MessageDigest digest = MessageDigest.getInstance("SHA-1");
-					digest.reset();
-					fileInputStream = connection.getFileInputStream();
-					if (fileInputStream == null) {
-						Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream");
-						callback.onFileTransferAborted();
-						return;
-					}
-					long size = file.getExpectedSize();
-					long transmitted = 0;
-					int count;
-					byte[] buffer = new byte[8192];
-					while ((count = fileInputStream.read(buffer)) > 0) {
-						outputStream.write(buffer, 0, count);
-						digest.update(buffer, 0, count);
-						transmitted += count;
-						connection.updateProgress((int) ((((double) transmitted) / size) * 100));
-					}
-					outputStream.flush();
-					file.setSha1Sum(digest.digest());
-					if (callback != null) {
-						callback.onFileTransmitted(file);
-					}
-				} catch (Exception e) {
-					Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": "+e.getMessage());
+		new Thread(() -> {
+			InputStream fileInputStream = null;
+			final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId());
+			try {
+				wakeLock.acquire();
+				MessageDigest digest = MessageDigest.getInstance("SHA-1");
+				digest.reset();
+				fileInputStream = connection.getFileInputStream();
+				if (fileInputStream == null) {
+					Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream");
 					callback.onFileTransferAborted();
-				} finally {
-					FileBackend.close(fileInputStream);
-					wakeLock.release();
+					return;
+				}
+				long size = file.getExpectedSize();
+				long transmitted = 0;
+				int count;
+				byte[] buffer = new byte[8192];
+				while ((count = fileInputStream.read(buffer)) > 0) {
+					outputStream.write(buffer, 0, count);
+					digest.update(buffer, 0, count);
+					transmitted += count;
+					connection.updateProgress((int) ((((double) transmitted) / size) * 100));
 				}
+				outputStream.flush();
+				file.setSha1Sum(digest.digest());
+				if (callback != null) {
+					callback.onFileTransmitted(file);
+				}
+			} catch (Exception e) {
+				Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage());
+				callback.onFileTransferAborted();
+			} finally {
+				FileBackend.close(fileInputStream);
+				WakeLockHelper.release(wakeLock);
 			}
 		}).start();
 
 	}
 
 	public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
-		new Thread(new Runnable() {
-
-			@Override
-			public void run() {
-				OutputStream fileOutputStream = null;
-				final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId());
-				try {
-					wakeLock.acquire();
-					MessageDigest digest = MessageDigest.getInstance("SHA-1");
-					digest.reset();
-					//inputStream.skip(45);
-					socket.setSoTimeout(30000);
-					fileOutputStream = connection.getFileOutputStream();
-					if (fileOutputStream == null) {
+		new Thread(() -> {
+			OutputStream fileOutputStream = null;
+			final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId());
+			try {
+				wakeLock.acquire();
+				MessageDigest digest = MessageDigest.getInstance("SHA-1");
+				digest.reset();
+				//inputStream.skip(45);
+				socket.setSoTimeout(30000);
+				fileOutputStream = connection.getFileOutputStream();
+				if (fileOutputStream == null) {
+					callback.onFileTransferAborted();
+					Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream");
+					return;
+				}
+				double size = file.getExpectedSize();
+				long remainingSize = file.getExpectedSize();
+				byte[] buffer = new byte[8192];
+				int count;
+				while (remainingSize > 0) {
+					count = inputStream.read(buffer);
+					if (count == -1) {
 						callback.onFileTransferAborted();
-						Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream");
+						Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": file ended prematurely with " + remainingSize + " bytes remaining");
 						return;
+					} else {
+						fileOutputStream.write(buffer, 0, count);
+						digest.update(buffer, 0, count);
+						remainingSize -= count;
 					}
-					double size = file.getExpectedSize();
-					long remainingSize = file.getExpectedSize();
-					byte[] buffer = new byte[8192];
-					int count;
-					while (remainingSize > 0) {
-						count = inputStream.read(buffer);
-						if (count == -1) {
-							callback.onFileTransferAborted();
-							Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": file ended prematurely with "+remainingSize+" bytes remaining");
-							return;
-						} else {
-							fileOutputStream.write(buffer, 0, count);
-							digest.update(buffer, 0, count);
-							remainingSize -= count;
-						}
-						connection.updateProgress((int) (((size - remainingSize) / size) * 100));
-					}
-					fileOutputStream.flush();
-					fileOutputStream.close();
-					file.setSha1Sum(digest.digest());
-					callback.onFileTransmitted(file);
-				} catch (Exception e) {
-					Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": "+e.getMessage());
-					callback.onFileTransferAborted();
-				} finally {
-					wakeLock.release();
-					FileBackend.close(fileOutputStream);
-					FileBackend.close(inputStream);
+					connection.updateProgress((int) (((size - remainingSize) / size) * 100));
 				}
+				fileOutputStream.flush();
+				fileOutputStream.close();
+				file.setSha1Sum(digest.digest());
+				callback.onFileTransmitted(file);
+			} catch (Exception e) {
+				Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": " + e.getMessage());
+				callback.onFileTransferAborted();
+			} finally {
+				WakeLockHelper.release(wakeLock);
+				FileBackend.close(fileOutputStream);
+				FileBackend.close(inputStream);
 			}
 		}).start();
 	}