diff --git a/app/build.gradle b/app/build.gradle index 83c27276a..098c861c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,7 @@ android { packagingOptions { resources { - excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/proguard/androidx-annotations.pro', 'libsignal_jni.dylib', 'signal_jni.dll'] + excludes += ['LICENSE.txt', 'LICENSE', 'NOTICE', 'asm-license.txt', 'META-INF/LICENSE', 'META-INF/LICENSE.md', 'META-INF/NOTICE', 'META-INF/LICENSE-notice.md', 'META-INF/proguard/androidx-annotations.pro', 'libsignal_jni.dylib', 'signal_jni.dll'] } } @@ -570,6 +570,7 @@ dependencies { androidTestImplementation testLibs.androidx.test.ext.junit.ktx androidTestImplementation testLibs.mockito.android androidTestImplementation testLibs.mockito.kotlin + androidTestImplementation testLibs.mockk.android androidTestImplementation testLibs.square.okhttp.mockserver instrumentationImplementation (libs.androidx.fragment.testing) { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/SignalInstrumentationApplicationContext.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/SignalInstrumentationApplicationContext.kt index 0fdd1d28e..c979bd073 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/SignalInstrumentationApplicationContext.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/SignalInstrumentationApplicationContext.kt @@ -1,15 +1,39 @@ package org.thoughtcrime.securesms +import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.logging.AndroidLogger +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.logging.SignalProtocolLoggerProvider +import org.thoughtcrime.securesms.database.LogDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider import org.thoughtcrime.securesms.dependencies.InstrumentationApplicationDependencyProvider +import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger +import org.thoughtcrime.securesms.logging.PersistentLogger +import org.thoughtcrime.securesms.testing.InMemoryLogger /** * Application context for running instrumentation tests (aka androidTests). */ class SignalInstrumentationApplicationContext : ApplicationContext() { + + val inMemoryLogger: InMemoryLogger = InMemoryLogger() + override fun initializeAppDependencies() { val default = ApplicationDependencyProvider(this) ApplicationDependencies.init(this, InstrumentationApplicationDependencyProvider(this, default)) } + + override fun initializeLogging() { + persistentLogger = PersistentLogger(this) + + Log.initialize({ true }, AndroidLogger(), persistentLogger, inMemoryLogger) + + SignalProtocolLoggerProvider.setProvider(CustomSignalProtocolLogger()) + + SignalExecutors.UNBOUNDED.execute { + Log.blockUntilAllWritesFinished() + LogDatabase.getInstance(this).trimToSize() + } + } } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt index 876641d29..9ec88c352 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/database/AttachmentTableTest.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database import android.net.Uri import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.FlakyTest import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Before @@ -34,6 +35,7 @@ class AttachmentTableTest { assertEquals(attachment2.fileName, attachment.fileName) } + @FlakyTest @Test fun givenABlobAndDifferentTransformQuality_whenIInsert2AttachmentsForPreUpload_thenIExpectDifferentFileInfos() { val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() @@ -61,6 +63,7 @@ class AttachmentTableTest { assertNotEquals(attachment1Info, attachment2Info) } + @FlakyTest @Test fun givenIdenticalAttachmentsInsertedForPreUpload_whenIUpdateAttachmentDataAndSpecifyOnlyModifyThisAttachment_thenIExpectDifferentFileInfos() { val blob = BlobProvider.getInstance().forData(byteArrayOf(1, 2, 3, 4, 5)).createForSingleSessionInMemory() diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageProcessingPerformanceTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageProcessingPerformanceTest.kt new file mode 100644 index 000000000..3e133eb0c --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageProcessingPerformanceTest.kt @@ -0,0 +1,188 @@ +package org.thoughtcrime.securesms.messages + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import org.junit.After +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.signal.core.util.logging.Log +import org.signal.libsignal.protocol.ecc.Curve +import org.signal.libsignal.protocol.ecc.ECKeyPair +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testing.AliceClient +import org.thoughtcrime.securesms.testing.BobClient +import org.thoughtcrime.securesms.testing.Entry +import org.thoughtcrime.securesms.testing.FakeClientHelpers +import org.thoughtcrime.securesms.testing.SignalActivityRule +import org.thoughtcrime.securesms.testing.awaitFor +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +/** + * Sends N messages from Bob to Alice to track performance of Alice's processing of messages. + */ +@Ignore("Ignore test in normal testing as it's a performance test with no assertions") +@RunWith(AndroidJUnit4::class) +class MessageProcessingPerformanceTest { + + companion object { + private val TAG = Log.tag(MessageProcessingPerformanceTest::class.java) + private val TIMING_TAG = "TIMING_$TAG".substring(0..23) + + private val jobFinishRegex = "\\[JOB::[a-f\\d]{8}-[a-f\\d]{4}-[a-f\\d]{4}-[a-f\\d]{4}-[a-f\\d]{12}]\\[([^]]*)]\\[\\d+] Job finished with result SUCCESS in (\\d+) ms. \\(Time Since Submission: (\\d+) ms.*".toRegex() + } + + @get:Rule + val harness = SignalActivityRule() + + private val trustRoot: ECKeyPair = Curve.generateKeyPair() + + @Before + fun setup() { + mockkStatic(UnidentifiedAccessUtil::class) + every { UnidentifiedAccessUtil.getCertificateValidator() } returns FakeClientHelpers.noOpCertificateValidator + + mockkStatic(MessageContentProcessor::class) + every { MessageContentProcessor.create(harness.application) } returns TimingMessageContentProcessor(harness.application) + } + + @After + fun after() { + unmockkStatic(UnidentifiedAccessUtil::class) + unmockkStatic(MessageContentProcessor::class) + } + + @Test + fun testPerformance() { + val aliceClient = AliceClient( + serviceId = harness.self.requireServiceId(), + e164 = harness.self.requireE164(), + trustRoot = trustRoot + ) + + val bob = Recipient.resolved(harness.others[0]) + val bobClient = BobClient( + serviceId = bob.requireServiceId(), + e164 = bob.requireE164(), + identityKeyPair = harness.othersKeys[0], + trustRoot = trustRoot, + profileKey = ProfileKey(bob.profileKey) + ) + + // Send message from Bob to Alice (self) + + val firstPreKeyMessageTimestamp = System.currentTimeMillis() + val encryptedEnvelope = bobClient.encrypt(firstPreKeyMessageTimestamp) + + val aliceProcessFirstMessageLatch = harness + .inMemoryLogger + .getLockForUntil(TimingMessageContentProcessor.endTagPredicate(firstPreKeyMessageTimestamp)) + + Thread { aliceClient.process(encryptedEnvelope) }.start() + aliceProcessFirstMessageLatch.awaitFor(15.seconds) + + // Send message from Alice to Bob + bobClient.decrypt(aliceClient.encrypt(System.currentTimeMillis(), bob)) + + // Build N messages from Bob to Alice + + val messageCount = 100 + val envelopes = ArrayList(messageCount) + var now = System.currentTimeMillis() + for (i in 0..messageCount) { + envelopes += bobClient.encrypt(now) + now += 3 + } + + val firstTimestamp = envelopes.first().timestamp + val lastTimestamp = envelopes.last().timestamp + + // Alice processes N messages + + val aliceProcessLastMessageLatch = harness + .inMemoryLogger + .getLockForUntil(TimingMessageContentProcessor.endTagPredicate(lastTimestamp)) + + Thread { + for (envelope in envelopes) { + Log.i(TIMING_TAG, "Retrieved envelope! ${envelope.timestamp}") + aliceClient.process(envelope) + } + }.start() + + // Wait for Alice to finish processing messages + aliceProcessLastMessageLatch.awaitFor(1.minutes) + harness.inMemoryLogger.flush() + + // Process logs for timing data + val entries = harness.inMemoryLogger.entries() + + // Calculate decrypt jobs + var skipFirst = true + var decryptJobCount = 0L + var decryptJobDuration = 0L + var decryptJobSinceSubmission = 0L + var firstDuration = 0L + var firstSinceSubmission = 0L + entries.filter { it.tag == "JobRunner" } + .forEach { + val match = jobFinishRegex.matchEntire(it.message!!) + + if (match != null) { + val job = match.groupValues[1] + if (job == "PushDecryptMessageJob") { + if (skipFirst) { + skipFirst = false + } else { + val duration = match.groupValues[2].toLong() + val sinceSubmission = match.groupValues[3].toLong() + decryptJobCount++ + decryptJobDuration += duration + decryptJobSinceSubmission += sinceSubmission + + if (decryptJobCount == 1L) { + firstDuration = duration + firstSinceSubmission = sinceSubmission + } + } + } + } + } + + android.util.Log.w(TAG, "Push Decrypt Job: First runtime ${firstDuration}ms First since submission: ${firstSinceSubmission}ms") + android.util.Log.w(TAG, "Push Decrypt Job: Average runtime: ${decryptJobDuration.toFloat() / decryptJobCount.toFloat()}ms Average since submission: ${decryptJobSinceSubmission.toFloat() / decryptJobCount.toFloat()}ms") + + // Calculate MessageContentProcessor + + val takeLast: List = entries.filter { it.tag == TimingMessageContentProcessor.TAG }.drop(2) + val iterator = takeLast.iterator() + var processCount = 0L + var processDuration = 0L + while (iterator.hasNext()) { + val start = iterator.next() + val end = iterator.next() + processCount++ + processDuration += end.timestamp - start.timestamp + } + + android.util.Log.w(TAG, "MessageContentProcessor.process: Average runtime: ${processDuration.toFloat() / processCount.toFloat()}ms") + + // Calculate messages per second from "retrieving" first message post session initialization to processing last message + + val start = entries.first { it.message == "Retrieved envelope! $firstTimestamp" } + val end = entries.first { it.message == TimingMessageContentProcessor.endTag(lastTimestamp) } + + val duration = (end.timestamp - start.timestamp).toFloat() / 1000f + val messagePerSecond = messageCount.toFloat() / duration + + android.util.Log.w(TAG, "Processing $messageCount messages took ${duration}s or ${messagePerSecond}m/s") + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/TimingMessageContentProcessor.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/TimingMessageContentProcessor.kt new file mode 100644 index 000000000..9fa27e754 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/TimingMessageContentProcessor.kt @@ -0,0 +1,25 @@ +package org.thoughtcrime.securesms.messages + +import android.content.Context +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.testing.LogPredicate +import org.whispersystems.signalservice.api.messages.SignalServiceContent + +class TimingMessageContentProcessor(context: Context) : MessageContentProcessor(context) { + companion object { + val TAG = Log.tag(TimingMessageContentProcessor::class.java) + + fun endTagPredicate(timestamp: Long): LogPredicate = { entry -> + entry.tag == TAG && entry.message == endTag(timestamp) + } + + private fun startTag(timestamp: Long) = "$timestamp start" + fun endTag(timestamp: Long) = "$timestamp end" + } + + override fun process(messageState: MessageState?, content: SignalServiceContent?, exceptionMetadata: ExceptionMetadata?, envelopeTimestamp: Long, smsMessageId: Long) { + Log.d(TAG, startTag(envelopeTimestamp)) + super.process(messageState, content, exceptionMetadata, envelopeTimestamp, smsMessageId) + Log.d(TAG, endTag(envelopeTimestamp)) + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/AliceClient.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/AliceClient.kt new file mode 100644 index 000000000..c296eb1c1 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/AliceClient.kt @@ -0,0 +1,44 @@ +package org.thoughtcrime.securesms.testing + +import org.signal.libsignal.protocol.ecc.ECKeyPair +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.testing.FakeClientHelpers.toSignalServiceEnvelope +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.push.SignalServiceAddress + +/** + * Welcome to Alice's Client. + * + * Alice represent the Android instrumentation test user. Unlike [BobClient] much less is needed here + * as it can make use of the standard Signal Android App infrastructure. + */ +class AliceClient(val serviceId: ServiceId, val e164: String, val trustRoot: ECKeyPair) { + + private val aliceSenderCertificate = FakeClientHelpers.createCertificateFor( + trustRoot = trustRoot, + uuid = serviceId.uuid(), + e164 = e164, + deviceId = 1, + identityKey = SignalStore.account().aciIdentityKey.publicKey.publicKey, + expires = 31337 + ) + + fun process(envelope: SignalServiceEnvelope) { + ApplicationDependencies.getIncomingMessageProcessor().acquire().use { processor -> processor.processEnvelope(envelope) } + } + + fun encrypt(now: Long, destination: Recipient): SignalServiceEnvelope { + return ApplicationDependencies.getSignalServiceMessageSender().getEncryptedMessage( + SignalServiceAddress(destination.requireServiceId(), destination.requireE164()), + FakeClientHelpers.getTargetUnidentifiedAccess(ProfileKeyUtil.getSelfProfileKey(), ProfileKey(destination.profileKey), aliceSenderCertificate), + 1, + FakeClientHelpers.encryptedTextMessage(now), + false + ).toSignalServiceEnvelope(now, destination.requireServiceId()) + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt new file mode 100644 index 000000000..9a98cc467 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt @@ -0,0 +1,167 @@ +package org.thoughtcrime.securesms.testing + +import org.signal.core.util.readToSingleInt +import org.signal.core.util.select +import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.IdentityKeyPair +import org.signal.libsignal.protocol.SessionBuilder +import org.signal.libsignal.protocol.SignalProtocolAddress +import org.signal.libsignal.protocol.ecc.ECKeyPair +import org.signal.libsignal.protocol.groups.state.SenderKeyRecord +import org.signal.libsignal.protocol.state.IdentityKeyStore +import org.signal.libsignal.protocol.state.PreKeyBundle +import org.signal.libsignal.protocol.state.PreKeyRecord +import org.signal.libsignal.protocol.state.SessionRecord +import org.signal.libsignal.protocol.state.SignedPreKeyRecord +import org.signal.libsignal.protocol.util.KeyHelper +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +import org.thoughtcrime.securesms.database.OneTimePreKeyTable +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.SignedPreKeyTable +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.testing.FakeClientHelpers.toSignalServiceEnvelope +import org.whispersystems.signalservice.api.SignalServiceAccountDataStore +import org.whispersystems.signalservice.api.SignalSessionLock +import org.whispersystems.signalservice.api.crypto.SignalServiceCipher +import org.whispersystems.signalservice.api.crypto.SignalSessionBuilder +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope +import org.whispersystems.signalservice.api.push.DistributionId +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.api.push.SignalServiceAddress +import java.util.Optional +import java.util.UUID +import java.util.concurrent.locks.ReentrantLock + +/** + * Welcome to Bob's Client. + * + * Bob is a "fake" client that can start a session with the Android instrumentation test user (Alice). + * + * Bob can create a new session using a prekey bundle created from Alice's prekeys, send a message, decrypt + * a return message from Alice, and that'll start a standard Signal session with normal keys/ratcheting. + */ +class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: IdentityKeyPair, val trustRoot: ECKeyPair, val profileKey: ProfileKey) { + + private val serviceAddress = SignalServiceAddress(serviceId, e164) + private val registrationId = KeyHelper.generateRegistrationId(false) + private val aciStore = BobSignalServiceAccountDataStore(registrationId, identityKeyPair) + private val senderCertificate = FakeClientHelpers.createCertificateFor(trustRoot, serviceId.uuid(), e164, 1, identityKeyPair.publicKey.publicKey, 31337) + private val sessionLock = object : SignalSessionLock { + private val lock = ReentrantLock() + + override fun acquire(): SignalSessionLock.Lock { + lock.lock() + return SignalSessionLock.Lock { lock.unlock() } + } + } + + /** Inspired by SignalServiceMessageSender#getEncryptedMessage */ + fun encrypt(now: Long): SignalServiceEnvelope { + val envelopeContent = FakeClientHelpers.encryptedTextMessage(now) + + val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, null) + + if (!aciStore.containsSession(getAliceProtocolAddress())) { + val sessionBuilder = SignalSessionBuilder(sessionLock, SessionBuilder(aciStore, getAliceProtocolAddress())) + sessionBuilder.process(getAlicePreKeyBundle()) + } + + return cipher.encrypt(getAliceProtocolAddress(), getAliceUnidentifiedAccess(), envelopeContent) + .toSignalServiceEnvelope(envelopeContent.content.get().dataMessage.timestamp, getAliceServiceId()) + } + + fun decrypt(envelope: SignalServiceEnvelope) { + val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, UnidentifiedAccessUtil.getCertificateValidator()) + cipher.decrypt(envelope) + } + + private fun getAliceServiceId(): ServiceId { + return SignalStore.account().requireAci() + } + + private fun getAlicePreKeyBundle(): PreKeyBundle { + val selfPreKeyId = SignalDatabase.rawDatabase + .select(OneTimePreKeyTable.KEY_ID) + .from(OneTimePreKeyTable.TABLE_NAME) + .where("${OneTimePreKeyTable.ACCOUNT_ID} = ?", getAliceServiceId().toString()) + .run() + .readToSingleInt(-1) + + val selfPreKeyRecord = SignalDatabase.oneTimePreKeys.get(getAliceServiceId(), selfPreKeyId)!! + + val selfSignedPreKeyId = SignalDatabase.rawDatabase + .select(SignedPreKeyTable.KEY_ID) + .from(SignedPreKeyTable.TABLE_NAME) + .where("${SignedPreKeyTable.ACCOUNT_ID} = ?", getAliceServiceId().toString()) + .run() + .readToSingleInt(-1) + + val selfSignedPreKeyRecord = SignalDatabase.signedPreKeys.get(getAliceServiceId(), selfSignedPreKeyId)!! + + return PreKeyBundle( + SignalStore.account().registrationId, + 1, + selfPreKeyId, + selfPreKeyRecord.keyPair.publicKey, + selfSignedPreKeyId, + selfSignedPreKeyRecord.keyPair.publicKey, + selfSignedPreKeyRecord.signature, + getAlicePublicKey() + ) + } + + private fun getAliceProtocolAddress(): SignalProtocolAddress { + return SignalProtocolAddress(SignalStore.account().requireAci().toString(), 1) + } + + private fun getAlicePublicKey(): IdentityKey { + return SignalStore.account().aciIdentityKey.publicKey + } + + private fun getAliceProfileKey(): ProfileKey { + return ProfileKeyUtil.getSelfProfileKey() + } + + private fun getAliceUnidentifiedAccess(): Optional { + return FakeClientHelpers.getTargetUnidentifiedAccess(profileKey, getAliceProfileKey(), senderCertificate) + } + + private class BobSignalServiceAccountDataStore(private val registrationId: Int, private val identityKeyPair: IdentityKeyPair) : SignalServiceAccountDataStore { + private var aliceSessionRecord: SessionRecord? = null + + override fun getIdentityKeyPair(): IdentityKeyPair = identityKeyPair + + override fun getLocalRegistrationId(): Int = registrationId + override fun isTrustedIdentity(address: SignalProtocolAddress?, identityKey: IdentityKey?, direction: IdentityKeyStore.Direction?): Boolean = true + override fun loadSession(address: SignalProtocolAddress?): SessionRecord = aliceSessionRecord ?: SessionRecord() + override fun saveIdentity(address: SignalProtocolAddress?, identityKey: IdentityKey?): Boolean = false + override fun storeSession(address: SignalProtocolAddress?, record: SessionRecord?) { aliceSessionRecord = record } + override fun getSubDeviceSessions(name: String?): List = emptyList() + override fun containsSession(address: SignalProtocolAddress?): Boolean = aliceSessionRecord != null + override fun getIdentity(address: SignalProtocolAddress?): IdentityKey = SignalStore.account().aciIdentityKey.publicKey + + override fun loadPreKey(preKeyId: Int): PreKeyRecord = throw UnsupportedOperationException() + override fun storePreKey(preKeyId: Int, record: PreKeyRecord?) = throw UnsupportedOperationException() + override fun containsPreKey(preKeyId: Int): Boolean = throw UnsupportedOperationException() + override fun removePreKey(preKeyId: Int) = throw UnsupportedOperationException() + override fun loadExistingSessions(addresses: MutableList?): MutableList = throw UnsupportedOperationException() + override fun deleteSession(address: SignalProtocolAddress?) = throw UnsupportedOperationException() + override fun deleteAllSessions(name: String?) = throw UnsupportedOperationException() + override fun loadSignedPreKey(signedPreKeyId: Int): SignedPreKeyRecord = throw UnsupportedOperationException() + override fun loadSignedPreKeys(): MutableList = throw UnsupportedOperationException() + override fun storeSignedPreKey(signedPreKeyId: Int, record: SignedPreKeyRecord?) = throw UnsupportedOperationException() + override fun containsSignedPreKey(signedPreKeyId: Int): Boolean = throw UnsupportedOperationException() + override fun removeSignedPreKey(signedPreKeyId: Int) = throw UnsupportedOperationException() + override fun storeSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?, record: SenderKeyRecord?) = throw UnsupportedOperationException() + override fun loadSenderKey(sender: SignalProtocolAddress?, distributionId: UUID?): SenderKeyRecord = throw UnsupportedOperationException() + override fun archiveSession(address: SignalProtocolAddress?) = throw UnsupportedOperationException() + override fun getAllAddressesWithActiveSessions(addressNames: MutableList?): MutableSet = throw UnsupportedOperationException() + override fun getSenderKeySharedWith(distributionId: DistributionId?): MutableSet = throw UnsupportedOperationException() + override fun markSenderKeySharedWith(distributionId: DistributionId?, addresses: MutableCollection?) = throw UnsupportedOperationException() + override fun clearSenderKeySharedWith(addresses: MutableCollection?) = throw UnsupportedOperationException() + override fun isMultiDevice(): Boolean = throw UnsupportedOperationException() + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/FakeClientHelpers.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/FakeClientHelpers.kt new file mode 100644 index 000000000..4405f3e4b --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/FakeClientHelpers.kt @@ -0,0 +1,81 @@ +package org.thoughtcrime.securesms.testing + +import org.signal.libsignal.internal.Native +import org.signal.libsignal.internal.NativeHandleGuard +import org.signal.libsignal.metadata.certificate.CertificateValidator +import org.signal.libsignal.metadata.certificate.SenderCertificate +import org.signal.libsignal.metadata.certificate.ServerCertificate +import org.signal.libsignal.protocol.ecc.Curve +import org.signal.libsignal.protocol.ecc.ECKeyPair +import org.signal.libsignal.protocol.ecc.ECPublicKey +import org.signal.libsignal.zkgroup.profiles.ProfileKey +import org.whispersystems.signalservice.api.crypto.ContentHint +import org.whispersystems.signalservice.api.crypto.EnvelopeContent +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair +import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.internal.push.OutgoingPushMessage +import org.whispersystems.signalservice.internal.push.SignalServiceProtos +import org.whispersystems.util.Base64 +import java.util.Optional +import java.util.UUID + +object FakeClientHelpers { + + val noOpCertificateValidator = object : CertificateValidator(null) { + override fun validate(certificate: SenderCertificate, validationTime: Long) = Unit + } + + fun createCertificateFor(trustRoot: ECKeyPair, uuid: UUID, e164: String, deviceId: Int, identityKey: ECPublicKey, expires: Long): SenderCertificate { + val serverKey: ECKeyPair = Curve.generateKeyPair() + NativeHandleGuard(serverKey.publicKey).use { serverPublicGuard -> + NativeHandleGuard(trustRoot.privateKey).use { trustRootPrivateGuard -> + val serverCertificate = ServerCertificate(Native.ServerCertificate_New(1, serverPublicGuard.nativeHandle(), trustRootPrivateGuard.nativeHandle())) + NativeHandleGuard(identityKey).use { identityGuard -> + NativeHandleGuard(serverCertificate).use { serverCertificateGuard -> + NativeHandleGuard(serverKey.privateKey).use { serverPrivateGuard -> + return SenderCertificate(Native.SenderCertificate_New(uuid.toString(), e164, deviceId, identityGuard.nativeHandle(), expires, serverCertificateGuard.nativeHandle(), serverPrivateGuard.nativeHandle())) + } + } + } + } + } + } + + fun getTargetUnidentifiedAccess(myProfileKey: ProfileKey, theirProfileKey: ProfileKey, senderCertificate: SenderCertificate): Optional { + val selfUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(myProfileKey) + val themUnidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey) + + return UnidentifiedAccessPair(UnidentifiedAccess(selfUnidentifiedAccessKey, senderCertificate.serialized), UnidentifiedAccess(themUnidentifiedAccessKey, senderCertificate.serialized)).targetUnidentifiedAccess + } + + fun encryptedTextMessage(now: Long, message: String = "Test body message"): EnvelopeContent { + val content = SignalServiceProtos.Content.newBuilder().apply { + setDataMessage( + SignalServiceProtos.DataMessage.newBuilder().apply { + body = message + timestamp = now + } + ) + } + return EnvelopeContent.encrypted(content.build(), ContentHint.RESENDABLE, Optional.empty()) + } + + fun OutgoingPushMessage.toSignalServiceEnvelope(timestamp: Long, destination: ServiceId): SignalServiceEnvelope { + return SignalServiceEnvelope( + this.type, + Optional.empty(), + 1, + timestamp, + Base64.decode(this.content), + timestamp + 1, + timestamp + 2, + UUID.randomUUID().toString(), + destination.toString(), + true, + false, + null + ) + } +} diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt new file mode 100644 index 000000000..25a7ebd06 --- /dev/null +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/InMemoryLogger.kt @@ -0,0 +1,86 @@ +package org.thoughtcrime.securesms.testing + +import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.logging.Log +import java.util.concurrent.CountDownLatch + +typealias LogPredicate = (Entry) -> Boolean + +/** + * Logging implementation that holds logs in memory as they are added to be retrieve at a later time by a test. + * Can also be used for multithreaded synchronization and waiting until certain logs are emitted before continuing + * a test. + */ +class InMemoryLogger : Log.Logger() { + + private val executor = SignalExecutors.newCachedSingleThreadExecutor("inmemory-logger") + private val predicates = mutableListOf() + private val logEntries = mutableListOf() + + override fun v(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Verbose(tag, message, t, System.currentTimeMillis())) + override fun d(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Debug(tag, message, t, System.currentTimeMillis())) + override fun i(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Info(tag, message, t, System.currentTimeMillis())) + override fun w(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Warn(tag, message, t, System.currentTimeMillis())) + override fun e(tag: String, message: String?, t: Throwable?, keepLonger: Boolean) = add(Error(tag, message, t, System.currentTimeMillis())) + + override fun flush() { + val latch = CountDownLatch(1) + executor.execute { latch.countDown() } + latch.await() + } + + private fun add(entry: Entry) { + executor.execute { + logEntries += entry + + val iterator = predicates.iterator() + while (iterator.hasNext()) { + val predicate = iterator.next() + if (predicate(entry)) { + iterator.remove() + } + } + } + } + + /** Blocks until a snapshot of all log entries can be taken in a thread-safe way. */ + fun entries(): List { + val latch = CountDownLatch(1) + var entries: List = emptyList() + executor.execute { + entries = logEntries.toList() + latch.countDown() + } + latch.await() + return entries + } + + /** Returns a countdown latch that'll fire at a future point when an [Entry] is received that matches the predicate. */ + fun getLockForUntil(predicate: LogPredicate): CountDownLatch { + val latch = CountDownLatch(1) + executor.execute { + predicates += { entry -> + if (predicate(entry)) { + latch.countDown() + true + } else { + false + } + } + } + return latch + } +} + +sealed interface Entry { + val tag: String + val message: String? + val throwable: Throwable? + val timestamp: Long +} + +data class Verbose(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry +data class Debug(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry +data class Info(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry +data class Warn(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry +data class Error(override val tag: String, override val message: String?, override val throwable: Throwable?, override val timestamp: Long) : Entry diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt index 558b03e5d..6e32dfaf1 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/SignalActivityRule.kt @@ -11,7 +11,9 @@ import androidx.test.platform.app.InstrumentationRegistry import okhttp3.mockwebserver.MockResponse import org.junit.rules.ExternalResource import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.IdentityKeyPair import org.signal.libsignal.protocol.SignalProtocolAddress +import org.thoughtcrime.securesms.SignalInstrumentationApplicationContext import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.MasterSecretUtil import org.thoughtcrime.securesms.crypto.ProfileKeyUtil @@ -54,11 +56,18 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource() private set lateinit var others: List private set + lateinit var othersKeys: List + + val inMemoryLogger: InMemoryLogger + get() = (application as SignalInstrumentationApplicationContext).inMemoryLogger override fun before() { context = InstrumentationRegistry.getInstrumentation().targetContext self = setupSelf() - others = setupOthers() + + val setupOthers = setupOthers() + others = setupOthers.first + othersKeys = setupOthers.second InstrumentationApplicationDependencyProvider.clearHandlers() } @@ -99,8 +108,9 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource() return Recipient.self() } - private fun setupOthers(): List { + private fun setupOthers(): Pair, List> { val others = mutableListOf() + val othersKeys = mutableListOf() if (othersCount !in 0 until 1000) { throw IllegalArgumentException("$othersCount must be between 0 and 1000") @@ -114,11 +124,13 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource() SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true, true, true, true, true, true, true)) SignalDatabase.recipients.setProfileSharing(recipientId, true) SignalDatabase.recipients.markRegistered(recipientId, aci) - ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), IdentityKeyUtil.generateIdentityKeyPair().publicKey) + val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair() + ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), otherIdentity.publicKey) others += recipientId + othersKeys += otherIdentity } - return others + return others to othersKeys } inline fun launchActivity(initIntent: Intent.() -> Unit = {}): ActivityScenario { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/TestUtils.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/TestUtils.kt index 0604ec82a..4b787f09e 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/TestUtils.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/TestUtils.kt @@ -7,6 +7,9 @@ import org.hamcrest.Matchers.not import org.hamcrest.Matchers.notNullValue import org.hamcrest.Matchers.nullValue import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException +import kotlin.time.Duration /** * Run the given [runnable] on a new thread and wait for it to finish. @@ -44,3 +47,9 @@ infix fun T.assertIsNot(expected: T) { infix fun > T.assertIsSize(expected: Int) { assertThat(this, hasSize(expected)) } + +fun CountDownLatch.awaitFor(duration: Duration) { + if (!await(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS)) { + throw TimeoutException("Latch await took longer than ${duration.inWholeMilliseconds}ms") + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 7452f3629..650d35c91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -126,7 +126,8 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr private static final String TAG = Log.tag(ApplicationContext.class); - private PersistentLogger persistentLogger; + @VisibleForTesting + protected PersistentLogger persistentLogger; public static ApplicationContext getInstance(Context context) { return (ApplicationContext)context.getApplicationContext(); @@ -299,7 +300,8 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr } } - private void initializeLogging() { + @VisibleForTesting + protected void initializeLogging() { persistentLogger = new PersistentLogger(this); org.signal.core.util.logging.Log.initialize(FeatureFlags::internalUser, new AndroidLogger(), persistentLogger); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index 5c4cbc99f..448770061 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -8,6 +8,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; @@ -198,7 +199,7 @@ import java.util.concurrent.TimeUnit; * data to our data stores. */ @SuppressWarnings({ "OptionalGetWithoutIsPresent", "OptionalIsPresent" }) -public final class MessageContentProcessor { +public class MessageContentProcessor { private static final String TAG = Log.tag(MessageContentProcessor.class); @@ -208,7 +209,8 @@ public final class MessageContentProcessor { return new MessageContentProcessor(context); } - private MessageContentProcessor(@NonNull Context context) { + @VisibleForTesting + MessageContentProcessor(@NonNull Context context) { this.context = context; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationUtil.java b/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationUtil.java index 975333e69..f905aafa8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/RegistrationUtil.java @@ -1,9 +1,5 @@ package org.thoughtcrime.securesms.registration; -import android.content.Context; - -import androidx.annotation.NonNull; - import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; diff --git a/dependencies.gradle b/dependencies.gradle index a7276873b..01926e970 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -169,6 +169,7 @@ dependencyResolutionManagement { alias('assertj-core').to('org.assertj:assertj-core:3.11.1') alias('square-okhttp-mockserver').to('com.squareup.okhttp3:mockwebserver:3.12.13') alias('mockk').to('io.mockk:mockk:1.13.2') + alias('mockk-android').to('io.mockk:mockk-android:1.13.2') alias('conscrypt-openjdk-uber').to('org.conscrypt:conscrypt-openjdk-uber:2.0.0') } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b788c4190..7c5b8f383 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -20,14 +20,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -71,9 +63,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - @@ -102,14 +91,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -118,14 +99,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -389,21 +362,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -425,21 +388,10 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - @@ -497,11 +449,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -512,11 +459,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -527,14 +469,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -563,14 +497,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -579,14 +505,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -621,14 +539,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -693,11 +603,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -737,14 +642,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -769,14 +666,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -815,21 +704,10 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - @@ -838,14 +716,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -854,24 +724,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - @@ -880,14 +737,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -896,14 +745,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -930,21 +771,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -1056,14 +887,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1072,14 +895,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1096,14 +911,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1131,9 +938,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - @@ -1310,11 +1114,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1325,11 +1124,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1343,11 +1137,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1358,11 +1147,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1373,11 +1157,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1498,11 +1277,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1558,17 +1332,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - @@ -1580,11 +1343,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1593,14 +1351,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1617,14 +1367,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1641,11 +1383,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1656,11 +1393,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1671,14 +1403,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1719,14 +1443,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1748,19 +1464,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - @@ -1777,14 +1480,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -1846,11 +1541,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1859,11 +1549,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1887,11 +1572,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -1972,11 +1652,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -2002,11 +1677,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -2017,14 +1687,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2033,14 +1695,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2051,14 +1705,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2067,14 +1713,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2093,14 +1731,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2111,14 +1741,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2127,14 +1749,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2145,14 +1759,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2161,27 +1767,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - @@ -2190,14 +1780,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2206,14 +1788,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2224,14 +1798,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2240,14 +1806,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -2263,11 +1821,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -2276,21 +1829,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -2299,16 +1842,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -2317,16 +1850,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -2335,26 +1858,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - @@ -2363,11 +1866,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -2393,21 +1891,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -2423,31 +1911,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -2525,61 +1998,26 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2590,36 +2028,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - @@ -2630,49 +2048,21 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2681,11 +2071,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -2776,41 +2161,21 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - @@ -2836,31 +2201,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -2951,16 +2301,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -2985,76 +2325,36 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3070,16 +2370,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -3090,11 +2380,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -3120,6 +2405,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -3353,9 +2646,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - @@ -3438,11 +2728,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -3458,19 +2743,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - @@ -3573,36 +2845,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - @@ -3685,11 +2937,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -3711,131 +2958,66 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3857,6 +3039,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -3881,6 +3071,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + + + + @@ -3921,116 +3119,56 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4065,11 +3203,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4080,11 +3213,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4095,26 +3223,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -4140,9 +3253,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + @@ -4155,9 +3268,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - + + + @@ -4185,41 +3298,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4240,21 +3323,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -4343,126 +3416,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4478,11 +3436,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4493,31 +3446,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -4556,61 +3494,21 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -4626,27 +3524,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - @@ -4667,16 +3544,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -4692,11 +3559,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4707,16 +3569,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -4737,11 +3589,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4752,11 +3599,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4767,16 +3609,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -4797,11 +3629,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4815,14 +3642,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -4839,17 +3658,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - @@ -4858,24 +3666,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - @@ -4889,14 +3684,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -4907,11 +3694,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4922,11 +3704,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -4937,26 +3714,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -4972,51 +3734,21 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5027,16 +3759,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + - - - - - @@ -5057,11 +3789,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5072,11 +3799,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5087,11 +3809,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5102,11 +3819,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5117,46 +3829,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5167,11 +3849,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5187,41 +3864,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5237,11 +3889,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5257,26 +3904,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -5292,11 +3924,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5312,26 +3939,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -5347,11 +3959,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5367,26 +3974,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -5402,11 +3994,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5417,16 +4004,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -5443,22 +4020,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - - @@ -5480,11 +4041,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5511,14 +4067,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5527,14 +4075,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5559,14 +4099,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5606,11 +4138,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5624,24 +4151,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - + @@ -5657,14 +4174,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5673,14 +4182,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5697,14 +4198,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5713,14 +4206,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - @@ -5729,19 +4214,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - @@ -5752,11 +4224,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5817,16 +4284,16 @@ https://docs.gradle.org/current/userguide/dependency_verification.html + + + + + - - - - - @@ -5837,11 +4304,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -5852,26 +4314,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - - - - - - @@ -5882,16 +4329,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - - - @@ -6011,11 +4448,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -6045,11 +4477,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - @@ -6080,14 +4507,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - - - - + + + @@ -6100,11 +4522,6 @@ https://docs.gradle.org/current/userguide/dependency_verification.html - - - - - diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 5a08dd41f..6868b07c7 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -2209,7 +2209,8 @@ public class SignalServiceMessageSender { return new OutgoingPushMessageList(recipient.getIdentifier(), timestamp, messages, online, urgent); } - private OutgoingPushMessage getEncryptedMessage(SignalServiceAddress recipient, + // Visible for testing only + public OutgoingPushMessage getEncryptedMessage(SignalServiceAddress recipient, Optional unidentifiedAccess, int deviceId, EnvelopeContent plaintext, @@ -2248,7 +2249,6 @@ public class SignalServiceMessageSender { } } - private List getPreKeys(SignalServiceAddress recipient, Optional unidentifiedAccess, int deviceId, boolean story) throws IOException { try { return socket.getPreKeys(recipient, unidentifiedAccess, deviceId); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java index ed34b0454..618d2ca13 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/internal/push/OutgoingPushMessage.java @@ -12,13 +12,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; public class OutgoingPushMessage { @JsonProperty - private int type; + public int type; @JsonProperty - private int destinationDeviceId; + public int destinationDeviceId; @JsonProperty - private int destinationRegistrationId; + public int destinationRegistrationId; @JsonProperty - private String content; + public String content; public OutgoingPushMessage() {}