mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 00:59:49 +01:00
Perform individual decryptions inside a database transaction.
Required a lot of random locking work to prevent deadlocking, but overall this results in about a 2x speed increase for decryptions.
This commit is contained in:
committed by
Cody Henthorne
parent
d56607a686
commit
28f3ded4bd
@@ -0,0 +1,31 @@
|
||||
package org.thoughtcrime.securesms.crypto;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
|
||||
/**
|
||||
* An implementation of {@link SignalSessionLock} that effectively re-uses our database lock.
|
||||
*/
|
||||
public enum DatabaseSessionLock implements SignalSessionLock {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Lock acquire() {
|
||||
SQLiteDatabase db = DatabaseFactory.getInstance(ApplicationDependencies.getApplication()).getRawDatabase();
|
||||
|
||||
if (db.isDbLockedByCurrentThread()) {
|
||||
return () -> {};
|
||||
}
|
||||
|
||||
db.beginTransaction();
|
||||
|
||||
return () -> {
|
||||
db.setTransactionSuccessful();
|
||||
db.endTransaction();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.SessionUtil;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
@@ -18,6 +19,7 @@ import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.state.IdentityKeyStore;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -45,7 +47,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
||||
}
|
||||
|
||||
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) {
|
||||
synchronized (LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
Recipient recipient = Recipient.external(context, address.getName());
|
||||
Optional<IdentityRecord> identityRecord = identityDatabase.getIdentity(recipient.getId());
|
||||
@@ -91,7 +93,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
|
||||
|
||||
@Override
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
|
||||
synchronized (LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
|
||||
RecipientId ourRecipientId = Recipient.self().getId();
|
||||
|
||||
@@ -4,12 +4,14 @@ import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.PreKeyStore;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyStore;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,8 +20,6 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = TextSecurePreKeyStore.class.getSimpleName();
|
||||
|
||||
private static final Object FILE_LOCK = new Object();
|
||||
|
||||
@NonNull
|
||||
private final Context context;
|
||||
|
||||
@@ -29,7 +29,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
|
||||
@Override
|
||||
public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId);
|
||||
|
||||
if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId);
|
||||
@@ -39,7 +39,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
|
||||
@Override
|
||||
public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId);
|
||||
|
||||
if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId);
|
||||
@@ -49,21 +49,21 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
|
||||
@Override
|
||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePreKey(int preKeyId, PreKeyRecord record) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record);
|
||||
}
|
||||
}
|
||||
@@ -87,5 +87,4 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore {
|
||||
public void removeSignedPreKey(int signedPreKeyId) {
|
||||
DatabaseFactory.getSignedPreKeyDatabase(context).removeSignedPreKey(signedPreKeyId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.crypto.DatabaseSessionLock;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.SessionDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@@ -13,6 +14,7 @@ import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.signalservice.api.SignalServiceSessionStore;
|
||||
import org.whispersystems.signalservice.api.SignalSessionLock;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -21,8 +23,6 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
private static final String TAG = TextSecureSessionStore.class.getSimpleName();
|
||||
|
||||
private static final Object FILE_LOCK = new Object();
|
||||
|
||||
@NonNull private final Context context;
|
||||
|
||||
public TextSecureSessionStore(@NonNull Context context) {
|
||||
@@ -31,7 +31,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public SessionRecord loadSession(@NonNull SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
||||
|
||||
@@ -46,7 +46,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
RecipientId id = Recipient.external(context, address.getName()).getId();
|
||||
DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public boolean containsSession(SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||
SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId());
|
||||
@@ -70,7 +70,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public void deleteSession(SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||
DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId());
|
||||
@@ -82,7 +82,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public void deleteAllSessions(String name) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
||||
RecipientId recipientId = Recipient.external(context, name).getId();
|
||||
DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId);
|
||||
@@ -92,7 +92,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public List<Integer> getSubDeviceSessions(String name) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) {
|
||||
RecipientId recipientId = Recipient.external(context, name).getId();
|
||||
return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId);
|
||||
@@ -105,7 +105,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
|
||||
@Override
|
||||
public void archiveSession(SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||
archiveSession(recipientId, address.getDeviceId());
|
||||
@@ -114,7 +114,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
}
|
||||
|
||||
public void archiveSession(@NonNull RecipientId recipientId, int deviceId) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId);
|
||||
if (session != null) {
|
||||
session.archiveCurrentState();
|
||||
@@ -124,7 +124,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
}
|
||||
|
||||
public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) {
|
||||
RecipientId recipientId = Recipient.external(context, address.getName()).getId();
|
||||
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId);
|
||||
@@ -142,7 +142,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore {
|
||||
}
|
||||
|
||||
public void archiveAllSessions() {
|
||||
synchronized (FILE_LOCK) {
|
||||
try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) {
|
||||
List<SessionDatabase.SessionRow> sessions = DatabaseFactory.getSessionDatabase(context).getAll();
|
||||
|
||||
for (SessionDatabase.SessionRow row : sessions) {
|
||||
|
||||
Reference in New Issue
Block a user