diff --git a/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java b/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java index e0eb5fa4f..9931491dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ConfirmIdentityDialog.java @@ -12,11 +12,10 @@ import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessageDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; @@ -95,7 +94,7 @@ public class ConfirmIdentityDialog extends AlertDialog { { @Override protected Void doInBackground(Void... params) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1); TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java index a7652fa83..118c9b81a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/VerifyIdentityActivity.java @@ -29,7 +29,6 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; import android.os.AsyncTask; import android.os.Bundle; import android.os.Vibrator; @@ -63,9 +62,8 @@ import androidx.fragment.app.FragmentTransaction; import org.signal.core.util.ThreadUtil; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.color.MaterialColor; import org.thoughtcrime.securesms.components.camera.CameraView; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -87,16 +85,13 @@ import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; -import org.thoughtcrime.securesms.util.WindowUtil; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.fingerprint.Fingerprint; -import org.whispersystems.libsignal.fingerprint.FingerprintParsingException; import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException; import org.whispersystems.libsignal.fingerprint.NumericFingerprintGenerator; import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.util.UuidUtil; -import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Locale; @@ -615,7 +610,7 @@ public class VerifyIdentityActivity extends PassphraseRequiredActivity implement final RecipientId recipientId = recipient.getId(); SignalExecutors.BOUNDED.execute(() -> { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { if (isChecked) { Log.i(TAG, "Saving identity: " + recipientId); DatabaseFactory.getIdentityDatabase(getActivity()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java index 93faef379..e8019bf66 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UntrustedSendDialog.java @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; @@ -43,7 +43,7 @@ public class UntrustedSendDialog extends AlertDialog.Builder implements DialogIn final IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(getContext()); SimpleTask.run(() -> { - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { for (IdentityRecord identityRecord : untrustedRecords) { identityDatabase.setApproval(identityRecord.getRecipientId(), true); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java index d1e981c3a..2a182c2c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/identity/UnverifiedSendDialog.java @@ -8,7 +8,7 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord; @@ -44,7 +44,7 @@ public class UnverifiedSendDialog extends AlertDialog.Builder implements DialogI new AsyncTask() { @Override protected Void doInBackground(Void... params) { - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { for (IdentityRecord identityRecord : untrustedRecords) { identityDatabase.setVerified(identityRecord.getRecipientId(), identityRecord.getIdentityKey(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index bee0a4ffc..9ee832d53 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -143,7 +143,7 @@ import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel; import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel; import org.thoughtcrime.securesms.search.MessageResult; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DraftDatabase; @@ -3759,7 +3759,7 @@ public class ConversationActivity extends PassphraseRequiredActivity new AsyncTask() { @Override protected Void doInBackground(Void... params) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { for (IdentityRecord identityRecord : unverifiedIdentities) { identityDatabase.setVerified(identityRecord.getRecipientId(), identityRecord.getIdentityKey(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java index f8c4f1e5f..53d997d5e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ui/error/SafetyNumberChangeRepository.java @@ -12,8 +12,8 @@ import com.annimon.stream.Stream; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; import org.thoughtcrime.securesms.crypto.SessionUtil; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.IdentityDatabase; @@ -96,7 +96,7 @@ final class SafetyNumberChangeRepository { private TrustAndVerifyResult trustOrVerifyChangedRecipientsInternal(@NonNull List changedRecipients) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { for (ChangedRecipient changedRecipient : changedRecipients) { IdentityRecord identityRecord = changedRecipient.getIdentityRecord(); @@ -122,7 +122,7 @@ final class SafetyNumberChangeRepository { Log.d(TAG, "No changed recipients to process, will still process message record"); } - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { for (ChangedRecipient changedRecipient : changedRecipients) { SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(changedRecipient.getRecipient().requireServiceId(), 1); TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context); diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSessionLock.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSessionLock.java deleted file mode 100644 index 99f14e76e..000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/DatabaseSessionLock.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.thoughtcrime.securesms.crypto; - -import org.whispersystems.signalservice.api.SignalSessionLock; - -import java.util.concurrent.locks.ReentrantLock; - -/** - * An implementation of {@link SignalSessionLock} that effectively re-uses our database lock. - */ -public enum DatabaseSessionLock implements SignalSessionLock { - - INSTANCE; - - public static final long NO_OWNER = -1; - - private static final ReentrantLock LEGACY_LOCK = new ReentrantLock(); - - private volatile long ownerThreadId = NO_OWNER; - - @Override - public Lock acquire() { - LEGACY_LOCK.lock(); - return LEGACY_LOCK::unlock; - - // TODO [greyson][db] Revisit after improving database locking -// SQLiteDatabase db = DatabaseFactory.getInstance(ApplicationDependencies.getApplication()).getRawDatabase(); -// -// if (db.isDbLockedByCurrentThread()) { -// return () -> {}; -// } -// -// db.beginTransaction(); -// -// ownerThreadId = Thread.currentThread().getId(); -// -// return () -> { -// ownerThreadId = -1; -// db.setTransactionSuccessful(); -// db.endTransaction(); -// }; - } - - /** - * Important: Only truly useful for debugging. Do not rely on this for functionality. There's tiny - * windows where this state might not be fully accurate. - * - * @return True if it's likely that some other thread owns this lock, and it's not you. - */ - public boolean isLikelyHeldByOtherThread() { - long ownerThreadId = this.ownerThreadId; - return ownerThreadId != -1 && ownerThreadId == Thread.currentThread().getId(); - } - - /** - * Important: Only truly useful for debugging. Do not rely on this for functionality. There's a - * tiny window where a thread may still own the lock, but the state we track around it has been - * cleared. - * - * @return The ID of the thread that likely owns this lock, or {@link #NO_OWNER} if no one owns it. - */ - public long getLikeyOwnerThreadId() { - return ownerThreadId; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/ReentrantSessionLock.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/ReentrantSessionLock.java new file mode 100644 index 000000000..0b03538c6 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/ReentrantSessionLock.java @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.crypto; + +import org.whispersystems.signalservice.api.SignalSessionLock; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * An implementation of {@link SignalSessionLock} that is backed by a {@link ReentrantLock}. + */ +public enum ReentrantSessionLock implements SignalSessionLock { + + INSTANCE; + + private static final ReentrantLock LOCK = new ReentrantLock(); + + @Override + public Lock acquire() { + LOCK.lock(); + return LOCK::unlock; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java index 40dc2a6ba..5a1e64ea3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/SenderKeyUtil.java @@ -18,7 +18,7 @@ public final class SenderKeyUtil { * Clears the state for a sender key session we created. It will naturally get re-created when it is next needed, rotating the key. */ public static void rotateOurKey(@NonNull Context context, @NonNull DistributionId distributionId) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { new SignalSenderKeyStore(context).deleteAllFor(Recipient.self().getId(), distributionId); DatabaseFactory.getSenderKeySharedDatabase(context).deleteAllFor(distributionId); } @@ -35,7 +35,7 @@ public final class SenderKeyUtil { * Deletes all stored state around session keys. Should only really be used when the user is re-registering. */ public static void clearAllState(@NonNull Context context) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { new SignalSenderKeyStore(context).deleteAll(); DatabaseFactory.getSenderKeySharedDatabase(context).deleteAll(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java index 6a70267a8..e488bc89c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalSenderKeyStore.java @@ -4,14 +4,12 @@ import android.content.Context; import androidx.annotation.NonNull; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore; import org.whispersystems.signalservice.api.push.DistributionId; import org.thoughtcrime.securesms.recipients.RecipientId; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.groups.state.SenderKeyRecord; -import org.whispersystems.signalservice.api.SignalSessionLock; import java.util.Collection; import java.util.Set; @@ -25,6 +23,8 @@ import javax.annotation.Nullable; */ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { + private static final Object LOCK = new Object(); + private final Context context; public SignalSenderKeyStore(@NonNull Context context) { @@ -33,7 +33,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { @Override public void storeSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId, @NonNull SenderKeyRecord record) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { RecipientId recipientId = RecipientId.fromExternalPush(sender.getName()); DatabaseFactory.getSenderKeyDatabase(context).store(recipientId, sender.getDeviceId(), DistributionId.from(distributionId), record); } @@ -41,7 +41,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { @Override public @Nullable SenderKeyRecord loadSenderKey(@NonNull SignalProtocolAddress sender, @NonNull UUID distributionId) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { RecipientId recipientId = RecipientId.fromExternalPush(sender.getName()); return DatabaseFactory.getSenderKeyDatabase(context).load(recipientId, sender.getDeviceId(), DistributionId.from(distributionId)); } @@ -49,21 +49,21 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { @Override public Set getSenderKeySharedWith(DistributionId distributionId) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { return DatabaseFactory.getSenderKeySharedDatabase(context).getSharedWith(distributionId); } } @Override public void markSenderKeySharedWith(DistributionId distributionId, Collection addresses) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getSenderKeySharedDatabase(context).markAsShared(distributionId, addresses); } } @Override public void clearSenderKeySharedWith(DistributionId distributionId, Collection addresses) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getSenderKeySharedDatabase(context).delete(distributionId, addresses); } } @@ -72,7 +72,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { * Removes all sender key session state for all devices for the provided recipient-distributionId pair. */ public void deleteAllFor(@NonNull RecipientId recipientId, @NonNull DistributionId distributionId) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getSenderKeyDatabase(context).deleteAllFor(recipientId, distributionId); } } @@ -81,7 +81,7 @@ public final class SignalSenderKeyStore implements SignalServiceSenderKeyStore { * Deletes all sender key session state. */ public void deleteAll() { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getSenderKeyDatabase(context).deleteAll(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java index 5cd36972f..c3258783b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureIdentityKeyStore.java @@ -5,7 +5,6 @@ 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.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.SessionUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -21,7 +20,6 @@ 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; @@ -49,7 +47,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore { } public @NonNull SaveResult saveIdentity(SignalProtocolAddress address, IdentityKey identityKey, boolean nonBlockingApproval) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); Optional identityRecord = identityDatabase.getIdentity(recipientId); @@ -96,7 +94,7 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore { @Override public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); RecipientId ourRecipientId = Recipient.self().getId(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java index 72a2942ad..b184f2381 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecurePreKeyStore.java @@ -5,14 +5,12 @@ 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.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; @@ -21,6 +19,8 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { @SuppressWarnings("unused") private static final String TAG = Log.tag(TextSecurePreKeyStore.class); + private static final Object LOCK = new Object(); + @NonNull private final Context context; @@ -30,7 +30,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { @Override public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { PreKeyRecord preKeyRecord = DatabaseFactory.getPreKeyDatabase(context).getPreKey(preKeyId); if (preKeyRecord == null) throw new InvalidKeyIdException("No such key: " + preKeyId); @@ -40,7 +40,7 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { @Override public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { SignedPreKeyRecord signedPreKeyRecord = DatabaseFactory.getSignedPreKeyDatabase(context).getSignedPreKey(signedPreKeyId); if (signedPreKeyRecord == null) throw new InvalidKeyIdException("No such signed prekey: " + signedPreKeyId); @@ -50,21 +50,21 @@ public class TextSecurePreKeyStore implements PreKeyStore, SignedPreKeyStore { @Override public List loadSignedPreKeys() { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { return DatabaseFactory.getSignedPreKeyDatabase(context).getAllSignedPreKeys(); } } @Override public void storePreKey(int preKeyId, PreKeyRecord record) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getPreKeyDatabase(context).insertPreKey(preKeyId, record); } } @Override public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { DatabaseFactory.getSignedPreKeyDatabase(context).insertSignedPreKey(signedPreKeyId, record); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index 4d52b0591..15958b24c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -5,7 +5,6 @@ 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.database.SessionDatabase.RecipientDevice; @@ -16,7 +15,6 @@ 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; @@ -26,6 +24,8 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { private static final String TAG = Log.tag(TextSecureSessionStore.class); + private static final Object LOCK = new Object(); + @NonNull private final Context context; public TextSecureSessionStore(@NonNull Context context) { @@ -34,7 +34,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public SessionRecord loadSession(@NonNull SignalProtocolAddress address) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId()); @@ -49,7 +49,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public List loadExistingSessions(List addresses) throws NoSessionException { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { List ids = addresses.stream() .map(address -> new RecipientDevice(RecipientId.fromExternalPush(address.getName()), address.getDeviceId())) .collect(Collectors.toList()); @@ -68,7 +68,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { RecipientId id = RecipientId.fromExternalPush(address.getName()); DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record); } @@ -76,7 +76,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public boolean containsSession(SignalProtocolAddress address) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId()); @@ -92,7 +92,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public void deleteSession(SignalProtocolAddress address) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId()); @@ -104,7 +104,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public void deleteAllSessions(String name) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) { RecipientId recipientId = RecipientId.fromExternalPush(name); DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId); @@ -114,7 +114,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public List getSubDeviceSessions(String name) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) { RecipientId recipientId = RecipientId.fromExternalPush(name); return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId); @@ -127,7 +127,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public void archiveSession(SignalProtocolAddress address) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); archiveSession(recipientId, address.getDeviceId()); @@ -136,7 +136,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { } public void archiveSession(@NonNull RecipientId recipientId, int deviceId) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId); if (session != null) { session.archiveCurrentState(); @@ -146,7 +146,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { } public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); List sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId); @@ -164,7 +164,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { } public void archiveAllSessions() { - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + synchronized (LOCK) { List sessions = DatabaseFactory.getSessionDatabase(context).getAll(); for (SessionDatabase.SessionRow row : sessions) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 26daa1bcb..23368b242 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -10,7 +10,7 @@ import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.components.TypingStatusRepository; import org.thoughtcrime.securesms.components.TypingStatusSender; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.database.DatabaseObserver; import org.thoughtcrime.securesms.database.JobDatabase; @@ -109,7 +109,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr return new SignalServiceMessageSender(provideSignalServiceNetworkAccess().getConfiguration(context), new DynamicCredentialsProvider(context), new SignalProtocolStoreImpl(context), - DatabaseSessionLock.INSTANCE, + ReentrantSessionLock.INSTANCE, BuildConfig.SIGNAL_AGENT, TextSecurePreferences.isMultiDevice(context), Optional.fromNullable(IncomingMessageObserver.getPipe()), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java index 3b523f57d..1b2f3fa02 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java @@ -8,53 +8,20 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import org.signal.core.util.logging.Log; -import org.signal.libsignal.metadata.InvalidMetadataMessageException; -import org.signal.libsignal.metadata.InvalidMetadataVersionException; -import org.signal.libsignal.metadata.ProtocolDuplicateMessageException; -import org.signal.libsignal.metadata.ProtocolException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyException; -import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException; -import org.signal.libsignal.metadata.ProtocolInvalidMessageException; -import org.signal.libsignal.metadata.ProtocolInvalidVersionException; -import org.signal.libsignal.metadata.ProtocolLegacyMessageException; -import org.signal.libsignal.metadata.ProtocolNoSessionException; -import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; -import org.signal.libsignal.metadata.SelfSendException; import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.groups.BadGroupIdException; -import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.messages.MessageContentProcessor; -import org.thoughtcrime.securesms.messages.MessageContentProcessor.ExceptionMetadata; import org.thoughtcrime.securesms.messages.MessageContentProcessor.MessageState; import org.thoughtcrime.securesms.messages.MessageDecryptionUtil; import org.thoughtcrime.securesms.messages.MessageDecryptionUtil.DecryptionResult; import org.thoughtcrime.securesms.notifications.NotificationChannels; -import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.whispersystems.libsignal.state.SignalProtocolStore; -import org.whispersystems.libsignal.util.guava.Optional; -import org.whispersystems.signalservice.api.crypto.SignalServiceCipher; -import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; -import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.internal.push.UnsupportedDataMessageException; -import java.util.ArrayList; -import java.util.Collections; import java.util.LinkedList; import java.util.List; diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageProcessor.java index df0fba539..28bd2bda1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/IncomingMessageProcessor.java @@ -7,13 +7,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId; import org.thoughtcrime.securesms.database.MmsSmsDatabase; -import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.groups.BadGroupIdException; import org.thoughtcrime.securesms.groups.GroupChangeBusyException; @@ -25,7 +24,6 @@ import org.thoughtcrime.securesms.jobs.PushProcessMessageJob; import org.thoughtcrime.securesms.messages.MessageDecryptionUtil.DecryptionResult; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.Stopwatch; @@ -123,12 +121,7 @@ public class IncomingMessageProcessor { stopwatch.split("queue-check"); - long ownerThreadId = DatabaseSessionLock.INSTANCE.getLikeyOwnerThreadId(); - if (ownerThreadId != DatabaseSessionLock.NO_OWNER && ownerThreadId != Thread.currentThread().getId()) { - Log.i(TAG, "It is likely that some other thread has this lock. Owner: " + ownerThreadId + ", Us: " + Thread.currentThread().getId()); - } - - try (SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try (SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { Log.i(TAG, "Acquired lock while processing message " + envelope.getTimestamp() + "."); DecryptionResult result = MessageDecryptionUtil.decrypt(context, envelope); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptionUtil.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptionUtil.java index 35b4ef741..ccdc2aad8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptionUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptionUtil.java @@ -18,7 +18,7 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException; import org.signal.libsignal.metadata.ProtocolNoSessionException; import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException; import org.signal.libsignal.metadata.SelfSendException; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -68,7 +68,7 @@ public final class MessageDecryptionUtil { public static @NonNull DecryptionResult decrypt(@NonNull Context context, @NonNull SignalServiceEnvelope envelope) { SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); SignalServiceAddress localAddress = new SignalServiceAddress(Optional.of(TextSecurePreferences.getLocalUuid(context)), Optional.of(TextSecurePreferences.getLocalNumber(context))); - SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, DatabaseSessionLock.INSTANCE, UnidentifiedAccessUtil.getCertificateValidator()); + SignalServiceCipher cipher = new SignalServiceCipher(localAddress, axolotlStore, ReentrantSessionLock.INSTANCE, UnidentifiedAccessUtil.getCertificateValidator()); List jobs = new LinkedList<>(); if (envelope.isPreKeySignalMessage()) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java index a12c31583..cc3caac97 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/IdentityUtil.java @@ -9,7 +9,7 @@ import androidx.annotation.StringRes; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.crypto.DatabaseSessionLock; +import org.thoughtcrime.securesms.crypto.ReentrantSessionLock; import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.database.DatabaseFactory; @@ -143,7 +143,7 @@ public final class IdentityUtil { } public static void saveIdentity(Context context, String user, IdentityKey identityKey) { - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context); SessionStore sessionStore = new TextSecureSessionStore(context); SignalProtocolAddress address = new SignalProtocolAddress(user, 1); @@ -160,7 +160,7 @@ public final class IdentityUtil { } public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) { - try(SignalSessionLock.Lock unused = DatabaseSessionLock.INSTANCE.acquire()) { + try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) { IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context); Recipient recipient = Recipient.externalPush(context, verifiedMessage.getDestination()); Optional identityRecord = identityDatabase.getIdentity(recipient.getId());