Merge various proto utils together in core-util-jvm.

This commit is contained in:
Greyson Parrelli 2024-09-20 23:29:08 -04:00
parent 5b69d98579
commit ec49352635
17 changed files with 122 additions and 136 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 <E> ProtoAdapter<E>.decodeOrNull(serialized: ByteArray): E? {
return try {
this.decode(serialized)
} catch (e: InvalidProtocolBufferException) {
null
}
}

View file

@ -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)

View file

@ -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 <E> ProtoAdapter<E>.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<Any>).encodeWithTag(writer, tag, enumValue.toLong())
return buffer.readByteString()
}
/**
* Recursively retrieves all inner complex proto types inside a given proto.
*/
private fun Message<*, *>.getInnerProtos(): MutableList<Message<*, *>> {
val innerProtos: MutableList<Message<*, *>> = 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
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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<Any>).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<Message<*, *>> {
val innerProtos: MutableList<Message<*, *>> = 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
}
}