Skip to content

Log Refactor #14087

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/java/org/signal/glide/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public interface Provider {
void w(@NonNull String tag, @NonNull String message);
void e(@NonNull String tag, @NonNull String message, @Nullable Throwable throwable);


Provider EMPTY = new Provider() {
@Override
public void v(@NonNull String tag, @NonNull String message) { }
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/org/signal/glide/transforms/LogMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.signal.glide.transforms;

public record LogMessage(String value) {
public LogMessage {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("LogMessage cannot be null or blank");
}
}
}
Comment on lines +1 to +9

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that this PR introduces a new file in Java, while we're in the midst of migrating our codebase to Kotlin. Is there a particular reason we opted for Java here instead of Kotlin?
Would it be possible to implement this new functionality in Kotlin to maintain consistency with our migration efforts?

9 changes: 9 additions & 0 deletions app/src/main/java/org/signal/glide/transforms/LogTag.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.signal.glide.transforms;

public record LogTag(String value) {
public LogTag {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("LogTag cannot be null or blank");
}
}
}
Comment on lines +1 to +9

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

10 changes: 10 additions & 0 deletions app/src/main/java/org/signal/glide/transforms/LoggingService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.signal.glide.transforms;

import androidx.annotation.Nullable;

public interface LoggingService {
void logDebug(LogTag tag, LogMessage message);
void logInfo(LogTag tag, LogMessage message);
void logWarn(LogTag tag, LogMessage message);
void logError(LogTag tag, LogMessage message, @Nullable Throwable throwable);
}
Comment on lines +1 to +10

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Original file line number Diff line number Diff line change
Expand Up @@ -8,185 +8,211 @@ import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.war
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.ringrtc.RemotePeer
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.AnswerMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.CallMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.HangupMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.OfferMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.OpaqueMessageMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.ReceivedAnswerMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.ReceivedOfferMetadata
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.*
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.api.messages.calls.HangupMessage
import org.whispersystems.signalservice.api.messages.calls.OfferMessage
import org.whispersystems.signalservice.api.push.ServiceId
import org.whispersystems.signalservice.internal.push.CallMessage
import org.whispersystems.signalservice.internal.push.CallMessage.Offer
import org.whispersystems.signalservice.internal.push.CallMessage.Opaque
import org.whispersystems.signalservice.internal.push.Content
import org.whispersystems.signalservice.internal.push.Envelope
import kotlin.time.Duration.Companion.milliseconds

object CallMessageProcessor {
interface CallHandler {
fun canHandle(callMessage: CallMessage): Boolean
fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
serverDeliveredTimestamp: Long
)
}

class CallMessageProcessor(private val handlers: List<CallHandler>) {
fun process(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
serverDeliveredTimestamp: Long
) {
val callMessage = content.callMessage!!

when {
callMessage.offer != null -> handleCallOfferMessage(envelope, metadata, callMessage.offer!!, senderRecipient.id, serverDeliveredTimestamp)
callMessage.answer != null -> handleCallAnswerMessage(envelope, metadata, callMessage.answer!!, senderRecipient.id)
callMessage.iceUpdate.isNotEmpty() -> handleCallIceUpdateMessage(envelope, metadata, callMessage.iceUpdate, senderRecipient.id)
callMessage.hangup != null -> handleCallHangupMessage(envelope, metadata, callMessage.hangup!!, senderRecipient.id)
callMessage.busy != null -> handleCallBusyMessage(envelope, metadata, callMessage.busy!!, senderRecipient.id)
callMessage.opaque != null -> handleCallOpaqueMessage(envelope, metadata, callMessage.opaque!!, senderRecipient.requireAci(), serverDeliveredTimestamp)
}
val callMessage = content.callMessage ?: return
handlers.firstOrNull { it.canHandle(callMessage) }
?.handle(senderRecipient, envelope, content, metadata, serverDeliveredTimestamp)
?: warn(envelope.timestamp ?: 0, "No handler for call message type")
}
}

private fun handleCallOfferMessage(envelope: Envelope, metadata: EnvelopeMetadata, offer: Offer, senderRecipientId: RecipientId, serverDeliveredTimestamp: Long) {
log(envelope.timestamp!!, "handleCallOfferMessage...")
class OfferCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.offer != null

val offerId = if (offer.id != null && offer.type != null && offer.opaque != null) {
offer.id!!
} else {
warn(envelope.timestamp!!, "Invalid offer, missing id, type, or opaque")
override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp ?: 0, "handleCallOfferMessage...")

val offer = content.callMessage!!.offer!!
if (offer.id == null || offer.type == null || offer.opaque == null) {
warn(envelope.timestamp ?: 0, "Invalid offer, missing id, type, or opaque")
return
}

val remotePeer = RemotePeer(senderRecipientId, CallId(offerId))
val remoteIdentityKey = AppDependencies.protocolStore.aci().identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()
val remotePeer = RemotePeer(senderRecipient.id, CallId(offer.id))
val remoteIdentityKey = AppDependencies.protocolStore.aci().identities().getIdentityRecord(senderRecipient.id)
.map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()

AppDependencies.signalCallManager
.receivedOffer(
CallMetadata(remotePeer, metadata.sourceDeviceId),
OfferMetadata(offer.opaque?.toByteArray(), OfferMessage.Type.fromProto(offer.type!!)),
ReceivedOfferMetadata(
remoteIdentityKey,
envelope.serverTimestamp!!,
serverDeliveredTimestamp
)
)
AppDependencies.signalCallManager.receivedOffer(
CallMetadata(remotePeer, metadata.sourceDeviceId),
OfferMetadata(offer.opaque.toByteArray(), CallMessage.Offer.Type.fromProto(offer.type)),
ReceivedOfferMetadata(remoteIdentityKey, envelope.serverTimestamp!!, serverDeliveredTimestamp)
)
}
}

class AnswerCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.answer != null

private fun handleCallAnswerMessage(
override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
answer: CallMessage.Answer,
senderRecipientId: RecipientId
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp!!, "handleCallAnswerMessage...")
log(envelope.timestamp ?: 0, "handleCallAnswerMessage...")

val answerId = if (answer.id != null && answer.opaque != null) {
answer.id!!
} else {
warn(envelope.timestamp!!, "Invalid answer, missing id or opaque")
val answer = content.callMessage!!.answer!!
if (answer.id == null || answer.opaque == null) {
warn(envelope.timestamp ?: 0, "Invalid answer, missing id or opaque")
return
}

val remotePeer = RemotePeer(senderRecipientId, CallId(answerId))
val remoteIdentityKey = AppDependencies.protocolStore.aci().identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()
val remotePeer = RemotePeer(senderRecipient.id, CallId(answer.id))
val remoteIdentityKey = AppDependencies.protocolStore.aci().identities().getIdentityRecord(senderRecipient.id)
.map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()

AppDependencies.signalCallManager
.receivedAnswer(
CallMetadata(remotePeer, metadata.sourceDeviceId),
AnswerMetadata(answer.opaque?.toByteArray()),
ReceivedAnswerMetadata(remoteIdentityKey)
)
AppDependencies.signalCallManager.receivedAnswer(
CallMetadata(remotePeer, metadata.sourceDeviceId),
AnswerMetadata(answer.opaque.toByteArray()),
ReceivedAnswerMetadata(remoteIdentityKey)
)
}
}

class IceUpdateCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.iceUpdate.isNotEmpty()

private fun handleCallIceUpdateMessage(
override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
iceUpdateList: List<CallMessage.IceUpdate>,
senderRecipientId: RecipientId
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp!!, "handleCallIceUpdateMessage... " + iceUpdateList.size)

val iceCandidates: MutableList<ByteArray> = ArrayList(iceUpdateList.size)
var callId: Long = -1
log(envelope.timestamp ?: 0, "handleCallIceUpdateMessage... ${content.callMessage!!.iceUpdate.size}")

iceUpdateList
val iceCandidates = content.callMessage!!.iceUpdate
.filter { it.opaque != null && it.id != null }
.forEach { iceUpdate ->
iceCandidates += iceUpdate.opaque!!.toByteArray()
callId = iceUpdate.id!!
}
.map { it.opaque!!.toByteArray() }

val callId = content.callMessage!!.iceUpdate.firstOrNull { it.id != null }?.id ?: -1L

if (iceCandidates.isNotEmpty()) {
val remotePeer = RemotePeer(senderRecipientId, CallId(callId))
AppDependencies.signalCallManager
.receivedIceCandidates(
CallMetadata(remotePeer, metadata.sourceDeviceId),
iceCandidates
)
val remotePeer = RemotePeer(senderRecipient.id, CallId(callId))
AppDependencies.signalCallManager.receivedIceCandidates(
CallMetadata(remotePeer, metadata.sourceDeviceId),
iceCandidates
)
} else {
warn(envelope.timestamp!!, "Invalid ice updates, all missing opaque and/or call id")
warn(envelope.timestamp ?: 0, "Invalid ice updates, all missing opaque and/or call id")
}
}
}

private fun handleCallHangupMessage(
class HangupCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.hangup != null

override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
hangup: CallMessage.Hangup?,
senderRecipientId: RecipientId
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp!!, "handleCallHangupMessage")
log(envelope.timestamp ?: 0, "handleCallHangupMessage")

val (hangupId: Long, hangupDeviceId: Int?) = if (hangup?.id != null) {
hangup.id!! to hangup.deviceId
} else {
warn(envelope.timestamp!!, "Invalid hangup, null message or missing id/deviceId")
val hangup = content.callMessage!!.hangup
if (hangup?.id == null) {
warn(envelope.timestamp ?: 0, "Invalid hangup, null message or missing id/deviceId")
return
}

val remotePeer = RemotePeer(senderRecipientId, CallId(hangupId))
AppDependencies.signalCallManager
.receivedCallHangup(
CallMetadata(remotePeer, metadata.sourceDeviceId),
HangupMetadata(HangupMessage.Type.fromProto(hangup.type), hangupDeviceId ?: 0)
)
val remotePeer = RemotePeer(senderRecipient.id, CallId(hangup.id))
AppDependencies.signalCallManager.receivedCallHangup(
CallMetadata(remotePeer, metadata.sourceDeviceId),
HangupMetadata(HangupMessage.Type.fromProto(hangup.type), hangup.deviceId ?: 0)
)
}
}

private fun handleCallBusyMessage(envelope: Envelope, metadata: EnvelopeMetadata, busy: CallMessage.Busy, senderRecipientId: RecipientId) {
log(envelope.timestamp!!, "handleCallBusyMessage")
class BusyCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.busy != null

val busyId = if (busy.id != null) {
busy.id!!
} else {
warn(envelope.timestamp!!, "Invalid busy, missing call id")
override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp ?: 0, "handleCallBusyMessage")

val busy = content.callMessage!!.busy
if (busy?.id == null) {
warn(envelope.timestamp ?: 0, "Invalid busy, missing call id")
return
}

val remotePeer = RemotePeer(senderRecipientId, CallId(busyId))
val remotePeer = RemotePeer(senderRecipient.id, CallId(busy.id))
AppDependencies.signalCallManager.receivedCallBusy(CallMetadata(remotePeer, metadata.sourceDeviceId))
}
}

private fun handleCallOpaqueMessage(envelope: Envelope, metadata: EnvelopeMetadata, opaque: Opaque, senderServiceId: ServiceId, serverDeliveredTimestamp: Long) {
log(envelope.timestamp!!, "handleCallOpaqueMessage")
class OpaqueCallHandler : CallHandler {
override fun canHandle(callMessage: CallMessage): Boolean = callMessage.opaque != null

val data = if (opaque.data_ != null) {
opaque.data_!!.toByteArray()
} else {
warn(envelope.timestamp!!, "Invalid opaque message, null data")
return
}
override fun handle(
senderRecipient: Recipient,
envelope: Envelope,
content: Content,
metadata: EnvelopeMetadata,
serverDeliveredTimestamp: Long
) {
log(envelope.timestamp ?: 0, "handleCallOpaqueMessage")

var messageAgeSeconds: Long = 0
if (envelope.serverTimestamp in 1..serverDeliveredTimestamp) {
messageAgeSeconds = (serverDeliveredTimestamp - envelope.serverTimestamp!!).milliseconds.inWholeSeconds
val opaque = content.callMessage!!.opaque
val data = opaque?.data_?.toByteArray() ?: run {
warn(envelope.timestamp ?: 0, "Invalid opaque message, null data")
return
}

AppDependencies.signalCallManager
.receivedOpaqueMessage(
OpaqueMessageMetadata(
senderServiceId.rawUuid,
data,
metadata.sourceDeviceId,
messageAgeSeconds
)
val messageAgeSeconds = if (
envelope.serverTimestamp in 1..serverDeliveredTimestamp
) {
(serverDeliveredTimestamp - envelope.serverTimestamp!!).milliseconds.inWholeSeconds
} else 0

AppDependencies.signalCallManager.receivedOpaqueMessage(
OpaqueMessageMetadata(
senderRecipient.requireAci().rawUuid,
data,
metadata.sourceDeviceId,
messageAgeSeconds
)
)
}
}
}