Add support for baseline profiles.

This commit is contained in:
Clark 2023-03-09 14:27:03 -05:00 committed by Greyson Parrelli
parent 79a062c838
commit 04baa7925f
20 changed files with 37283 additions and 22 deletions

View file

@ -68,6 +68,7 @@ def selectableVariants = [
'playProdDebug',
'playProdSpinner',
'playProdPerf',
'playProdBenchmark',
'playProdInstrumentation',
'playProdRelease',
'playStagingDebug',
@ -219,6 +220,7 @@ android {
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"unset\""
buildConfigField "String", "BADGE_STATIC_ROOT", "\"https://updates2.signal.org/static/badges/\""
buildConfigField "String", "STRIPE_PUBLISHABLE_KEY", "\"pk_live_6cmGZopuTsV8novGgJJW9JpC00vLIgtQ1D\""
buildConfigField "boolean", "TRACING_ENABLED", "false"
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
@ -228,7 +230,7 @@ android {
splits {
abi {
enable true
enable !project.hasProperty('generateBaselineProfile')
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
@ -304,6 +306,17 @@ android {
minifyEnabled true
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Perf\""
buildConfigField "boolean", "TRACING_ENABLED", "true"
}
benchmark {
initWith debug
isDefault false
debuggable false
minifyEnabled true
matchingFallbacks = ['debug']
buildConfigField "String", "BUILD_VARIANT_TYPE", "\"Benchmark\""
buildConfigField "boolean", "TRACING_ENABLED", "true"
}
}
@ -462,6 +475,7 @@ dependencies {
implementation libs.androidx.autofill
implementation libs.androidx.biometric
implementation libs.androidx.sharetarget
implementation libs.androidx.profileinstaller
implementation (libs.firebase.messaging) {
exclude group: 'com.google.firebase', module: 'firebase-core'

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<profileable android:shell="true" />
<activity android:name="org.signal.benchmark.BenchmarkSetupActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.Signal.DayNight.NoActionBar"
android:windowSoftInputMode="stateHidden"
android:exported="true"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
</application>
</manifest>

View file

@ -0,0 +1,214 @@
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 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.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")
}
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 recipient: Recipient = Recipient.resolved(recipientId)
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)
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()
}
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()
)
}
}

View file

@ -0,0 +1,43 @@
package org.signal.benchmark
import android.content.Context
import org.signal.libsignal.protocol.IdentityKey
import org.signal.libsignal.protocol.state.PreKeyRecord
import org.signal.libsignal.protocol.state.SignedPreKeyRecord
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.push.AccountManagerFactory
import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.push.ServiceIdType
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import java.io.IOException
import java.util.Optional
class DummyAccountManagerFactory : AccountManagerFactory() {
override fun createAuthenticated(context: Context, aci: ACI, pni: PNI, number: String, deviceId: Int, password: String): SignalServiceAccountManager {
return DummyAccountManager(
ApplicationDependencies.getSignalServiceNetworkAccess().getConfiguration(number),
aci,
pni,
number,
deviceId,
password,
BuildConfig.SIGNAL_AGENT,
FeatureFlags.okHttpAutomaticRetry(),
FeatureFlags.groupLimits().hardLimit
)
}
private class DummyAccountManager(configuration: SignalServiceConfiguration?, aci: ACI?, pni: PNI?, e164: String?, deviceId: Int, password: String?, signalAgent: String?, automaticNetworkRetry: Boolean, maxGroupSize: Int) : SignalServiceAccountManager(configuration, aci, pni, e164, deviceId, password, signalAgent, automaticNetworkRetry, maxGroupSize) {
@Throws(IOException::class)
override fun setGcmId(gcmRegistrationId: Optional<String>) {
}
@Throws(IOException::class)
override fun setPreKeys(serviceIdType: ServiceIdType, identityKey: IdentityKey, signedPreKey: SignedPreKeyRecord, oneTimePreKeys: List<PreKeyRecord>) {
}
}
}

36450
app/src/main/baseline-prof.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.database.model.UpdateDescription;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SignalTrace;
import java.util.ArrayList;
import java.util.Collections;
@ -66,6 +67,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
@Override
public @NonNull List<Conversation> load(int start, int length, @NonNull CancellationSignal cancellationSignal) {
SignalTrace.beginSection("ConversationListDataSource#load");
Stopwatch stopwatch = new Stopwatch("load(" + start + ", " + length + "), " + getClass().getSimpleName() + ", " + conversationFilter);
List<Conversation> conversations = new ArrayList<>(length);
@ -97,6 +99,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
stopwatch.split("recipient-resolve");
stopwatch.stop(TAG);
SignalTrace.endSection();
if (conversations.isEmpty() && start == 0 && length == 1) {
if (conversationFilter == ConversationFilter.OFF) {

View file

@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.push;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.google.android.gms.security.ProviderInstaller;
@ -17,14 +18,32 @@ import org.whispersystems.signalservice.api.push.PNI;
public class AccountManagerFactory {
private static AccountManagerFactory instance;
public static AccountManagerFactory getInstance() {
if (instance == null) {
synchronized (AccountManagerFactory.class) {
if (instance == null) {
instance = new AccountManagerFactory();
}
}
}
return instance;
}
@VisibleForTesting
public static void setInstance(@NonNull AccountManagerFactory accountManagerFactory) {
synchronized (AccountManagerFactory.class) {
instance = accountManagerFactory;
}
}
private static final String TAG = Log.tag(AccountManagerFactory.class);
public static @NonNull SignalServiceAccountManager createAuthenticated(@NonNull Context context,
@NonNull ACI aci,
@NonNull PNI pni,
@NonNull String e164,
int deviceId,
@NonNull String password)
public @NonNull SignalServiceAccountManager createAuthenticated(@NonNull Context context,
@NonNull ACI aci,
@NonNull PNI pni,
@NonNull String e164,
int deviceId,
@NonNull String password)
{
if (ApplicationDependencies.getSignalServiceNetworkAccess().isCensored(e164)) {
SignalExecutors.BOUNDED.execute(() -> {
@ -50,10 +69,10 @@ public class AccountManagerFactory {
/**
* Should only be used during registration when you haven't yet been assigned an ACI.
*/
public static @NonNull SignalServiceAccountManager createUnauthenticated(@NonNull Context context,
@NonNull String e164,
int deviceId,
@NonNull String password)
public @NonNull SignalServiceAccountManager createUnauthenticated(@NonNull Context context,
@NonNull String e164,
int deviceId,
@NonNull String password)
{
if (new SignalServiceNetworkAccess(context).isCensored(e164)) {
SignalExecutors.BOUNDED.execute(() -> {

View file

@ -142,7 +142,7 @@ public final class RegistrationRepository {
ApplicationDependencies.getProtocolStore().pni().sessions().archiveAllSessions();
SenderKeyUtil.clearAllState();
SignalServiceAccountManager accountManager = AccountManagerFactory.createAuthenticated(context, aci, pni, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createAuthenticated(context, aci, pni, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
SignalServiceAccountDataStoreImpl aciProtocolStore = ApplicationDependencies.getProtocolStore().aci();
SignalServiceAccountDataStoreImpl pniProtocolStore = ApplicationDependencies.getProtocolStore().pni();
@ -219,7 +219,7 @@ public final class RegistrationRepository {
}
public Single<BackupAuthCheckProcessor> getKbsAuthCredential(@NonNull RegistrationData registrationData, List<String> usernamePasswords) {
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(context, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, registrationData.getE164(), SignalServiceAddress.DEFAULT_DEVICE_ID, registrationData.getPassword());
return accountManager.checkBackupAuthCredentials(registrationData.getE164(), usernamePasswords)
.map(BackupAuthCheckProcessor::new)

View file

@ -42,7 +42,7 @@ class VerifyAccountRepository(private val context: Application) {
return if (sessionId.isNullOrBlank()) {
Single.just(ServiceResponse.forApplicationError(NoSuchSessionException(), 409, null))
} else {
val accountManager: SignalServiceAccountManager = AccountManagerFactory.createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val accountManager: SignalServiceAccountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
Single.fromCallable { accountManager.getRegistrationSession(sessionId) }.subscribeOn(Schedulers.io())
}
}
@ -55,7 +55,7 @@ class VerifyAccountRepository(private val context: Application) {
): Single<ServiceResponse<RegistrationSessionMetadataResponse>> {
return Single.fromCallable {
val fcmToken: String? = FcmUtil.getToken(context).orElse(null)
val accountManager: SignalServiceAccountManager = AccountManagerFactory.createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val accountManager: SignalServiceAccountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
if (fcmToken == null) {
return@fromCallable accountManager.createRegistrationSession(null, mcc, mnc)
} else {
@ -97,7 +97,7 @@ class VerifyAccountRepository(private val context: Application) {
password: String
): Single<ServiceResponse<RegistrationSessionMetadataResponse>> {
val fcmToken: Optional<String> = FcmUtil.getToken(context)
val accountManager = AccountManagerFactory.createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val accountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val pushChallenge = PushChallengeRequest.getPushChallengeBlocking(accountManager, sessionId, fcmToken, PUSH_REQUEST_TIMEOUT)
return Single.fromCallable {
return@fromCallable accountManager.submitPushChallengeToken(sessionId, pushChallenge.orElse(null))
@ -110,7 +110,7 @@ class VerifyAccountRepository(private val context: Application) {
e164: String,
password: String
): Single<ServiceResponse<RegistrationSessionMetadataResponse>> {
val accountManager = AccountManagerFactory.createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val accountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
return Single.fromCallable {
return@fromCallable accountManager.submitCaptchaToken(sessionId, captcha)
}.subscribeOn(Schedulers.io())
@ -125,7 +125,7 @@ class VerifyAccountRepository(private val context: Application) {
Log.d(TAG, "SMS Verification requested")
return Single.fromCallable {
val accountManager = AccountManagerFactory.createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
val accountManager = AccountManagerFactory.getInstance().createUnauthenticated(context, e164, SignalServiceAddress.DEFAULT_DEVICE_ID, password)
if (mode == Mode.PHONE_CALL) {
return@fromCallable accountManager.requestVoiceVerificationCode(sessionId, Locale.getDefault(), mode.isSmsRetrieverSupported)
} else {
@ -135,7 +135,7 @@ class VerifyAccountRepository(private val context: Application) {
}
fun verifyAccount(sessionId: String, registrationData: RegistrationData): Single<ServiceResponse<RegistrationSessionMetadataResponse>> {
val accountManager: SignalServiceAccountManager = AccountManagerFactory.createUnauthenticated(
val accountManager: SignalServiceAccountManager = AccountManagerFactory.getInstance().createUnauthenticated(
context,
registrationData.e164,
SignalServiceAddress.DEFAULT_DEVICE_ID,
@ -154,7 +154,7 @@ class VerifyAccountRepository(private val context: Application) {
val universalUnidentifiedAccess: Boolean = TextSecurePreferences.isUniversalUnidentifiedAccess(context)
val unidentifiedAccessKey: ByteArray = UnidentifiedAccess.deriveAccessKeyFrom(registrationData.profileKey)
val accountManager: SignalServiceAccountManager = AccountManagerFactory.createUnauthenticated(
val accountManager: SignalServiceAccountManager = AccountManagerFactory.getInstance().createUnauthenticated(
context,
registrationData.e164,
SignalServiceAddress.DEFAULT_DEVICE_ID,

View file

@ -158,7 +158,7 @@ public final class SignalProxyUtil {
private static boolean testWebsocketConnectionUnregistered(long timeout) {
CountDownLatch latch = new CountDownLatch(1);
AtomicBoolean success = new AtomicBoolean(false);
SignalServiceAccountManager accountManager = AccountManagerFactory.createUnauthenticated(ApplicationDependencies.getApplication(), "", SignalServiceAddress.DEFAULT_DEVICE_ID, "");
SignalServiceAccountManager accountManager = AccountManagerFactory.getInstance().createUnauthenticated(ApplicationDependencies.getApplication(), "", SignalServiceAddress.DEFAULT_DEVICE_ID, "");
SignalExecutors.UNBOUNDED.execute(() -> {
try {

View file

@ -0,0 +1,22 @@
package org.thoughtcrime.securesms.util
import org.thoughtcrime.securesms.BuildConfig
import androidx.tracing.Trace as AndroidTrace
object SignalTrace {
@JvmStatic
fun beginSection(methodName: String) {
if (!BuildConfig.TRACING_ENABLED) {
return
}
AndroidTrace.beginSection(methodName)
}
@JvmStatic
fun endSection() {
if (!BuildConfig.TRACING_ENABLED) {
return
}
AndroidTrace.endSection()
}
}

1
benchmark/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,81 @@
@file:Suppress("UnstableApiUsage")
import com.android.build.api.dsl.ManagedVirtualDevice
import org.gradle.api.JavaVersion
import org.gradle.kotlin.dsl.extra
val benchmarkLibs = the<org.gradle.accessors.dm.LibrariesForBenchmarkLibs>()
val signalBuildToolsVersion: String by extra
val signalCompileSdkVersion: String by extra
val signalTargetSdkVersion: Int by extra
val signalMinSdkVersion: Int by extra
val signalJavaVersion: JavaVersion by extra
plugins {
id("com.android.test")
id("android-constants")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "org.signal.benchmark"
compileSdkVersion = signalCompileSdkVersion
compileOptions {
sourceCompatibility = signalJavaVersion
targetCompatibility = signalJavaVersion
}
kotlinOptions {
jvmTarget = "11"
}
defaultConfig {
minSdk = 23
targetSdk = signalTargetSdkVersion
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
missingDimensionStrategy("environment", "prod")
missingDimensionStrategy("distribution", "play")
}
buildTypes {
create("benchmark") {
isDebuggable = true
signingConfig = getByName("debug").signingConfig
matchingFallbacks += listOf("perf", "debug")
}
}
targetProjectPath = ":Signal-Android"
experimentalProperties["android.experimental.self-instrumenting"] = true
testOptions {
managedDevices {
devices {
create ("api31", ManagedVirtualDevice::class) {
device = "Pixel 6"
apiLevel = 31
systemImageSource = "aosp"
require64Bit = false
}
}
}
}
}
dependencies {
implementation(benchmarkLibs.androidx.test.ext.junit)
implementation(benchmarkLibs.espresso.core)
implementation(benchmarkLibs.uiautomator)
implementation(benchmarkLibs.androidx.benchmark.macro)
}
androidComponents {
beforeVariants(selector().all()) {
it.enabled = it.buildType == "benchmark"
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<package android:name="org.thoughtcrime.securesms" />
</queries>
</manifest>

View file

@ -0,0 +1,42 @@
@file:OptIn(ExperimentalBaselineProfilesApi::class)
package org.thoughtcrime.benchmark
import android.content.ComponentName
import android.content.Intent
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import org.junit.Rule
import org.junit.Test
/**
* WARNING! THIS WILL WIPE YOUR SIGNAL INSTALL
*
* Test that generates a Baseline profile from a user journey. Our journey is:
* - start the app
* - open a conversation
*/
class BaselineProfileGenerator {
@get:Rule
val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() = baselineProfileRule.collectBaselineProfile(
packageName = "org.thoughtcrime.securesms",
profileBlock = {
val setupIntent = Intent().apply {
component = ComponentName("org.thoughtcrime.securesms", "org.signal.benchmark.BenchmarkSetupActivity")
}
startActivityAndWait(setupIntent)
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)
}
)
}

View file

@ -0,0 +1,61 @@
package org.thoughtcrime.benchmark
import android.content.ComponentName
import android.content.Intent
import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.ExperimentalMetricApi
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.TraceSectionMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Macrobenchmark benchmarks for app startup performance.
*
* WARNING! THIS WILL WIPE YOUR SIGNAL INSTALL
*/
@RunWith(AndroidJUnit4::class)
class StartupBenchmarks {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun coldStartNone() {
measureStartup(5, CompilationMode.None())
}
@Test
fun coldStartBaselineProfile() {
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) {
benchmarkRule.measureRepeated(
packageName = "org.thoughtcrime.securesms",
metrics = listOf(StartupTimingMetric(), TraceSectionMetric("ConversationListDataSource#load")),
iterations = iterations,
startupMode = StartupMode.COLD,
compilationMode = compilationMode,
setupBlock = fakeDataSetupBlock
) {
pressHome()
startActivityAndWait()
}
}
}

View file

@ -51,7 +51,7 @@ subprojects {
}
}
if (project.name != "Signal-Android" && project.name != "libsignal-service" && project.name != "lintchecks" && !project.name.endsWith("-app")) {
if (project.name != "Signal-Android" && project.name != "libsignal-service" && project.name != "lintchecks" && !project.name.endsWith("-app") && project.name != "benchmark") {
task qa {
group 'Verification'
description 'Quality Assurance. Run before pushing'

View file

@ -73,6 +73,7 @@ dependencyResolutionManagement {
alias('androidx-sharetarget').to('androidx.sharetarget:sharetarget:1.2.0-rc02')
alias('androidx-sqlite').to('androidx.sqlite:sqlite:2.1.0')
alias('androidx-core-role').to('androidx.core:core-role:1.0.0')
alias('androidx-profileinstaller').to('androidx.profileinstaller:profileinstaller:1.2.2')
// Material
alias('material-material').to('com.google.android.material:material:1.8.0')
@ -147,6 +148,16 @@ dependencyResolutionManagement {
bundle('mp4parser', ['mp4parser-isoparser', 'mp4parser-streaming', 'mp4parser-muxer'])
}
benchmarkLibs {
version('androidx-test-ext-junit', '1.1.3')
// Macrobench/Baseline profiles
alias ('androidx-test-ext-junit').to('androidx.test.ext', 'junit').versionRef('androidx-test-ext-junit')
alias('espresso-core').to('androidx.test.espresso:espresso-core:3.4.0')
alias('uiautomator').to('androidx.test.uiautomator:uiautomator:2.2.0')
alias('androidx-benchmark-macro').to('androidx.benchmark:benchmark-macro-junit4:1.1.1')
}
testLibs {
version('androidx-test', '1.4.0')
version('androidx-test-ext-junit', '1.1.1')

View file

@ -79,6 +79,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.annotation" name="annotation" version="1.3.0">
<artifact name="annotation-1.3.0.jar">
<sha256 value="97dc45afefe3a1e421da42b8b6e9f90491477c45fc6178203e3a5e8a05ee8553" origin="Generated by Gradle"/>
</artifact>
<artifact name="annotation-1.3.0.module">
<sha256 value="9516c2ae44284ea0bd3d0eade0ee638879b708cbe31e3af92ba96c300604ebc3" origin="Generated by Gradle"/>
</artifact>
@ -161,6 +164,78 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="c9468f56e05006ea151a426c54957cd0799b8b83a579d2846dd22061f33e5ecd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-common" version="1.1.0-beta05">
<artifact name="benchmark-common-1.1.0-beta05.aar">
<sha256 value="685021a5a65fc92f94e84a942f029edb2fc77e89144b666c8a85e1538f35ad19" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-common-1.1.0-beta05.module">
<sha256 value="4f95dd05b913974187c990832bf09f904220f7be1a2a1366463eab7eba2e2ea2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-common" version="1.1.1">
<artifact name="benchmark-common-1.1.1.aar">
<sha256 value="bd66de24090fc8f7228859cd7cb326869e62ebbf5069ec9c06a4804c8ff83195" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-common-1.1.1.module">
<sha256 value="62198ddb35ec8370f061950481f874c96ad986d8228f2bd7fb501cc3b7075070" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-common" version="1.2.0-alpha10">
<artifact name="benchmark-common-1.2.0-alpha10.aar">
<sha256 value="38f2c778a69b8843d390a47239d3d3aaa81ce114bcbc45c69bff90518541fad0" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-common-1.2.0-alpha10.module">
<sha256 value="5c22871af95906149ea0a0ae9a30d76e93c35382dd5a1cb8df942773143325b8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro" version="1.1.0-beta05">
<artifact name="benchmark-macro-1.1.0-beta05.aar">
<sha256 value="979733a465d24a85536d4a51d0a9bfc20d8cd6537e032f88a5fb669f25d880e7" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-1.1.0-beta05.module">
<sha256 value="0929537dd9f790c9784b8ca20b92e59197a0466c720981c78a86667201e00f8f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro" version="1.1.1">
<artifact name="benchmark-macro-1.1.1.aar">
<sha256 value="298a6bdd2adc313db1606c918f38263f16c46ef0ec849f5a0af2a98805b2f423" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-1.1.1.module">
<sha256 value="714b731ef20da20539d52be597ca030fc81aaa8abded38a8f5d68f69456c509b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro" version="1.2.0-alpha10">
<artifact name="benchmark-macro-1.2.0-alpha10.aar">
<sha256 value="ac397fc7d46282a4afaa8c3f0285c337312c46b2dc91fc010290ac7b38a47457" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-1.2.0-alpha10.module">
<sha256 value="436437c6e2569265f449f3203e0fa00b2b18438fcaece70181446d5585368663" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro-junit4" version="1.1.0-beta05">
<artifact name="benchmark-macro-junit4-1.1.0-beta05.aar">
<sha256 value="ff73e04affb90e03c8955946286cf9ab0c89afea419a3df311da43669f8080b6" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-junit4-1.1.0-beta05.module">
<sha256 value="99b783f2b4d7e1c723fc0493044c87078ebcb03e59a2902df0f2e0825d47844c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro-junit4" version="1.1.1">
<artifact name="benchmark-macro-junit4-1.1.1.aar">
<sha256 value="af9a96a901c47227e59360545709bbe9d347b993111f8375d59b880162b05ee1" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-junit4-1.1.1.module">
<sha256 value="465927d805b76c133994659c94b221e6de1377fe550a1ce09a359bfd6170b92b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.benchmark" name="benchmark-macro-junit4" version="1.2.0-alpha10">
<artifact name="benchmark-macro-junit4-1.2.0-alpha10.aar">
<sha256 value="9f0265e0cc1f001c1e72e24442c7ef2b8e5de26cbf196b87a8688ef168499a2f" origin="Generated by Gradle"/>
</artifact>
<artifact name="benchmark-macro-junit4-1.2.0-alpha10.module">
<sha256 value="0feea5b65a9f768a543c7d0f2380d148ad9c23669549fd55340f850696be5b2a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.biometric" name="biometric" version="1.1.0">
<artifact name="biometric-1.1.0.aar">
<sha256 value="270c7b7d99942d5ec1dd88594e4648feb33d8e31d8c3c2ab2321d49d9abdfc1f" origin="Generated by Gradle"/>
@ -386,6 +461,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="5595a40e278a7b39fa78a09490e3d7f3faa95c7b01447148bd38b5ade0605c35" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.concurrent" name="concurrent-futures" version="1.1.0">
<artifact name="concurrent-futures-1.1.0.jar">
<sha256 value="0ce067c514a0d1049d1bebdf709e344ed3266fe9744275682937cdcb13334e9e" origin="Generated by Gradle"/>
</artifact>
<artifact name="concurrent-futures-1.1.0.module">
<sha256 value="77639a0b051e22510bad93affcea0ebd781ef124bf9b7621a95749937bcfcdfd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.constraintlayout" name="constraintlayout" version="2.0.4">
<artifact name="constraintlayout-2.0.4.aar">
<sha256 value="307a79a4a1ccff44249c72a2bf7f47da09fa1b6b1fab2a25808ca889382b738e" origin="Generated by Gradle"/>
@ -627,6 +710,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="7bad7a188804adea6fa1f35d5ef99b705f20bd93ecadde484760ff86b535fefc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.3.1">
<artifact name="lifecycle-common-2.3.1.jar">
<sha256 value="15848fb56db32f4c7cdc72b324003183d52a4884d6bf09be708ac7f587d139b5" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-common-2.3.1.module">
<sha256 value="5fb7c8514d8c56cada5e29ef89dc0289e71942ab4cb0b2e6dca137b9dcb8fdd4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.5.1">
<artifact name="lifecycle-common-2.5.1.jar">
<sha256 value="20ad1520f625cf455e6afd7290988306d3a9886efa993e0860fbabf4bb3f7bda" origin="Generated by Gradle"/>
@ -728,6 +819,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.3.1">
<artifact name="lifecycle-runtime-2.3.1.aar">
<sha256 value="dd294f4a689c71ff877fd41f3b67a3a62f7760d44ce420e6130f1fc3569d8f00" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-runtime-2.3.1.module">
<sha256 value="2a7b90e5049b674b36bccfd68677b3a0b3178b3f7c2ef7ddf618d3895598c4ce" origin="Generated by Gradle"/>
</artifact>
@ -898,6 +992,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="1d5c7f3135a1bba661fc373fd72e11eb0a4adbb3396787826dd8e4190d5d9edd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.profileinstaller" name="profileinstaller" version="1.0.3">
<artifact name="profileinstaller-1.0.3.aar">
<sha256 value="099685e5229ed2e5a586cfb1b0f92adacd0605e7fad4278b4989c05d3a5f709c" origin="Generated by Gradle"/>
</artifact>
<artifact name="profileinstaller-1.0.3.module">
<sha256 value="d730955cef887cf6b1a13d602f784f2f0e386570fde503b8e4b8b7b4f811a522" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.profileinstaller" name="profileinstaller" version="1.2.0">
<artifact name="profileinstaller-1.2.0.aar">
<sha256 value="0a1b2260af67962f4038d56fa84cda341315704aad56abbcf3a9b3bba6945426" origin="Generated by Gradle"/>
@ -906,6 +1008,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="fdb7391ab3e704cb4790181476eec1a45f77b7827642974861e0f510d084c32b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.profileinstaller" name="profileinstaller" version="1.2.2">
<artifact name="profileinstaller-1.2.2.aar">
<sha256 value="c0a6772d3dd880e5d73681e2b5827fd542a3f3bcf3dbf09fa847f6aaab17042e" origin="Generated by Gradle"/>
</artifact>
<artifact name="profileinstaller-1.2.2.module">
<sha256 value="7093bdffed3f18bc4ddfbd0784f75c1dbbd70e8ab3ccf4bff5b0e1c2713edbbe" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.recyclerview" name="recyclerview" version="1.1.0">
<artifact name="recyclerview-1.1.0.aar">
<sha256 value="f0d2b5a67d0a91ee1b1c73ef2b636a81f3563925ddd15a1d4e1c41ec28de7a4f" origin="Generated by Gradle"/>
@ -970,6 +1080,9 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
</artifact>
</component>
<component group="androidx.startup" name="startup-runtime" version="1.0.0">
<artifact name="startup-runtime-1.0.0.aar">
<sha256 value="ff081d2db7dd28aec59f74934c514fbaf4ae5aac5258495fe10d612a3622f876" origin="Generated by Gradle"/>
</artifact>
<artifact name="startup-runtime-1.0.0.module">
<sha256 value="40effca0d6ee1fde32bc296897e54ebbcc4cf4aa29b0c531036cbd2a824a3c24" origin="Generated by Gradle"/>
</artifact>
@ -987,11 +1100,26 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="9761b3a809c9b093fd06a3c4bbc645756dec0e95b5c9da419bc9f2a3f3026e8d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="annotation" version="1.0.0">
<artifact name="annotation-1.0.0.aar">
<sha256 value="c0754928effe1968c3a9a7b55d1dfc7ceb1e1e7c9f3f09f98afd42431f712492" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="annotation" version="1.0.1">
<artifact name="annotation-1.0.1.aar">
<sha256 value="c0754928effe1968c3a9a7b55d1dfc7ceb1e1e7c9f3f09f98afd42431f712492" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="core" version="1.4.0">
<artifact name="core-1.4.0.aar">
<sha256 value="671284e62e393f16ceae1a99a3a9a07bf1aacda29f8fe7b6b884355ef34c09cf" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="core" version="1.5.0">
<artifact name="core-1.5.0.aar">
<sha256 value="2c06715c0d0843cee2143ab8bb322bb3f34d5247630402fc8c1b6a0eafa15b9f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="core-ktx" version="1.4.0">
<artifact name="core-ktx-1.4.0.aar">
<sha256 value="e4f9ca2b8f700cc278d878ed3925730e1ad5d60135bbfd05ab6708a528ebfa58" origin="Generated by Gradle"/>
@ -1002,11 +1130,26 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="46a912a1e175f27a97521af3f50e5af87c22c49275dd2c57c043740012806325" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="monitor" version="1.5.0">
<artifact name="monitor-1.5.0.aar">
<sha256 value="10b1723c436beecb5884c69f8473504bc59611f9463ae549c48b3cf8e73b09c0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="monitor" version="1.6.1">
<artifact name="monitor-1.6.1.aar">
<sha256 value="2985ce8556989baf7c84342e7f687713c037a39a922e614d1a3ddf1ca3777079" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="orchestrator" version="1.4.1">
<artifact name="orchestrator-1.4.1.apk">
<sha256 value="0c7a02b619e4a97ee8ab55983ab19c6559d02aaff55710e6d2c1b8581832d4c1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="rules" version="1.3.0">
<artifact name="rules-1.3.0.aar">
<sha256 value="c1753946c498b0d5d7cf341cfed661f66915c4c9deb4ed10462a08ae33b2429a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test" name="runner" version="1.4.0">
<artifact name="runner-1.4.0.aar">
<sha256 value="e3f3d8b8d5d4a3edcacbdaa4a31bda2b0e41d3e704b02b3750466a06367ec5a0" origin="Generated by Gradle"/>
@ -1027,6 +1170,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="449df418d2916a0f86fe7dafb1edb09480fafb6e995d5c751c7d0d1970d4ae72" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test.ext" name="junit" version="1.1.3">
<artifact name="junit-1.1.3.aar">
<sha256 value="a97209d75a9a85815fa8934f5a4a320de1163ffe94e2f0b328c0c98a59660690" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test.ext" name="junit-ktx" version="1.1.1">
<artifact name="junit-ktx-1.1.1.aar">
<sha256 value="53990566c0638c1603655cfd680396c4c0aec5000f58015883034491273c7c0e" origin="Generated by Gradle"/>
@ -1037,11 +1185,21 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="35cfbf442abb83e5876cd5deb9de02ae047459f18f831097c5caa76d626bc38a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test.services" name="storage" version="1.4.2">
<artifact name="storage-1.4.2.aar">
<sha256 value="b34861f0cd920cb1089f08c3f27e5865b7f920284cc45f4ed12ef8d6980dac48" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test.services" name="test-services" version="1.4.1">
<artifact name="test-services-1.4.1.apk">
<sha256 value="99da32be34bfb54a6b402db05410332898494e6b24a16266e08f1fdc5ef361f2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.test.uiautomator" name="uiautomator" version="2.2.0">
<artifact name="uiautomator-2.2.0.aar">
<sha256 value="2838e9d961dbffefbbd229a2bd4f6f82ac4fb2462975862a9e75e9ed325a3197" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing" version="1.0.0">
<artifact name="tracing-1.0.0.aar">
<sha256 value="07b8b6139665b884a162eccf97891ca50f7f56831233bf25168ae04f7b568612" origin="Generated by Gradle"/>
@ -1050,6 +1208,72 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="fc8b21ebe5fa3a7c96ee098bcdcd00f077ebce73f243fa858e2b0671615f75d8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing" version="1.1.0">
<artifact name="tracing-1.1.0.aar">
<sha256 value="5b78e2c618fc10b3d14decc01df76158f15954ad746aacf0607766721da081f6" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-1.1.0.module">
<sha256 value="b1fed4309623b6f20bc817d8fbd70e4ea7085e40647694cd399ae58d2f0049e3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing" version="1.1.0-beta01">
<artifact name="tracing-1.1.0-beta01.aar">
<sha256 value="b5bd4a9c4be034c9e71395dbaf521ba740c9e10e9f1100c35500f08ddb80ddc5" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-1.1.0-beta01.module">
<sha256 value="5040c2a116bf0bc1a523376fbee5d134d34be92eae8fde8866a4f57c64e4e12a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing" version="1.2.0-alpha02">
<artifact name="tracing-1.2.0-alpha02.aar">
<sha256 value="c9bb1534cceeacf7b9940499eb5768966cfb1c9e51c86f5829390edc2d0dfc14" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-1.2.0-alpha02.module">
<sha256 value="65bd23769c41ef6276cca87e0910f17642d7712b8fcc34e060193156289e8bc9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.0.0">
<artifact name="tracing-ktx-1.0.0.module">
<sha256 value="f426e636a23a05db8c7fabf2b959f4dab80c9ca97b5131bf411d422555c48c18" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.1.0">
<artifact name="tracing-ktx-1.1.0.aar">
<sha256 value="914d8c83aa16455d431b5b3af306cef0eb0a0bfd6550d097e39ad475435bf56c" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-ktx-1.1.0.module">
<sha256 value="6ec8aa998df9b964424c7ab215738237a9b2cc4301da135bb9b8cdc62325fb3e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.1.0-beta01">
<artifact name="tracing-ktx-1.1.0-beta01.aar">
<sha256 value="a145682e014272602721938e4f8494c8f7e820d499e02fbb8383a6990226a191" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-ktx-1.1.0-beta01.module">
<sha256 value="9ae839bfb123aab5e52dfcacfbbc359b8e0fd405aed5afcf03ba476d8bc85d95" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.1.0-rc01">
<artifact name="tracing-ktx-1.1.0-rc01.module">
<sha256 value="23ab26ce8003909f952c2a83c021cf3c9eb0ae688105ab0cda6bab73405b4373" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.2.0-alpha02">
<artifact name="tracing-ktx-1.2.0-alpha02.aar">
<sha256 value="a170f1dab0f5f47d90f60f6bbf3358170ccdef2c815d3ab62eef4f1278bd5c96" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-ktx-1.2.0-alpha02.module">
<sha256 value="fd09602256d61a19b047f0c466e8647634ef5473f0fe520f802b0a8951e7efff" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-perfetto-common" version="1.0.0-alpha11">
<artifact name="tracing-perfetto-common-1.0.0-alpha11.jar">
<sha256 value="ec9f031a211eccefcd86ab68a951e2cd7c889959b44ae18d720934a4c09c2185" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-perfetto-common-1.0.0-alpha11.module">
<sha256 value="80453d621f7d279e1f81ccc52dee24d689d929941c0414a776bbcb4630744377" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.transition" name="transition" version="1.2.0">
<artifact name="transition-1.2.0.aar">
<sha256 value="a1e059b3bc0b43a58dec0efecdcaa89c82d2bca552ea5bacf6656c46e853157e" origin="Generated by Gradle"/>
@ -2128,6 +2352,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="1e7f53fa5b8b5c807e986ba335665da03f18d660802d8bf061823089d1bee468" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.code.findbugs" name="jsr305" version="2.0.2">
<artifact name="jsr305-2.0.2.jar">
<sha256 value="1e7f53fa5b8b5c807e986ba335665da03f18d660802d8bf061823089d1bee468" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
<artifact name="jsr305-3.0.2.jar">
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
@ -2790,6 +3019,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="e58c97406a6bb1138893750299ac63c6aa04b38b6b49eae1bfcad1a63ef9ba1b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.okio" name="okio" version="2.8.0">
<artifact name="okio-2.8.0.module">
<sha256 value="17baab7270389a5fa63ab12811864d0a00f381611bc4eb042fa1bd5918ed0965" origin="Generated by Gradle"/>
</artifact>
<artifact name="okio-jvm-2.8.0.jar">
<sha256 value="4496b06e73982fcdd8a5393f46e5df2ce2fa4465df5895454cac68a32f09bbc8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.okio" name="okio" version="3.0.0">
<artifact name="okio-3.0.0.module">
<sha256 value="6f9e3a797831e75c5b562d946c075183f9b2be846791e9f88bde45491daed987" origin="Generated by Gradle"/>
@ -2811,6 +3048,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="17f48d41775bd84dea78e9dfed8dfbcc66af80567a5c9ec9d9608785ec820cde" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.okio" name="okio-multiplatform" version="2.8.0">
<artifact name="okio-multiplatform-2.8.0.module">
<sha256 value="87c4327538ba166a914171631470a6e9db2b007125bae164af6b0d47aefd34d7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-compiler" version="4.4.3">
<artifact name="wire-compiler-4.4.3.jar">
<sha256 value="3927416764234a9e981e4bd49068760bed327a1056476a85a00fcfec1e3f5976" origin="Generated by Gradle"/>
@ -2843,6 +3085,22 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="283fb66d5c9d466972a96b365f6dbb64c26b77fafff2fca15befc1c5155e1521" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime" version="3.6.0">
<artifact name="wire-runtime-3.6.0.module">
<sha256 value="3b99891842fdec80e7b24ae7f7c485ae41ca35b47c902ca2043cc948aaf58010" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-runtime-jvm-3.6.0.jar">
<sha256 value="ac41d3f9b8a88046788c6827b0519bf0c53dcc271f598f48aa666c6f5a9523d0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime" version="4.4.1">
<artifact name="wire-runtime-4.4.1.module">
<sha256 value="e718b13c051ccf702f665741f4ad77307c5e9a1e328b6936357780bc216a3f29" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-runtime-metadata-4.4.1.jar">
<sha256 value="cfc84bf20df3c9503f89289f6d568f047f921bd3752a6c98ce7a20f2b34b35c9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime" version="4.4.3">
<artifact name="wire-runtime-4.4.3.module">
<sha256 value="7c66b05bc09c9cead5794a9708fedce6a39217fea16558d2a799fdf702537675" origin="Generated by Gradle"/>
@ -2851,6 +3109,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="3f350a77f7c241d6612d44859e27c8999b5ce2a47baaad7277ba3b7d450b4818" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime-jvm" version="4.4.1">
<artifact name="wire-runtime-jvm-4.4.1.jar">
<sha256 value="52fc66613e6cb36e197d2f609954d8ca23e4ea0d033e505fda2bf08a139e8fbb" origin="Generated by Gradle"/>
</artifact>
<artifact name="wire-runtime-jvm-4.4.1.module">
<sha256 value="4e28e28ea03e2bd02fb55e1b0193c683c765bd951d690cd7b704e764488ac73a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.squareup.wire" name="wire-runtime-jvm" version="4.4.3">
<artifact name="wire-runtime-jvm-4.4.3.jar">
<sha256 value="d3c0cc527e7a3dcaf59e3ae41e382dd0f62dfef3043911a7768e3787dc0b6335" origin="Generated by Gradle"/>
@ -3896,6 +4162,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="c77bef8774640b9fb9d6e217459ff220dae59878beb7d2e4b430506feffc654e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="1.8.10">
<artifact name="kotlin-stdlib-1.8.10.jar">
<sha256 value="17e1076131cd07c958a942ff8a087cf865b1ef3de58463e1e5dd6fd7515406b0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.4.10">
<artifact name="kotlin-stdlib-common-1.4.10.jar">
<sha256 value="4681f2d436a68c7523595d84ed5758e1382f9da0f67c91e6a848690d711274fe" origin="Generated by Gradle"/>
@ -3936,6 +4207,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
<sha256 value="78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.8.10">
<artifact name="kotlin-stdlib-common-1.8.10.jar">
<sha256 value="385031de4c6dd9e990d6a44455d79583572a490a3464d7fe7f10a5ecca3225a8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-jdk7" version="1.4.10">
<artifact name="kotlin-stdlib-jdk7-1.4.10.jar">
<sha256 value="f9566380c08722c780ce33ceee23e98ddf765ca98fabd3e2fabae7975c8d232b" origin="Generated by Gradle"/>

View file

@ -56,6 +56,7 @@ include ':qr-app'
include ':sticky-header-grid'
include ':photoview'
include ':core-ui'
include ':benchmark'
project(':app').name = 'Signal-Android'
project(':paging').projectDir = file('paging/lib')