Add benchmark for conversation open.
This commit is contained in:
parent
f3b830ae20
commit
97b349b0de
9 changed files with 429 additions and 219 deletions
|
@ -1,214 +1,66 @@
|
|||
package org.signal.benchmark
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import org.signal.core.util.ThreadUtil
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress
|
||||
import android.widget.TextView
|
||||
import org.signal.benchmark.setup.TestMessages
|
||||
import org.signal.benchmark.setup.TestUsers
|
||||
import org.thoughtcrime.securesms.BaseActivity
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory
|
||||
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.registration.RegistrationData
|
||||
import org.thoughtcrime.securesms.registration.RegistrationRepository
|
||||
import org.thoughtcrime.securesms.registration.RegistrationUtil
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponse
|
||||
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
|
||||
import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
import java.util.*
|
||||
|
||||
class BenchmarkSetupActivity : BaseActivity() {
|
||||
|
||||
private val othersCount: Int = 50
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setupSelf()
|
||||
setupOthers()
|
||||
}
|
||||
|
||||
@SuppressLint("VisibleForTests")
|
||||
private fun setupSelf(): Recipient {
|
||||
DeviceTransferBlockingInterceptor.getInstance().blockNetwork()
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(application).edit().putBoolean("pref_prompted_push_registration", true).commit()
|
||||
val masterSecret = MasterSecretUtil.generateMasterSecret(application, MasterSecretUtil.UNENCRYPTED_PASSPHRASE)
|
||||
MasterSecretUtil.generateAsymmetricMasterSecret(application, masterSecret)
|
||||
val preferences: SharedPreferences = application.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0)
|
||||
preferences.edit().putBoolean("passphrase_initialized", true).commit()
|
||||
|
||||
val registrationRepository = RegistrationRepository(application)
|
||||
val registrationData = RegistrationData(
|
||||
code = "123123",
|
||||
e164 = "+15555550101",
|
||||
password = Util.getSecret(18),
|
||||
registrationId = registrationRepository.registrationId,
|
||||
profileKey = registrationRepository.getProfileKey("+15555550101"),
|
||||
fcmToken = "fcm-token",
|
||||
pniRegistrationId = registrationRepository.pniRegistrationId,
|
||||
recoveryPassword = "asdfasdfasdfasdf"
|
||||
)
|
||||
val verifyResponse = VerifyResponse(VerifyAccountResponse(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false), null, null)
|
||||
AccountManagerFactory.setInstance(DummyAccountManagerFactory())
|
||||
val response: ServiceResponse<VerifyResponse> = registrationRepository.registerAccount(
|
||||
registrationData,
|
||||
verifyResponse,
|
||||
false
|
||||
).blockingGet()
|
||||
ServiceResponseProcessor.DefaultProcessor(response).resultOrThrow
|
||||
|
||||
SignalStore.kbsValues().optOut()
|
||||
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||
SignalDatabase.recipients.setProfileName(Recipient.self().id, ProfileName.fromParts("Tester", "McTesterson"))
|
||||
|
||||
return Recipient.self()
|
||||
}
|
||||
|
||||
private fun setupOthers(): Pair<List<RecipientId>, List<IdentityKeyPair>> {
|
||||
val others = mutableListOf<RecipientId>()
|
||||
val othersKeys = mutableListOf<IdentityKeyPair>()
|
||||
|
||||
if (othersCount !in 0 until 1000) {
|
||||
throw IllegalArgumentException("$othersCount must be between 0 and 1000")
|
||||
when (intent.extras!!.getString("setup-type")) {
|
||||
"cold-start" -> setupColdStart()
|
||||
"conversation-open" -> setupConversationOpen()
|
||||
}
|
||||
|
||||
for (i in 0 until othersCount) {
|
||||
val aci = ACI.from(UUID.randomUUID())
|
||||
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||
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)
|
||||
val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), otherIdentity.publicKey)
|
||||
val textView: TextView = TextView(this).apply {
|
||||
text = "done"
|
||||
}
|
||||
setContentView(textView)
|
||||
}
|
||||
|
||||
val recipient: Recipient = Recipient.resolved(recipientId)
|
||||
private fun setupColdStart() {
|
||||
TestUsers.setupSelf()
|
||||
TestUsers.setupTestRecipients(50).forEach {
|
||||
val recipient: Recipient = Recipient.resolved(it)
|
||||
|
||||
insertMediaMessage(other = recipient, body = "Cool text message?!?!", attachmentCount = 0)
|
||||
insertFailedMediaMessage(other = recipient, attachmentCount = 1)
|
||||
insertFailedMediaMessage(other = recipient, attachmentCount = 2)
|
||||
insertFailedMediaMessage(other = recipient, body = "Test", attachmentCount = 1)
|
||||
TestMessages.insertIncomingTextMessage(other = recipient, body = "Cool text message?!?!")
|
||||
TestMessages.insertIncomingImageMessage(other = recipient, attachmentCount = 1)
|
||||
TestMessages.insertIncomingImageMessage(other = recipient, attachmentCount = 2, body = "Album")
|
||||
TestMessages.insertIncomingImageMessage(other = recipient, body = "Test", attachmentCount = 1, failed = true)
|
||||
|
||||
SignalDatabase.messages.setAllMessagesRead()
|
||||
|
||||
SignalDatabase.threads.update(SignalDatabase.threads.getOrCreateThreadIdFor(recipient = recipient), true)
|
||||
|
||||
others += recipientId
|
||||
othersKeys += otherIdentity
|
||||
}
|
||||
|
||||
SignalDatabase.messages.setAllMessagesRead()
|
||||
|
||||
return others to othersKeys
|
||||
}
|
||||
|
||||
private fun insertMediaMessage(other: Recipient, body: String? = null, attachmentCount: Int = 1) {
|
||||
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
|
||||
attachment()
|
||||
private fun setupConversationOpen() {
|
||||
TestUsers.setupSelf()
|
||||
TestUsers.setupTestRecipient().let {
|
||||
val recipient: Recipient = Recipient.resolved(it)
|
||||
val messagesToAdd = 1000
|
||||
val generator: TestMessages.TimestampGenerator = TestMessages.TimestampGenerator(System.currentTimeMillis() - (messagesToAdd * 2000L) - 60_000L)
|
||||
|
||||
for (i in 0 until messagesToAdd) {
|
||||
TestMessages.insertIncomingTextMessage(other = recipient, body = "Test message $i", timestamp = generator.nextTimestamp())
|
||||
TestMessages.insertOutgoingTextMessage(other = recipient, body = "Test message $i", timestamp = generator.nextTimestamp())
|
||||
}
|
||||
|
||||
val voiceMessageId = TestMessages.insertIncomingVoiceMessage(other = recipient, timestamp = generator.nextTimestamp())
|
||||
val mmsRecord = SignalDatabase.messages.getMessageRecord(voiceMessageId) as MediaMmsMessageRecord
|
||||
TestMessages.insertOutgoingImageMessage(other = recipient, body = "test", 2, generator.nextTimestamp())
|
||||
TestMessages.insertIncomingTextMessage(other = recipient, "reply to the test message", generator.nextTimestamp())
|
||||
TestMessages.insertIncomingQuoteTextMessage(other = recipient, quote = QuoteModel(mmsRecord.timestamp, recipient.id, "Fake voice message text", false, mmsRecord.slideDeck.asAttachments(), null, QuoteModel.Type.NORMAL, null), body = "Here is a cool quote", timestamp = generator.nextTimestamp())
|
||||
TestMessages.insertOutgoingTextMessage(other = recipient, body = "longaweorijoaijwerijoiajwer", timestamp = generator.nextTimestamp())
|
||||
|
||||
SignalDatabase.threads.update(SignalDatabase.threads.getOrCreateThreadIdFor(recipient = recipient), true)
|
||||
}
|
||||
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
body = body,
|
||||
sentTimeMillis = System.currentTimeMillis(),
|
||||
serverTimeMillis = System.currentTimeMillis(),
|
||||
receivedTimeMillis = System.currentTimeMillis(),
|
||||
attachments = PointerAttachment.forPointers(Optional.of(attachments))
|
||||
)
|
||||
|
||||
SignalDatabase.messages.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
|
||||
|
||||
ThreadUtil.sleep(1)
|
||||
}
|
||||
|
||||
private fun insertFailedMediaMessage(other: Recipient, body: String? = null, attachmentCount: Int = 1) {
|
||||
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
|
||||
attachment()
|
||||
}
|
||||
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
body = body,
|
||||
sentTimeMillis = System.currentTimeMillis(),
|
||||
serverTimeMillis = System.currentTimeMillis(),
|
||||
receivedTimeMillis = System.currentTimeMillis(),
|
||||
attachments = PointerAttachment.forPointers(Optional.of(attachments))
|
||||
)
|
||||
|
||||
val insert = SignalDatabase.messages.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get()
|
||||
|
||||
SignalDatabase.attachments.getAttachmentsForMessage(insert.messageId).forEachIndexed { index, attachment ->
|
||||
SignalDatabase.attachments.setTransferProgressPermanentFailure(attachment.attachmentId, insert.messageId)
|
||||
}
|
||||
|
||||
ThreadUtil.sleep(1)
|
||||
}
|
||||
|
||||
private fun insertFailedOutgoingMediaMessage(other: Recipient, body: String? = null, attachmentCount: Int = 1) {
|
||||
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
|
||||
attachment()
|
||||
}
|
||||
|
||||
val message = OutgoingMessage(
|
||||
recipient = other,
|
||||
body = body,
|
||||
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
isSecure = true
|
||||
)
|
||||
|
||||
val insert = SignalDatabase.messages.insertMessageOutbox(
|
||||
message,
|
||||
SignalDatabase.threads.getOrCreateThreadIdFor(other),
|
||||
false,
|
||||
null
|
||||
)
|
||||
|
||||
SignalDatabase.attachments.getAttachmentsForMessage(insert).forEachIndexed { index, attachment ->
|
||||
SignalDatabase.attachments.setTransferProgressPermanentFailure(attachment.attachmentId, insert)
|
||||
}
|
||||
|
||||
ThreadUtil.sleep(1)
|
||||
}
|
||||
|
||||
private fun attachment(): SignalServiceAttachmentPointer {
|
||||
return SignalServiceAttachmentPointer(
|
||||
ReleaseChannel.CDN_NUMBER,
|
||||
SignalServiceAttachmentRemoteId.from(""),
|
||||
"image/webp",
|
||||
null,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
1024,
|
||||
1024,
|
||||
Optional.empty(),
|
||||
Optional.of("/not-there.jpg"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
package org.signal.benchmark.setup
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.TestDbUtils
|
||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMessage
|
||||
import org.thoughtcrime.securesms.mms.QuoteModel
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.releasechannel.ReleaseChannel
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import java.util.Collections
|
||||
import java.util.Optional
|
||||
|
||||
object TestMessages {
|
||||
fun insertOutgoingTextMessage(other: Recipient, body: String, timestamp: Long = System.currentTimeMillis()) {
|
||||
insertOutgoingMessage(
|
||||
recipient = other,
|
||||
message = OutgoingMessage(
|
||||
recipient = other,
|
||||
body = body,
|
||||
timestamp = timestamp,
|
||||
isSecure = true
|
||||
),
|
||||
timestamp = timestamp
|
||||
)
|
||||
}
|
||||
|
||||
fun insertOutgoingImageMessage(other: Recipient, body: String? = null, attachmentCount: Int, timestamp: Long = System.currentTimeMillis()): Long {
|
||||
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
|
||||
imageAttachment()
|
||||
}
|
||||
val message = OutgoingMessage(
|
||||
recipient = other,
|
||||
body = body,
|
||||
attachments = PointerAttachment.forPointers(Optional.of(attachments)),
|
||||
timestamp = timestamp,
|
||||
isSecure = true
|
||||
)
|
||||
return insertOutgoingMediaMessage(recipient = other, message = message, timestamp = timestamp)
|
||||
}
|
||||
|
||||
private fun insertOutgoingMediaMessage(recipient: Recipient, message: OutgoingMessage, timestamp: Long): Long {
|
||||
val insert = insertOutgoingMessage(recipient, message = message, timestamp = timestamp)
|
||||
setMessageMediaTransfered(insert)
|
||||
|
||||
return insert
|
||||
}
|
||||
|
||||
private fun insertOutgoingMessage(recipient: Recipient, message: OutgoingMessage, timestamp: Long? = null): Long {
|
||||
val insert = SignalDatabase.messages.insertMessageOutbox(
|
||||
message,
|
||||
SignalDatabase.threads.getOrCreateThreadIdFor(recipient),
|
||||
false,
|
||||
null
|
||||
)
|
||||
if (timestamp != null) {
|
||||
TestDbUtils.setMessageReceived(insert, timestamp)
|
||||
}
|
||||
SignalDatabase.messages.markAsSent(insert, true)
|
||||
|
||||
return insert
|
||||
}
|
||||
fun insertIncomingTextMessage(other: Recipient, body: String, timestamp: Long? = null) {
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
body = body,
|
||||
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
receivedTimeMillis = timestamp ?: System.currentTimeMillis()
|
||||
)
|
||||
|
||||
SignalDatabase.messages.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(other)).get().messageId
|
||||
}
|
||||
fun insertIncomingQuoteTextMessage(other: Recipient, body: String, quote: QuoteModel, timestamp: Long?) {
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
body = body,
|
||||
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
quote = quote
|
||||
)
|
||||
insertIncomingMessage(other, message = message)
|
||||
}
|
||||
fun insertIncomingImageMessage(other: Recipient, body: String? = null, attachmentCount: Int, timestamp: Long? = null, failed: Boolean = false): Long {
|
||||
val attachments: List<SignalServiceAttachmentPointer> = (0 until attachmentCount).map {
|
||||
imageAttachment()
|
||||
}
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
attachments = PointerAttachment.forPointers(Optional.of(attachments))
|
||||
)
|
||||
return insertIncomingMediaMessage(recipient = other, message = message, failed = failed)
|
||||
}
|
||||
|
||||
fun insertIncomingVoiceMessage(other: Recipient, timestamp: Long? = null): Long {
|
||||
val message = IncomingMediaMessage(
|
||||
from = other.id,
|
||||
sentTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
serverTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
receivedTimeMillis = timestamp ?: System.currentTimeMillis(),
|
||||
attachments = PointerAttachment.forPointers(Optional.of(Collections.singletonList(voiceAttachment()) as List<SignalServiceAttachment>))
|
||||
)
|
||||
return insertIncomingMediaMessage(recipient = other, message = message, failed = false)
|
||||
}
|
||||
|
||||
private fun insertIncomingMediaMessage(recipient: Recipient, message: IncomingMediaMessage, failed: Boolean = false): Long {
|
||||
val id = insertIncomingMessage(recipient = recipient, message = message)
|
||||
if (failed) {
|
||||
setMessageMediaFailed(id)
|
||||
} else {
|
||||
setMessageMediaTransfered(id)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
private fun insertIncomingMessage(recipient: Recipient, message: IncomingMediaMessage): Long {
|
||||
return SignalDatabase.messages.insertSecureDecryptedMessageInbox(message, SignalDatabase.threads.getOrCreateThreadIdFor(recipient)).get().messageId
|
||||
}
|
||||
|
||||
private fun setMessageMediaFailed(messageId: Long) {
|
||||
SignalDatabase.attachments.getAttachmentsForMessage(messageId).forEachIndexed { index, attachment ->
|
||||
SignalDatabase.attachments.setTransferProgressPermanentFailure(attachment.attachmentId, messageId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMessageMediaTransfered(messageId: Long) {
|
||||
SignalDatabase.attachments.getAttachmentsForMessage(messageId).forEachIndexed { _, attachment ->
|
||||
SignalDatabase.attachments.setTransferState(messageId, attachment.attachmentId, AttachmentTable.TRANSFER_PROGRESS_DONE)
|
||||
}
|
||||
}
|
||||
private fun imageAttachment(): SignalServiceAttachmentPointer {
|
||||
return SignalServiceAttachmentPointer(
|
||||
ReleaseChannel.CDN_NUMBER,
|
||||
SignalServiceAttachmentRemoteId.from(""),
|
||||
"image/webp",
|
||||
null,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
1024,
|
||||
1024,
|
||||
Optional.empty(),
|
||||
Optional.of("/not-there.jpg"),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
|
||||
private fun voiceAttachment(): SignalServiceAttachmentPointer {
|
||||
return SignalServiceAttachmentPointer(
|
||||
ReleaseChannel.CDN_NUMBER,
|
||||
SignalServiceAttachmentRemoteId.from(""),
|
||||
"audio/aac",
|
||||
null,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
1024,
|
||||
1024,
|
||||
Optional.empty(),
|
||||
Optional.of("/not-there.aac"),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
|
||||
class TimestampGenerator(private var start: Long = System.currentTimeMillis()) {
|
||||
fun nextTimestamp(): Long {
|
||||
start += 500L
|
||||
|
||||
return start
|
||||
}
|
||||
}
|
||||
}
|
103
app/src/benchmark/java/org/signal/benchmark/setup/TestUsers.kt
Normal file
103
app/src/benchmark/java/org/signal/benchmark/setup/TestUsers.kt
Normal file
|
@ -0,0 +1,103 @@
|
|||
package org.signal.benchmark.setup
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import org.signal.benchmark.DummyAccountManagerFactory
|
||||
import org.signal.libsignal.protocol.SignalProtocolAddress
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.registration.RegistrationData
|
||||
import org.thoughtcrime.securesms.registration.RegistrationRepository
|
||||
import org.thoughtcrime.securesms.registration.RegistrationUtil
|
||||
import org.thoughtcrime.securesms.registration.VerifyResponse
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile
|
||||
import org.whispersystems.signalservice.api.push.ACI
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse
|
||||
import java.util.UUID
|
||||
|
||||
object TestUsers {
|
||||
|
||||
private var generatedOthers: Int = 0
|
||||
|
||||
fun setupSelf(): Recipient {
|
||||
val application: Application = ApplicationDependencies.getApplication()
|
||||
DeviceTransferBlockingInterceptor.getInstance().blockNetwork()
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(application).edit().putBoolean("pref_prompted_push_registration", true).commit()
|
||||
val masterSecret = MasterSecretUtil.generateMasterSecret(application, MasterSecretUtil.UNENCRYPTED_PASSPHRASE)
|
||||
MasterSecretUtil.generateAsymmetricMasterSecret(application, masterSecret)
|
||||
val preferences: SharedPreferences = application.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0)
|
||||
preferences.edit().putBoolean("passphrase_initialized", true).commit()
|
||||
|
||||
val registrationRepository = RegistrationRepository(application)
|
||||
val registrationData = RegistrationData(
|
||||
code = "123123",
|
||||
e164 = "+15555550101",
|
||||
password = Util.getSecret(18),
|
||||
registrationId = registrationRepository.registrationId,
|
||||
profileKey = registrationRepository.getProfileKey("+15555550101"),
|
||||
fcmToken = "fcm-token",
|
||||
pniRegistrationId = registrationRepository.pniRegistrationId,
|
||||
recoveryPassword = "asdfasdfasdfasdf"
|
||||
)
|
||||
val verifyResponse = VerifyResponse(VerifyAccountResponse(UUID.randomUUID().toString(), UUID.randomUUID().toString(), false), null, null)
|
||||
AccountManagerFactory.setInstance(DummyAccountManagerFactory())
|
||||
val response: ServiceResponse<VerifyResponse> = registrationRepository.registerAccount(
|
||||
registrationData,
|
||||
verifyResponse,
|
||||
false
|
||||
).blockingGet()
|
||||
ServiceResponseProcessor.DefaultProcessor(response).resultOrThrow
|
||||
|
||||
SignalStore.kbsValues().optOut()
|
||||
RegistrationUtil.maybeMarkRegistrationComplete()
|
||||
SignalDatabase.recipients.setProfileName(Recipient.self().id, ProfileName.fromParts("Tester", "McTesterson"))
|
||||
|
||||
return Recipient.self()
|
||||
}
|
||||
|
||||
fun setupTestRecipient(): RecipientId {
|
||||
return setupTestRecipients(1).first()
|
||||
}
|
||||
|
||||
fun setupTestRecipients(othersCount: Int): List<RecipientId> {
|
||||
val others = mutableListOf<RecipientId>()
|
||||
synchronized(this) {
|
||||
if (generatedOthers + othersCount !in 0 until 1000) {
|
||||
throw IllegalArgumentException("$othersCount must be between 0 and 1000")
|
||||
}
|
||||
|
||||
for (i in generatedOthers until generatedOthers + othersCount) {
|
||||
val aci = ACI.from(UUID.randomUUID())
|
||||
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||
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)
|
||||
val otherIdentity = IdentityKeyUtil.generateIdentityKeyPair()
|
||||
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), otherIdentity.publicKey)
|
||||
|
||||
others += recipientId
|
||||
}
|
||||
|
||||
generatedOthers += othersCount
|
||||
}
|
||||
|
||||
return others
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import org.signal.core.util.SqlUtil.buildArgs
|
||||
|
||||
object TestDbUtils {
|
||||
|
||||
fun setMessageReceived(messageId: Long, timestamp: Long) {
|
||||
val database: SQLiteDatabase = SignalDatabase.messages.databaseHelper.signalWritableDatabase
|
||||
val contentValues = ContentValues()
|
||||
contentValues.put(MessageTable.DATE_RECEIVED, timestamp)
|
||||
val rowsUpdated = database.update(MessageTable.TABLE_NAME, contentValues, MessageTable.ID_WHERE, buildArgs(messageId))
|
||||
}
|
||||
}
|
|
@ -184,6 +184,7 @@ import org.thoughtcrime.securesms.util.RemoteDeleteUtil;
|
|||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
|
||||
import org.thoughtcrime.securesms.util.SignalProxyUtil;
|
||||
import org.thoughtcrime.securesms.util.SignalTrace;
|
||||
import org.thoughtcrime.securesms.util.SnapToTopDataObserver;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||
|
@ -290,6 +291,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
this.locale = Locale.getDefault();
|
||||
startupStopwatch = new Stopwatch("conversation-open");
|
||||
SignalLocalMetrics.ConversationOpen.start();
|
||||
SignalTrace.beginSection("ConversationOpen");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -747,6 +749,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
|||
startupStopwatch.stop(TAG);
|
||||
SignalLocalMetrics.ConversationOpen.onRenderFinished();
|
||||
listener.onFirstRender();
|
||||
SignalTrace.endSection();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -23,22 +23,20 @@ class BaselineProfileGenerator {
|
|||
val baselineProfileRule = BaselineProfileRule()
|
||||
|
||||
@Test
|
||||
fun startup() = baselineProfileRule.collectBaselineProfile(
|
||||
packageName = "org.thoughtcrime.securesms",
|
||||
profileBlock = {
|
||||
if (iteration == 0) {
|
||||
val setupIntent = Intent().apply {
|
||||
component = ComponentName("org.thoughtcrime.securesms", "org.signal.benchmark.BenchmarkSetupActivity")
|
||||
fun startup() {
|
||||
var setup = false
|
||||
baselineProfileRule.collectBaselineProfile(
|
||||
packageName = "org.thoughtcrime.securesms",
|
||||
profileBlock = {
|
||||
if (!setup) {
|
||||
BenchmarkSetup.setup("cold-start", device)
|
||||
setup = true
|
||||
}
|
||||
startActivityAndWait(setupIntent)
|
||||
startActivityAndWait()
|
||||
device.findObject(By.textContains("Buddy")).click();
|
||||
device.wait(Until.hasObject(By.textContains("Signal message")), 10_000L)
|
||||
device.wait(Until.hasObject(By.textContains("Test")), 5_000L)
|
||||
}
|
||||
startActivityAndWait()
|
||||
device.findObject(By.textContains("Buddy")).click();
|
||||
device.wait(
|
||||
Until.hasObject(By.clazz("$packageName.conversation.ConversationActivity")),
|
||||
10000L
|
||||
)
|
||||
device.wait(Until.hasObject(By.textContains("Test")), 10_000L)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.thoughtcrime.benchmark
|
||||
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import androidx.test.uiautomator.Until
|
||||
|
||||
object BenchmarkSetup {
|
||||
fun setup(type: String, device: UiDevice) {
|
||||
device.executeShellCommand("am start -W -n org.thoughtcrime.securesms/org.signal.benchmark.BenchmarkSetupActivity --es setup-type $type")
|
||||
device.wait(Until.hasObject(By.textContains("done")), 25_000L)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.thoughtcrime.benchmark
|
||||
|
||||
import androidx.benchmark.macro.CompilationMode
|
||||
import androidx.benchmark.macro.ExperimentalMetricApi
|
||||
import androidx.benchmark.macro.TraceSectionMetric
|
||||
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ConversationBenchmarks {
|
||||
@get:Rule
|
||||
val benchmarkRule = MacrobenchmarkRule()
|
||||
|
||||
@OptIn(ExperimentalMetricApi::class)
|
||||
@Test
|
||||
fun simpleConversationOpen() {
|
||||
var setup = false
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = "org.thoughtcrime.securesms",
|
||||
metrics = listOf(TraceSectionMetric("ConversationOpen")),
|
||||
iterations = 10,
|
||||
compilationMode = CompilationMode.Partial(),
|
||||
setupBlock = {
|
||||
if (!setup) {
|
||||
BenchmarkSetup.setup("conversation-open", device)
|
||||
setup = true
|
||||
}
|
||||
killProcess()
|
||||
startActivityAndWait()
|
||||
device.waitForIdle()
|
||||
}) {
|
||||
device.findObject(By.textContains("Buddy")).click();
|
||||
device.wait(Until.hasObject(By.textContains("Signal message")), 10_000L)
|
||||
device.wait(Until.hasObject(By.textContains("Test")), 5_000L)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ import androidx.benchmark.macro.StartupTimingMetric
|
|||
import androidx.benchmark.macro.TraceSectionMetric
|
||||
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -34,18 +36,9 @@ class StartupBenchmarks {
|
|||
measureStartup(5, CompilationMode.Partial())
|
||||
}
|
||||
|
||||
private val fakeDataSetupBlock: MacrobenchmarkScope.() -> Unit = {
|
||||
val setupIntent = Intent().apply {
|
||||
component = ComponentName("org.thoughtcrime.securesms", "org.signal.benchmark.BenchmarkSetupActivity")
|
||||
}
|
||||
startActivityAndWait(setupIntent)
|
||||
|
||||
killProcess()
|
||||
dropKernelPageCache()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMetricApi::class)
|
||||
private fun measureStartup(iterations: Int, compilationMode: CompilationMode) {
|
||||
var setup = false
|
||||
benchmarkRule.measureRepeated(
|
||||
packageName = "org.thoughtcrime.securesms",
|
||||
metrics = listOf(StartupTimingMetric(), TraceSectionMetric("ConversationListDataSource#load")),
|
||||
|
@ -53,8 +46,12 @@ class StartupBenchmarks {
|
|||
startupMode = StartupMode.COLD,
|
||||
compilationMode = compilationMode,
|
||||
setupBlock = {
|
||||
if (compilationMode !is CompilationMode.Partial || iteration == 0) {
|
||||
fakeDataSetupBlock()
|
||||
if (!setup) {
|
||||
BenchmarkSetup.setup("cold-start", device)
|
||||
|
||||
killProcess()
|
||||
dropKernelPageCache()
|
||||
setup = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
|
Loading…
Add table
Reference in a new issue