From e036344c76068f3bfee577a5d70ae92d708f143e Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 29 Sep 2021 15:29:24 +1000 Subject: [PATCH] refactor: performance improvements in batch message processing, synchronized cache access and audible message notifications. Increase audible timeout on DefaultMessageNotifier.java, don't send in-thread notification based on last audible notification. Create a batch message receive job to handle up to 20 chunked messages at a time per job instead of singular or open group poll amount Remove synchronized access to recipient cache and replace with a concurrent cache that's lock free from perf tracing monitor contention --- .../notifications/DefaultMessageNotifier.java | 7 +- .../libsession/database/StorageProtocol.kt | 3 +- .../messaging/jobs/BatchMessageReceiveJob.kt | 120 ++++ .../libsession/messaging/jobs/JobQueue.kt | 11 +- .../messaging/jobs/MessageReceiveJob.kt | 18 +- .../jobs/SessionJobManagerFactories.kt | 3 +- .../pollers/OpenGroupPollerV2.kt | 41 +- .../sending_receiving/pollers/Poller.kt | 13 +- .../utilities/TextSecurePreferences.kt | 2 +- .../recipients/RecipientProvider.java | 10 +- libsignal/protobuf/Makefile | 2 +- libsignal/protobuf/Utils.proto | 10 + .../session/libsignal/protos/UtilProtos.java | 512 ++++++++++++++++++ 13 files changed, 700 insertions(+), 52 deletions(-) create mode 100644 libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt create mode 100644 libsignal/protobuf/Utils.proto create mode 100644 libsignal/src/main/java/org/session/libsignal/protos/UtilProtos.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java index 936548ecc7..c155e124dd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -97,7 +97,7 @@ public class DefaultMessageNotifier implements MessageNotifier { private static final int SUMMARY_NOTIFICATION_ID = 1338; private static final int PENDING_MESSAGES_ID = 1111; private static final String NOTIFICATION_GROUP = "messages"; - private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(2); + private static final long MIN_AUDIBLE_PERIOD_MILLIS = TimeUnit.SECONDS.toMillis(5); private static final long DESKTOP_ACTIVITY_PERIOD = TimeUnit.MINUTES.toMillis(1); private volatile static long visibleThread = -1; @@ -440,9 +440,12 @@ public class DefaultMessageNotifier implements MessageNotifier { private void sendInThreadNotification(Context context, Recipient recipient) { if (!TextSecurePreferences.isInThreadNotifications(context) || - ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL) + ServiceUtil.getAudioManager(context).getRingerMode() != AudioManager.RINGER_MODE_NORMAL || + (System.currentTimeMillis() - lastAudibleNotification) < MIN_AUDIBLE_PERIOD_MILLIS) { return; + } else { + lastAudibleNotification = System.currentTimeMillis(); } Uri uri = null; diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index 3c8e0c7123..09a3ba5e4c 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -5,7 +5,6 @@ import android.net.Uri import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.Job -import org.session.libsession.messaging.jobs.MessageReceiveJob import org.session.libsession.messaging.jobs.MessageSendJob import org.session.libsession.messaging.messages.control.ConfigurationMessage import org.session.libsession.messaging.messages.visible.Attachment @@ -43,7 +42,7 @@ interface StorageProtocol { fun getAllPendingJobs(type: String): Map fun getAttachmentUploadJob(attachmentID: Long): AttachmentUploadJob? fun getMessageSendJob(messageSendJobID: String): MessageSendJob? - fun getMessageReceiveJob(messageReceiveJobID: String): MessageReceiveJob? + fun getMessageReceiveJob(messageReceiveJobID: String): Job? fun resumeMessageSendJobIfNeeded(messageSendJobID: String) fun isJobCanceled(job: Job): Boolean diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt new file mode 100644 index 0000000000..f1df08d1fa --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -0,0 +1,120 @@ +package org.session.libsession.messaging.jobs + +import com.google.protobuf.ByteString +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.task +import org.session.libsession.messaging.sending_receiving.MessageReceiver +import org.session.libsession.messaging.sending_receiving.handle +import org.session.libsession.messaging.utilities.Data +import org.session.libsignal.protos.UtilProtos +import org.session.libsignal.utilities.Log + +data class MessageReceiveParameters( + val data: ByteArray, + val serverHash: String? = null, + val openGroupMessageServerID: Long? = null +) + +class BatchMessageReceiveJob( + val messages: List, + val openGroupID: String? = null +) : Job { + + override var delegate: JobDelegate? = null + override var id: String? = null + override var failureCount: Int = 0 + override val maxFailureCount: Int = 10 + // Failure Exceptions must be retryable if they're a MessageReceiver.Error + val failures = mutableListOf() + + companion object { + const val TAG = "BatchMessageReceiveJob" + const val KEY = "BatchMessageReceiveJob" + + // Keys used for database storage + private val NUM_MESSAGES_KEY = "numMessages" + private val DATA_KEY = "data" + private val SERVER_HASH_KEY = "serverHash" + private val OPEN_GROUP_MESSAGE_SERVER_ID_KEY = "openGroupMessageServerID" + private val OPEN_GROUP_ID_KEY = "open_group_id" + } + + override fun execute() { + executeAsync().get() + } + + fun executeAsync(): Promise { + return task { + messages.forEach { messageParameters -> + val (data, serverHash, openGroupMessageServerID) = messageParameters + try { + val (message, proto) = MessageReceiver.parse(data, openGroupMessageServerID) + message.serverHash = serverHash + MessageReceiver.handle(message, proto, this.openGroupID) + } catch (e: Exception) { + Log.e(TAG, "Couldn't receive message.", e) + if (e is MessageReceiver.Error && !e.isRetryable) { + Log.e(TAG, "Message failed permanently",e) + } else { + Log.e(TAG, "Message failed",e) + failures += messageParameters + } + } + } + if (failures.isEmpty()) { + handleSuccess() + } else { + handleFailure() + } + } + } + + private fun handleSuccess() { + this.delegate?.handleJobSucceeded(this) + } + + private fun handleFailure() { + this.delegate?.handleJobFailed(this, Exception("One or more jobs resulted in failure")) + } + + override fun serialize(): Data { + val arraySize = messages.size + val dataArrays = UtilProtos.ByteArrayList.newBuilder() + .addAllContent(messages.map(MessageReceiveParameters::data).map(ByteString::copyFrom)) + .build() + val serverHashes = messages.map { it.serverHash.orEmpty() } + val openGroupServerIds = messages.map { it.openGroupMessageServerID ?: -1L } + return Data.Builder() + .putInt(NUM_MESSAGES_KEY, arraySize) + .putByteArray(DATA_KEY, dataArrays.toByteArray()) + .putString(OPEN_GROUP_ID_KEY, openGroupID) + .putLongArray(OPEN_GROUP_MESSAGE_SERVER_ID_KEY, openGroupServerIds.toLongArray()) + .putStringArray(SERVER_HASH_KEY, serverHashes.toTypedArray()) + .build() + } + + override fun getFactoryKey(): String = KEY + + class Factory : Job.Factory { + override fun create(data: Data): BatchMessageReceiveJob { + val numMessages = data.getInt(NUM_MESSAGES_KEY) + val dataArrays = data.getByteArray(DATA_KEY) + val contents = + UtilProtos.ByteArrayList.parseFrom(dataArrays).contentList.map(ByteString::toByteArray) + val serverHashes = + if (data.hasStringArray(SERVER_HASH_KEY)) data.getStringArray(SERVER_HASH_KEY) else arrayOf() + val openGroupMessageServerIDs = data.getLongArray(OPEN_GROUP_MESSAGE_SERVER_ID_KEY) + val openGroupID = data.getStringOrDefault(OPEN_GROUP_ID_KEY, null) + + val parameters = (0 until numMessages).map { index -> + val data = contents[index] + val serverHash = serverHashes[index].let { if (it.isEmpty()) null else it } + val serverId = openGroupMessageServerIDs[index].let { if (it == -1L) null else it } + MessageReceiveParameters(data, serverHash, serverId) + } + + return BatchMessageReceiveJob(parameters, openGroupID) + } + } + +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt index 205db3d6f8..0c200c588e 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsignal.utilities.Log -import java.lang.IllegalStateException import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors @@ -51,7 +50,7 @@ class JobQueue : JobDelegate { when (job) { is NotifyPNServerJob, is AttachmentUploadJob, is MessageSendJob -> txQueue.send(job) is AttachmentDownloadJob -> attachmentQueue.send(job) - is MessageReceiveJob, is TrimThreadJob -> rxQueue.send(job) + is MessageReceiveJob, is BatchMessageReceiveJob, is TrimThreadJob -> rxQueue.send(job) else -> throw IllegalStateException("Unexpected job type.") } } @@ -128,7 +127,8 @@ class JobQueue : JobDelegate { AttachmentDownloadJob.KEY, MessageReceiveJob.KEY, MessageSendJob.KEY, - NotifyPNServerJob.KEY + NotifyPNServerJob.KEY, + BatchMessageReceiveJob.KEY ) allJobTypes.forEach { type -> resumePendingJobs(type) @@ -152,6 +152,11 @@ class JobQueue : JobDelegate { Log.i("Loki", "Message send job waiting for attachment upload to finish.") return } + // Batch message receive job, re-queue non-permanently failed jobs + if (job is BatchMessageReceiveJob) { + val replacementParameters = job.failures + } + // Regular job failure job.failureCount += 1 if (job.failureCount >= job.maxFailureCount) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt index 0601c670af..2377a7edce 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt @@ -17,8 +17,6 @@ class MessageReceiveJob(val data: ByteArray, val serverHash: String? = null, val val TAG = MessageReceiveJob::class.simpleName val KEY: String = "MessageReceiveJob" - private val RECEIVE_LOCK = Object() - // Keys used for database storage private val DATA_KEY = "data" private val SERVER_HASH_KEY = "serverHash" @@ -36,9 +34,7 @@ class MessageReceiveJob(val data: ByteArray, val serverHash: String? = null, val val isRetry: Boolean = failureCount != 0 val (message, proto) = MessageReceiver.parse(this.data, this.openGroupMessageServerID) message.serverHash = serverHash - synchronized(RECEIVE_LOCK) { // FIXME: Do we need this? - MessageReceiver.handle(message, proto, this.openGroupID) - } + MessageReceiver.handle(message, proto, this.openGroupID) this.handleSuccess() deferred.resolve(Unit) } catch (e: Exception) { @@ -82,11 +78,15 @@ class MessageReceiveJob(val data: ByteArray, val serverHash: String? = null, val class Factory: Job.Factory { override fun create(data: Data): MessageReceiveJob { + val dataArray = data.getByteArray(DATA_KEY) + val serverHash = data.getStringOrDefault(SERVER_HASH_KEY, null) + val openGroupMessageServerID = data.getLongOrDefault(OPEN_GROUP_MESSAGE_SERVER_ID_KEY, -1).let { if (it == -1L) null else it } + val openGroupID = data.getStringOrDefault(OPEN_GROUP_ID_KEY, null) return MessageReceiveJob( - data.getByteArray(DATA_KEY), - data.getString(SERVER_HASH_KEY), - data.getLong(OPEN_GROUP_MESSAGE_SERVER_ID_KEY), - data.getString(OPEN_GROUP_ID_KEY) + dataArray, + serverHash, + openGroupMessageServerID, + openGroupID ) } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt index 15526981cf..cc22c59dbf 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/SessionJobManagerFactories.kt @@ -11,7 +11,8 @@ class SessionJobManagerFactories { MessageReceiveJob.KEY to MessageReceiveJob.Factory(), MessageSendJob.KEY to MessageSendJob.Factory(), NotifyPNServerJob.KEY to NotifyPNServerJob.Factory(), - TrimThreadJob.KEY to TrimThreadJob.Factory() + TrimThreadJob.KEY to TrimThreadJob.Factory(), + BatchMessageReceiveJob.KEY to BatchMessageReceiveJob.Factory() ) } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt index aadb73b9e1..194072e543 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/OpenGroupPollerV2.kt @@ -3,17 +3,12 @@ package org.session.libsession.messaging.sending_receiving.pollers import nl.komponents.kovenant.Promise import nl.komponents.kovenant.functional.map import org.session.libsession.messaging.MessagingModuleConfiguration -import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.MessageReceiveJob -import org.session.libsession.messaging.jobs.TrimThreadJob +import org.session.libsession.messaging.jobs.* import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.open_groups.OpenGroupMessageV2 -import org.session.libsession.messaging.sending_receiving.MessageReceiver -import org.session.libsession.messaging.sending_receiving.handle import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil import org.session.libsignal.protos.SignalServiceProtos -import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.successBackground import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledFuture @@ -27,7 +22,7 @@ class OpenGroupPollerV2(private val server: String, private val executorService: private var future: ScheduledFuture<*>? = null companion object { - private val pollInterval: Long = 4 * 1000 + private const val pollInterval: Long = 4000L const val maxInactivityPeriod = 14 * 24 * 60 * 60 * 1000 } @@ -66,21 +61,22 @@ class OpenGroupPollerV2(private val server: String, private val executorService: val threadId = storage.getThreadId(Address.fromSerialized(groupID)) ?: -1 val threadExists = threadId >= 0 if (!hasStarted || !threadExists) { return } - messages.sortedBy { it.serverID!! }.forEach { message -> - try { - val senderPublicKey = message.sender!! - val builder = SignalServiceProtos.Envelope.newBuilder() - builder.type = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE - builder.source = senderPublicKey - builder.sourceDevice = 1 - builder.content = message.toProto().toByteString() - builder.timestamp = message.sentTimestamp - val envelope = builder.build() - val (parsedMessage, content) = MessageReceiver.parse(envelope.toByteArray(), message.serverID) - MessageReceiver.handle(parsedMessage, content, openGroupID) - } catch (e: Exception) { - Log.e("Loki", "Exception parsing message", e) + val envelopes = messages.sortedBy { it.serverID!! }.map { message -> + val senderPublicKey = message.sender!! + val builder = SignalServiceProtos.Envelope.newBuilder() + builder.type = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE + builder.source = senderPublicKey + builder.sourceDevice = 1 + builder.content = message.toProto().toByteString() + builder.timestamp = message.sentTimestamp + builder.build() to message.serverID + } + + envelopes.chunked(20).forEach { list -> + val parameters = list.map { (message, serverId) -> + MessageReceiveParameters(message.toByteArray(), openGroupMessageServerID = serverId) } + JobQueue.shared.add(BatchMessageReceiveJob(parameters, openGroupID)) } val currentLastMessageServerID = storage.getLastMessageServerID(room, server) ?: 0 @@ -88,9 +84,6 @@ class OpenGroupPollerV2(private val server: String, private val executorService: if (actualMax > 0) { storage.setLastMessageServerID(room, server, actualMax) } - if (messages.isNotEmpty()) { - JobQueue.shared.add(TrimThreadJob(threadId)) - } } private fun handleDeletedMessages(room: String, openGroupID: String, deletions: List) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt index be472874d0..0edcf0a509 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/Poller.kt @@ -3,12 +3,13 @@ package org.session.libsession.messaging.sending_receiving.pollers import nl.komponents.kovenant.* import nl.komponents.kovenant.functional.bind import org.session.libsession.messaging.MessagingModuleConfiguration +import org.session.libsession.messaging.jobs.BatchMessageReceiveJob import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.MessageReceiveJob +import org.session.libsession.messaging.jobs.MessageReceiveParameters import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeModule -import org.session.libsignal.utilities.Snode import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.Snode import java.security.SecureRandom import java.util.* @@ -91,10 +92,14 @@ class Poller { task { Unit } // The long polling connection has been canceled; don't recurse } else { val messages = SnodeAPI.parseRawMessagesResponse(rawResponse, snode, userPublicKey) - messages.forEach { (envelope, serverHash) -> - val job = MessageReceiveJob(envelope.toByteArray(), serverHash) + val parameters = messages.map { (envelope, serverHash) -> + MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash) + } + parameters.chunked(20).forEach { chunk -> + val job = BatchMessageReceiveJob(chunk) JobQueue.shared.add(job) } + poll(snode, deferred) } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 8c67a36b80..07bc99d3b5 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -363,7 +363,7 @@ object TextSecurePreferences { @JvmStatic fun getDirectCaptureCameraId(context: Context): Int { - return getIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, Camera.CameraInfo.CAMERA_FACING_FRONT) + return getIntegerPreference(context, DIRECT_CAPTURE_CAMERA_ID, Camera.CameraInfo.CAMERA_FACING_BACK) } @JvmStatic diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java b/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java index f87906e4f9..5174eae4e5 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java @@ -29,7 +29,6 @@ import org.session.libsession.utilities.Address; import org.session.libsession.utilities.GroupRecord; import org.session.libsession.utilities.ListenableFutureTask; import org.session.libsession.utilities.MaterialColor; -import org.session.libsession.utilities.SoftHashMap; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.Util; import org.session.libsession.utilities.recipients.Recipient.RecipientSettings; @@ -43,6 +42,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; class RecipientProvider { @@ -222,17 +222,17 @@ class RecipientProvider { private static class RecipientCache { - private final Map cache = new SoftHashMap<>(1000); + private final Map cache = new ConcurrentHashMap<>(1000); - public synchronized Recipient get(Address address) { + public Recipient get(Address address) { return cache.get(address); } - public synchronized void set(Address address, Recipient recipient) { + public void set(Address address, Recipient recipient) { cache.put(address, recipient); } - public synchronized boolean remove(Address address) { + public boolean remove(Address address) { return cache.remove(address) != null; } diff --git a/libsignal/protobuf/Makefile b/libsignal/protobuf/Makefile index 77fe09c08b..9fa9a86dcc 100644 --- a/libsignal/protobuf/Makefile +++ b/libsignal/protobuf/Makefile @@ -1,3 +1,3 @@ all: - protoc25 --java_out=../src/main/java/ SignalService.proto WebSocketResources.proto + protoc25 --java_out=../src/main/java/ SignalService.proto WebSocketResources.proto Utils.proto diff --git a/libsignal/protobuf/Utils.proto b/libsignal/protobuf/Utils.proto new file mode 100644 index 0000000000..7d6794016a --- /dev/null +++ b/libsignal/protobuf/Utils.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +package signalservice; + +option java_package = "org.session.libsignal.protos"; +option java_outer_classname = "UtilProtos"; + +message ByteArrayList { + repeated bytes content = 1; +} diff --git a/libsignal/src/main/java/org/session/libsignal/protos/UtilProtos.java b/libsignal/src/main/java/org/session/libsignal/protos/UtilProtos.java new file mode 100644 index 0000000000..1b005f194f --- /dev/null +++ b/libsignal/src/main/java/org/session/libsignal/protos/UtilProtos.java @@ -0,0 +1,512 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Utils.proto + +package org.session.libsignal.protos; + +public final class UtilProtos { + private UtilProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface ByteArrayListOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // repeated bytes content = 1; + /** + * repeated bytes content = 1; + */ + java.util.List getContentList(); + /** + * repeated bytes content = 1; + */ + int getContentCount(); + /** + * repeated bytes content = 1; + */ + com.google.protobuf.ByteString getContent(int index); + } + /** + * Protobuf type {@code signalservice.ByteArrayList} + */ + public static final class ByteArrayList extends + com.google.protobuf.GeneratedMessage + implements ByteArrayListOrBuilder { + // Use ByteArrayList.newBuilder() to construct. + private ByteArrayList(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ByteArrayList(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ByteArrayList defaultInstance; + public static ByteArrayList getDefaultInstance() { + return defaultInstance; + } + + public ByteArrayList getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ByteArrayList( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + content_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + content_.add(input.readBytes()); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) { + content_ = java.util.Collections.unmodifiableList(content_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.protos.UtilProtos.internal_static_signalservice_ByteArrayList_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.protos.UtilProtos.internal_static_signalservice_ByteArrayList_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.protos.UtilProtos.ByteArrayList.class, org.session.libsignal.protos.UtilProtos.ByteArrayList.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ByteArrayList parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ByteArrayList(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + // repeated bytes content = 1; + public static final int CONTENT_FIELD_NUMBER = 1; + private java.util.List content_; + /** + * repeated bytes content = 1; + */ + public java.util.List + getContentList() { + return content_; + } + /** + * repeated bytes content = 1; + */ + public int getContentCount() { + return content_.size(); + } + /** + * repeated bytes content = 1; + */ + public com.google.protobuf.ByteString getContent(int index) { + return content_.get(index); + } + + private void initFields() { + content_ = java.util.Collections.emptyList(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + for (int i = 0; i < content_.size(); i++) { + output.writeBytes(1, content_.get(i)); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + { + int dataSize = 0; + for (int i = 0; i < content_.size(); i++) { + dataSize += com.google.protobuf.CodedOutputStream + .computeBytesSizeNoTag(content_.get(i)); + } + size += dataSize; + size += 1 * getContentList().size(); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.session.libsignal.protos.UtilProtos.ByteArrayList parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.session.libsignal.protos.UtilProtos.ByteArrayList prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signalservice.ByteArrayList} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.session.libsignal.protos.UtilProtos.ByteArrayListOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.session.libsignal.protos.UtilProtos.internal_static_signalservice_ByteArrayList_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.session.libsignal.protos.UtilProtos.internal_static_signalservice_ByteArrayList_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.session.libsignal.protos.UtilProtos.ByteArrayList.class, org.session.libsignal.protos.UtilProtos.ByteArrayList.Builder.class); + } + + // Construct using org.session.libsignal.protos.UtilProtos.ByteArrayList.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + content_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.session.libsignal.protos.UtilProtos.internal_static_signalservice_ByteArrayList_descriptor; + } + + public org.session.libsignal.protos.UtilProtos.ByteArrayList getDefaultInstanceForType() { + return org.session.libsignal.protos.UtilProtos.ByteArrayList.getDefaultInstance(); + } + + public org.session.libsignal.protos.UtilProtos.ByteArrayList build() { + org.session.libsignal.protos.UtilProtos.ByteArrayList result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.session.libsignal.protos.UtilProtos.ByteArrayList buildPartial() { + org.session.libsignal.protos.UtilProtos.ByteArrayList result = new org.session.libsignal.protos.UtilProtos.ByteArrayList(this); + int from_bitField0_ = bitField0_; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + content_ = java.util.Collections.unmodifiableList(content_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.content_ = content_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.session.libsignal.protos.UtilProtos.ByteArrayList) { + return mergeFrom((org.session.libsignal.protos.UtilProtos.ByteArrayList)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.session.libsignal.protos.UtilProtos.ByteArrayList other) { + if (other == org.session.libsignal.protos.UtilProtos.ByteArrayList.getDefaultInstance()) return this; + if (!other.content_.isEmpty()) { + if (content_.isEmpty()) { + content_ = other.content_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureContentIsMutable(); + content_.addAll(other.content_); + } + onChanged(); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.session.libsignal.protos.UtilProtos.ByteArrayList parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.session.libsignal.protos.UtilProtos.ByteArrayList) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // repeated bytes content = 1; + private java.util.List content_ = java.util.Collections.emptyList(); + private void ensureContentIsMutable() { + if (!((bitField0_ & 0x00000001) == 0x00000001)) { + content_ = new java.util.ArrayList(content_); + bitField0_ |= 0x00000001; + } + } + /** + * repeated bytes content = 1; + */ + public java.util.List + getContentList() { + return java.util.Collections.unmodifiableList(content_); + } + /** + * repeated bytes content = 1; + */ + public int getContentCount() { + return content_.size(); + } + /** + * repeated bytes content = 1; + */ + public com.google.protobuf.ByteString getContent(int index) { + return content_.get(index); + } + /** + * repeated bytes content = 1; + */ + public Builder setContent( + int index, com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureContentIsMutable(); + content_.set(index, value); + onChanged(); + return this; + } + /** + * repeated bytes content = 1; + */ + public Builder addContent(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + ensureContentIsMutable(); + content_.add(value); + onChanged(); + return this; + } + /** + * repeated bytes content = 1; + */ + public Builder addAllContent( + java.lang.Iterable values) { + ensureContentIsMutable(); + super.addAll(values, content_); + onChanged(); + return this; + } + /** + * repeated bytes content = 1; + */ + public Builder clearContent() { + content_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signalservice.ByteArrayList) + } + + static { + defaultInstance = new ByteArrayList(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signalservice.ByteArrayList) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signalservice_ByteArrayList_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signalservice_ByteArrayList_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\013Utils.proto\022\rsignalservice\" \n\rByteArra" + + "yList\022\017\n\007content\030\001 \003(\014B*\n\034org.session.li" + + "bsignal.protosB\nUtilProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signalservice_ByteArrayList_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signalservice_ByteArrayList_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signalservice_ByteArrayList_descriptor, + new java.lang.String[] { "Content", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +}