catch dead object exceptions when acquiring wake locks

This commit is contained in:
Daniel Gultsch 2018-04-15 18:31:58 +02:00
parent ffc35f5bc5
commit 4a706aad03
5 changed files with 159 additions and 116 deletions

View file

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

View file

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

View file

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

View file

@ -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);
}
}
}

View file

@ -17,6 +17,7 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.SocksSocketFactory; import eu.siacs.conversations.utils.SocksSocketFactory;
import eu.siacs.conversations.utils.WakeLockHelper;
import eu.siacs.conversations.xmpp.jingle.stanzas.Content; import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
public class JingleSocks5Transport extends JingleTransport { public class JingleSocks5Transport extends JingleTransport {
@ -27,17 +28,16 @@ public class JingleSocks5Transport extends JingleTransport {
private InputStream inputStream; private InputStream inputStream;
private boolean isEstablished = false; private boolean isEstablished = false;
private boolean activated = false; private boolean activated = false;
protected Socket socket; private Socket socket;
public JingleSocks5Transport(JingleConnection jingleConnection, JingleSocks5Transport(JingleConnection jingleConnection, JingleCandidate candidate) {
JingleCandidate candidate) {
this.candidate = candidate; this.candidate = candidate;
this.connection = jingleConnection; this.connection = jingleConnection;
try { try {
MessageDigest mDigest = MessageDigest.getInstance("SHA-1"); MessageDigest mDigest = MessageDigest.getInstance("SHA-1");
StringBuilder destBuilder = new StringBuilder(); StringBuilder destBuilder = new StringBuilder();
if (jingleConnection.getFtVersion() == Content.Version.FT_3) { 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()); destBuilder.append(jingleConnection.getSessionId());
} else { } else {
destBuilder.append(jingleConnection.getTransportId()); destBuilder.append(jingleConnection.getTransportId());
@ -58,124 +58,112 @@ public class JingleSocks5Transport extends JingleTransport {
} }
public void connect(final OnTransportConnected callback) { public void connect(final OnTransportConnected callback) {
new Thread(new Runnable() { new Thread(() -> {
try {
@Override final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect();
public void run() { if (useTor) {
try { socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(), candidate.getPort());
final boolean useTor = connection.getAccount().isOnion() || connection.getConnectionManager().getXmppConnectionService().useTorToConnect(); } else {
if (useTor) { socket = new Socket();
socket = SocksSocketFactory.createSocketOverTor(candidate.getHost(),candidate.getPort()); SocketAddress address = new InetSocketAddress(candidate.getHost(), candidate.getPort());
} else { socket.connect(address, Config.SOCKET_TIMEOUT * 1000);
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();
} }
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
SocksSocketFactory.createSocksConnection(socket, destination, 0);
isEstablished = true;
callback.established();
} catch (IOException e) {
callback.failed();
} }
}).start(); }).start();
} }
public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { public void send(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() { new Thread(() -> {
InputStream fileInputStream = null;
@Override final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_" + connection.getSessionId());
public void run() { try {
InputStream fileInputStream = null; wakeLock.acquire();
final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_send_"+connection.getSessionId()); MessageDigest digest = MessageDigest.getInstance("SHA-1");
try { digest.reset();
wakeLock.acquire(); fileInputStream = connection.getFileInputStream();
MessageDigest digest = MessageDigest.getInstance("SHA-1"); if (fileInputStream == null) {
digest.reset(); Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create input stream");
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());
callback.onFileTransferAborted(); callback.onFileTransferAborted();
} finally { return;
FileBackend.close(fileInputStream);
wakeLock.release();
} }
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(); }).start();
} }
public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) { public void receive(final DownloadableFile file, final OnFileTransmissionStatusChanged callback) {
new Thread(new Runnable() { new Thread(() -> {
OutputStream fileOutputStream = null;
@Override final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_" + connection.getSessionId());
public void run() { try {
OutputStream fileOutputStream = null; wakeLock.acquire();
final PowerManager.WakeLock wakeLock = connection.getConnectionManager().createWakeLock("jingle_receive_"+connection.getSessionId()); MessageDigest digest = MessageDigest.getInstance("SHA-1");
try { digest.reset();
wakeLock.acquire(); //inputStream.skip(45);
MessageDigest digest = MessageDigest.getInstance("SHA-1"); socket.setSoTimeout(30000);
digest.reset(); fileOutputStream = connection.getFileOutputStream();
//inputStream.skip(45); if (fileOutputStream == null) {
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());
callback.onFileTransferAborted(); callback.onFileTransferAborted();
} finally { Log.d(Config.LOGTAG, connection.getAccount().getJid().asBareJid() + ": could not create output stream");
wakeLock.release(); return;
FileBackend.close(fileOutputStream);
FileBackend.close(inputStream);
} }
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(); }).start();
} }