diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt index ba276e15bc..46fe65cad8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt @@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.backup.v2.database import android.database.Cursor import androidx.core.content.contentValuesOf import org.signal.core.util.SqlUtil +import org.signal.core.util.decodeOrNull import org.signal.core.util.insertInto import org.signal.core.util.logging.Log import org.signal.core.util.requireBlob @@ -30,7 +31,6 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.ChatColor import org.thoughtcrime.securesms.database.model.databaseprotos.Wallpaper import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.recipients.RecipientId -import org.thoughtcrime.securesms.util.decodeOrNull import org.thoughtcrime.securesms.wallpaper.ChatWallpaper import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory import org.thoughtcrime.securesms.wallpaper.UriChatWallpaper diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt index 6808bdfde0..6e50e35f81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageConverter.kt @@ -5,9 +5,9 @@ package org.thoughtcrime.securesms.database.model -import ProtoUtil.isNullOrEmpty import okio.ByteString import org.signal.core.util.StringUtil +import org.signal.core.util.isNullOrEmpty import org.signal.storageservice.protos.groups.AccessControl import org.signal.storageservice.protos.groups.AccessControl.AccessRequired import org.signal.storageservice.protos.groups.Member diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt index e3f6a70779..8ca06d701e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/DataMessageProcessor.kt @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.messages -import ProtoUtil.isNotEmpty import android.content.Context import android.text.TextUtils import com.mobilecoin.lib.exceptions.SerializationException @@ -8,6 +7,7 @@ import okio.ByteString.Companion.toByteString import org.signal.core.util.Base64 import org.signal.core.util.Hex import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.isNotEmpty import org.signal.core.util.logging.Log import org.signal.core.util.orNull import org.signal.core.util.toOptional diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt index 0a09e38108..9ed0a8e4da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SignalServiceProtoUtil.kt @@ -1,9 +1,9 @@ package org.thoughtcrime.securesms.messages -import ProtoUtil.isNotEmpty import com.squareup.wire.Message import okio.ByteString import okio.ByteString.Companion.toByteString +import org.signal.core.util.isNotEmpty import org.signal.core.util.orNull import org.signal.libsignal.protocol.message.DecryptionErrorMessage import org.signal.libsignal.zkgroup.groups.GroupMasterKey diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt index f7a315dcf3..8dbd453d45 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/SyncMessageProcessor.kt @@ -1,11 +1,11 @@ package org.thoughtcrime.securesms.messages -import ProtoUtil.isNotEmpty import android.content.Context import com.mobilecoin.lib.exceptions.SerializationException import okio.ByteString import org.signal.core.util.Base64 import org.signal.core.util.Hex +import org.signal.core.util.isNotEmpty import org.signal.core.util.orNull import org.signal.libsignal.protocol.IdentityKey import org.signal.libsignal.protocol.InvalidKeyException diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ProtoExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ProtoExtensions.kt deleted file mode 100644 index 8a2a76ced4..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ProtoExtensions.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2024 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.thoughtcrime.securesms.util - -import com.google.protobuf.InvalidProtocolBufferException -import com.squareup.wire.ProtoAdapter - -/** - * Performs the common pattern of attempting to decode a serialized proto and returning null if it fails to decode. - */ -fun ProtoAdapter.decodeOrNull(serialized: ByteArray): E? { - return try { - this.decode(serialized) - } catch (e: InvalidProtocolBufferException) { - null - } -} diff --git a/core-util-jvm/build.gradle.kts b/core-util-jvm/build.gradle.kts index 1c6b2152f0..9ce4d6f4d1 100644 --- a/core-util-jvm/build.gradle.kts +++ b/core-util-jvm/build.gradle.kts @@ -10,6 +10,7 @@ plugins { id("java-library") id("org.jetbrains.kotlin.jvm") id("ktlint") + id("com.squareup.wire") } java { @@ -23,6 +24,16 @@ kotlin { } } +wire { + kotlin { + javaInterop = true + } + + sourcePath { + srcDir("src/main/protowire") + } +} + dependencies { implementation(libs.kotlin.reflect) implementation(libs.kotlinx.coroutines.core) diff --git a/core-util-jvm/src/main/java/org/signal/core/util/ProtoExtensions.kt b/core-util-jvm/src/main/java/org/signal/core/util/ProtoExtensions.kt new file mode 100644 index 0000000000..2bd6cf61d1 --- /dev/null +++ b/core-util-jvm/src/main/java/org/signal/core/util/ProtoExtensions.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2024 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +@file:JvmName("ProtoUtil") + +package org.signal.core.util + +import com.squareup.wire.FieldEncoding +import com.squareup.wire.Message +import com.squareup.wire.ProtoAdapter +import com.squareup.wire.ProtoReader +import com.squareup.wire.ProtoWriter +import okio.Buffer +import okio.ByteString +import org.signal.core.util.logging.Log +import java.io.IOException +import java.util.LinkedList + +private const val TAG = "ProtoExtension" + +fun ByteString?.isNotEmpty(): Boolean { + return this != null && this.size > 0 +} + +fun ByteString?.isNullOrEmpty(): Boolean { + return this == null || this.size == 0 +} + +/** + * Performs the common pattern of attempting to decode a serialized proto and returning null if it fails to decode. + */ +fun ProtoAdapter.decodeOrNull(serialized: ByteArray): E? { + return try { + this.decode(serialized) + } catch (e: IOException) { + null + } +} + +/** + * True if there are unknown fields anywhere inside the proto or its nested protos. + */ +fun Message<*, *>.hasUnknownFields(): Boolean { + val allProtos = this.getInnerProtos() + allProtos.add(this) + for (proto in allProtos) { + val unknownFields = proto.unknownFields + if (unknownFields.size > 0) { + return true + } + } + return false +} + +fun Message<*, *>.getUnknownEnumValue(tag: Int): Int { + val reader = ProtoReader(Buffer().write(this.unknownFields)) + reader.forEachTag { unknownTag -> + if (unknownTag == tag) { + return ProtoAdapter.INT32.decode(reader) + } + } + + throw AssertionError("Tag $tag not found in unknown fields") +} + +fun writeUnknownEnumValue(tag: Int, enumValue: Int): ByteString { + val buffer = Buffer() + val writer = ProtoWriter(buffer) + + @Suppress("UNCHECKED_CAST") + (FieldEncoding.VARINT.rawProtoAdapter() as ProtoAdapter).encodeWithTag(writer, tag, enumValue.toLong()) + + return buffer.readByteString() +} + +/** + * Recursively retrieves all inner complex proto types inside a given proto. + */ +private fun Message<*, *>.getInnerProtos(): MutableList> { + val innerProtos: MutableList> = LinkedList() + try { + val fields = this.javaClass.declaredFields + for (field in fields) { + if (Message::class.java.isAssignableFrom(field.type)) { + field.isAccessible = true + val inner = field[this] as? Message<*, *> + if (inner != null) { + innerProtos.add(inner) + innerProtos.addAll(inner.getInnerProtos()) + } + } + } + } catch (e: IllegalAccessException) { + Log.w(TAG, "Failed to get inner protos!", e) + } + return innerProtos +} diff --git a/core-util/src/main/java/ProtoUtil.kt b/core-util/src/main/java/ProtoUtil.kt deleted file mode 100644 index 0b36206cf5..0000000000 --- a/core-util/src/main/java/ProtoUtil.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -@file:JvmName("ProtoUtil") - -import okio.ByteString - -object ProtoUtil { - - fun ByteString?.isNotEmpty(): Boolean { - return this != null && this.size > 0 - } - - fun ByteString?.isNullOrEmpty(): Boolean { - return this == null || this.size == 0 - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java index 386bc14aa4..3335a2316f 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalAccountRecord.java @@ -1,11 +1,11 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.logging.Log; import org.whispersystems.signalservice.api.payments.PaymentsConstants; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.util.OptionalUtil; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.AccountRecord; import org.whispersystems.signalservice.internal.storage.protos.OptionalBool; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java index 1b595ea337..90e468a8c8 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalContactRecord.java @@ -1,11 +1,11 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.logging.Log; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.ServiceId.ACI; import org.whispersystems.signalservice.api.push.ServiceId.PNI; import org.whispersystems.signalservice.api.util.OptionalUtil; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.ContactRecord; import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java index 7df3282371..3537171cc9 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV1Record.java @@ -1,7 +1,7 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.logging.Log; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.GroupV1Record; import java.io.IOException; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java index 4ef3cc874f..059a8979ad 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalGroupV2Record.java @@ -1,9 +1,9 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.zkgroup.InvalidInputException; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record; import java.io.IOException; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageManifest.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageManifest.java index 12d6844e0a..bf50e00c83 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageManifest.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageManifest.java @@ -1,7 +1,6 @@ package org.whispersystems.signalservice.api.storage; -import org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage; -import org.whispersystems.signalservice.api.util.ProtoUtil; +import org.signal.core.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import org.whispersystems.signalservice.internal.storage.protos.StorageManifest; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java index ed2f0f76cb..fca879f2b5 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageModels.java @@ -1,9 +1,9 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.InvalidKeyException; import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.zkgroup.groups.GroupMasterKey; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import org.whispersystems.signalservice.internal.storage.protos.StorageItem; import org.whispersystems.signalservice.internal.storage.protos.StorageManifest; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java index 4e2f209e3a..e7c497c2ff 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStoryDistributionListRecord.java @@ -1,9 +1,9 @@ package org.whispersystems.signalservice.api.storage; +import org.signal.core.util.ProtoUtil; import org.signal.libsignal.protocol.logging.Log; import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.push.SignalServiceAddress; -import org.whispersystems.signalservice.api.util.ProtoUtil; import org.whispersystems.signalservice.internal.storage.protos.StoryDistributionListRecord; import java.io.IOException; diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/ProtoUtil.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/ProtoUtil.kt deleted file mode 100644 index 52078c0e37..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/util/ProtoUtil.kt +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2023 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -@file:JvmName("ProtoUtil") - -package org.whispersystems.signalservice.api.util - -import com.squareup.wire.FieldEncoding -import com.squareup.wire.Message -import com.squareup.wire.ProtoAdapter -import com.squareup.wire.ProtoReader -import com.squareup.wire.ProtoWriter -import okio.Buffer -import okio.ByteString -import org.signal.libsignal.protocol.logging.Log -import java.util.LinkedList - -object ProtoUtil { - private val TAG = ProtoUtil::class.java.simpleName - - /** - * True if there are unknown fields anywhere inside the proto or its nested protos. - */ - @JvmStatic - fun hasUnknownFields(rootProto: Message<*, *>): Boolean { - val allProtos = getInnerProtos(rootProto) - allProtos.add(rootProto) - for (proto in allProtos) { - val unknownFields = proto.unknownFields - if (unknownFields.size > 0) { - return true - } - } - return false - } - - @JvmStatic - fun getUnknownEnumValue(proto: Message<*, *>, tag: Int): Int { - val reader = ProtoReader(Buffer().write(proto.unknownFields)) - reader.forEachTag { unknownTag -> - if (unknownTag == tag) { - return ProtoAdapter.INT32.decode(reader) - } - } - - throw AssertionError("Tag $tag not found in unknown fields") - } - - @JvmStatic - fun writeUnknownEnumValue(tag: Int, enumValue: Int): ByteString { - val buffer = Buffer() - val writer = ProtoWriter(buffer) - - @Suppress("UNCHECKED_CAST") - (FieldEncoding.VARINT.rawProtoAdapter() as ProtoAdapter).encodeWithTag(writer, tag, enumValue.toLong()) - - return buffer.readByteString() - } - - /** - * Recursively retrieves all inner complex proto types inside a given proto. - */ - private fun getInnerProtos(proto: Message<*, *>): MutableList> { - val innerProtos: MutableList> = LinkedList() - try { - val fields = proto.javaClass.declaredFields - for (field in fields) { - if (Message::class.java.isAssignableFrom(field.type)) { - field.isAccessible = true - val inner = field[proto] as? Message<*, *> - if (inner != null) { - innerProtos.add(inner) - innerProtos.addAll(getInnerProtos(inner)) - } - } - } - } catch (e: IllegalAccessException) { - Log.w(TAG, "Failed to get inner protos!", e) - } - return innerProtos - } -}