From 4a706aad036a3e7ba0c72881f43b5af4a088da2f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 15 Apr 2018 18:31:58 +0200 Subject: [PATCH] catch dead object exceptions when acquiring wake locks --- .../http/HttpDownloadConnection.java | 3 +- .../http/HttpUploadConnection.java | 5 +- .../services/XmppConnectionService.java | 10 +- .../conversations/utils/WakeLockHelper.java | 59 ++++++ .../xmpp/jingle/JingleSocks5Transport.java | 198 ++++++++---------- 5 files changed, 159 insertions(+), 116 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java diff --git a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java index 9f2bd327a..b8862244e 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpDownloadConnection.java +++ b/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); } } diff --git a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java b/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java index 4f72baf03..c7625e175 100644 --- a/src/main/java/eu/siacs/conversations/http/HttpUploadConnection.java +++ b/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); } } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index e73cfeba4..c7247b0c4 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/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 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(); diff --git a/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java b/src/main/java/eu/siacs/conversations/utils/WakeLockHelper.java new file mode 100644 index 000000000..ce1631b03 --- /dev/null +++ b/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); + } + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index 528751553..15aebc724 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/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) { - 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() + ": 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()); + 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(); - } finally { - wakeLock.release(); - FileBackend.close(fileOutputStream); - FileBackend.close(inputStream); + 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() + ": 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 { + WakeLockHelper.release(wakeLock); + FileBackend.close(fileOutputStream); + FileBackend.close(inputStream); } }).start(); }