mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 04:58:45 +00:00
Fix call requests to a PNI.
This commit is contained in:
committed by
jeffrey-signal
parent
55617c18f0
commit
76e92f29b9
@@ -56,6 +56,19 @@ public class ProfileKeySendJob extends BaseJob {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suitable for a 1:1 conversation or a GV1 group only.
|
||||
*
|
||||
* @param queueLimits True if you only want one of these to be run per person after decryptions
|
||||
* are drained, otherwise false.
|
||||
*
|
||||
* @return The job that is created, or null if the threadId provided was invalid.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static @Nullable ProfileKeySendJob create(@NonNull Recipient recipient, boolean queueLimits) {
|
||||
return create(SignalDatabase.threads().getOrCreateThreadIdFor(recipient), queueLimits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Suitable for a 1:1 conversation or a GV1 group only.
|
||||
*
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package org.thoughtcrime.securesms.messages
|
||||
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.ringrtc.CallId
|
||||
import org.thoughtcrime.securesms.database.model.IdentityRecord
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.ProfileKeySendJob
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.log
|
||||
import org.thoughtcrime.securesms.messages.MessageContentProcessor.Companion.warn
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.AnswerMetadata
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData.CallMetadata
|
||||
@@ -36,6 +39,21 @@ object CallMessageProcessor {
|
||||
) {
|
||||
val callMessage = content.callMessage!!
|
||||
|
||||
if (metadata.destinationServiceId is ServiceId.PNI) {
|
||||
if (RecipientUtil.isCallRequestAccepted(senderRecipient) && callMessage.offer != null) {
|
||||
log(envelope.timestamp!!, "Received call offer message at our PNI from trusted sender, responding with profile and pni signature")
|
||||
RecipientUtil.shareProfileIfFirstSecureMessage(senderRecipient)
|
||||
ProfileKeySendJob.create(senderRecipient, false)?.let { AppDependencies.jobManager.add(it) }
|
||||
}
|
||||
|
||||
if (callMessage.offer != null) {
|
||||
log(envelope.timestamp!!, "Call message at our PNI is an offer, continuing.")
|
||||
} else {
|
||||
log(envelope.timestamp!!, "Call message at our PNI is not an offer, ignoring.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
callMessage.offer != null -> handleCallOfferMessage(envelope, metadata, callMessage.offer!!, senderRecipient.id, serverDeliveredTimestamp)
|
||||
callMessage.answer != null -> handleCallAnswerMessage(envelope, metadata, callMessage.answer!!, senderRecipient.id)
|
||||
@@ -57,13 +75,14 @@ object CallMessageProcessor {
|
||||
}
|
||||
|
||||
val remotePeer = RemotePeer(senderRecipientId, CallId(offerId))
|
||||
val remoteIdentityKey = AppDependencies.protocolStore.aci().identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.get()
|
||||
val remoteIdentityKey = AppDependencies.protocolStore.get(metadata.destinationServiceId).identities().getIdentityRecord(senderRecipientId).map { (_, identityKey): IdentityRecord -> identityKey.serialize() }.orNull()
|
||||
|
||||
AppDependencies.signalCallManager
|
||||
.receivedOffer(
|
||||
CallMetadata(remotePeer, metadata.sourceDeviceId),
|
||||
OfferMetadata(offer.opaque?.toByteArray(), OfferMessage.Type.fromProto(offer.type!!)),
|
||||
ReceivedOfferMetadata(
|
||||
metadata.destinationServiceId,
|
||||
remoteIdentityKey,
|
||||
envelope.serverTimestamp!!,
|
||||
serverDeliveredTimestamp
|
||||
|
||||
@@ -146,7 +146,7 @@ object MessageDecryptor {
|
||||
}
|
||||
|
||||
val bufferedStore = bufferedProtocolStore.get(destination)
|
||||
val localAddress = SignalServiceAddress(selfAci, SignalStore.account.e164)
|
||||
val localAddress = SignalServiceAddress(destination, SignalStore.account.e164)
|
||||
val cipher = SignalServiceCipher(localAddress, SignalStore.account.deviceId, bufferedStore, ReentrantSessionLock.INSTANCE, SealedSenderAccessUtil.getCertificateValidator())
|
||||
|
||||
return try {
|
||||
|
||||
@@ -4,15 +4,22 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.ringrtc.CallException;
|
||||
import org.signal.ringrtc.CallId;
|
||||
import org.signal.ringrtc.CallManager;
|
||||
import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink;
|
||||
import org.thoughtcrime.securesms.database.CallTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.events.CallParticipant;
|
||||
import org.thoughtcrime.securesms.events.CallParticipantId;
|
||||
import org.thoughtcrime.securesms.events.WebRtcViewModel;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_INCOMING_CONNECTING;
|
||||
|
||||
@@ -31,6 +38,38 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor {
|
||||
@NonNull RemotePeer remotePeer,
|
||||
@NonNull OfferMessage.Type offerType)
|
||||
{
|
||||
if (!remotePeer.getRecipient().getHasAci()) {
|
||||
Log.w(tag, "1:1 outgoing recipient is PNI only, send pseudo-call offer and terminate call");
|
||||
|
||||
remotePeer.setCallId(new CallId(ByteBuffer.wrap(Util.getSecretBytes(8)).getLong()));
|
||||
|
||||
currentState = currentState.builder()
|
||||
.actionProcessor(new IdleActionProcessor(webRtcInteractor))
|
||||
.changeCallInfoState()
|
||||
.callRecipient(remotePeer.getRecipient())
|
||||
.callState(WebRtcViewModel.State.CALL_NEEDS_PERMISSION)
|
||||
.putParticipant(remotePeer.getRecipient(), CallParticipant.EMPTY)
|
||||
.build();
|
||||
|
||||
boolean isVideoOffer = OfferMessage.Type.VIDEO_CALL == offerType;
|
||||
|
||||
SignalDatabase.calls().insertOneToOneCall(remotePeer.getCallId().longValue(),
|
||||
System.currentTimeMillis(),
|
||||
remotePeer.getId(),
|
||||
isVideoOffer ? CallTable.Type.VIDEO_CALL : CallTable.Type.AUDIO_CALL,
|
||||
CallTable.Direction.OUTGOING,
|
||||
CallTable.Event.ONGOING);
|
||||
|
||||
webRtcInteractor.insertMissedCall(remotePeer, System.currentTimeMillis(), isVideoOffer, CallTable.Event.NOT_ACCEPTED);
|
||||
webRtcInteractor.postStateUpdate(currentState);
|
||||
webRtcInteractor.sendCallMessage(remotePeer, SignalServiceCallMessage.forOffer(new OfferMessage(remotePeer.getCallId().longValue(),
|
||||
offerType,
|
||||
new byte[0]),
|
||||
null));
|
||||
|
||||
return terminate(currentState, remotePeer);
|
||||
}
|
||||
|
||||
remotePeer.setCallStartTimestamp(System.currentTimeMillis());
|
||||
|
||||
currentState = currentState.builder()
|
||||
@@ -60,7 +99,6 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor {
|
||||
.build();
|
||||
|
||||
CallManager.CallMediaType callMediaType = WebRtcUtil.getCallMediaTypeFromOfferType(offerType);
|
||||
|
||||
try {
|
||||
webRtcInteractor.getCallManager().call(remotePeer, callMediaType, SignalStore.account().getDeviceId());
|
||||
} catch (CallException e) {
|
||||
|
||||
@@ -49,12 +49,12 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
|
||||
this.actionProcessorFactory = actionProcessorFactory;
|
||||
}
|
||||
|
||||
protected @NonNull WebRtcServiceState handleReceivedOffer(@NonNull WebRtcServiceState currentState,
|
||||
@NonNull WebRtcData.CallMetadata callMetadata,
|
||||
@NonNull WebRtcData.OfferMetadata offerMetadata,
|
||||
@NonNull WebRtcData.ReceivedOfferMetadata receivedOfferMetadata)
|
||||
protected @NonNull WebRtcServiceState handleValidatedReceivedOffer(@NonNull WebRtcServiceState currentState,
|
||||
@NonNull WebRtcData.CallMetadata callMetadata,
|
||||
@NonNull WebRtcData.OfferMetadata offerMetadata,
|
||||
@NonNull WebRtcData.ReceivedOfferMetadata receivedOfferMetadata)
|
||||
{
|
||||
Log.i(tag, "handleReceivedOffer(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice()));
|
||||
Log.i(tag, "handleValidatedReceivedOffer(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice()));
|
||||
|
||||
Log.i(tag, "In a group call, send busy back to 1:1 call offer.");
|
||||
currentState.getActionProcessor().handleSendBusy(currentState, callMetadata, true);
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -167,16 +168,20 @@ public abstract class WebRtcActionProcessor {
|
||||
|
||||
//region Incoming call
|
||||
|
||||
protected @NonNull WebRtcServiceState handleReceivedOffer(@NonNull WebRtcServiceState currentState,
|
||||
@NonNull CallMetadata callMetadata,
|
||||
@NonNull OfferMetadata offerMetadata,
|
||||
@NonNull ReceivedOfferMetadata receivedOfferMetadata)
|
||||
protected final @NonNull WebRtcServiceState handleReceivedOffer(@NonNull WebRtcServiceState currentState,
|
||||
@NonNull CallMetadata callMetadata,
|
||||
@NonNull OfferMetadata offerMetadata,
|
||||
@NonNull ReceivedOfferMetadata receivedOfferMetadata)
|
||||
{
|
||||
Log.i(tag, "handleReceivedOffer(): id: " + callMetadata.getCallId().format(callMetadata.getRemoteDevice()) + " offer_type:" + offerMetadata.getOfferType());
|
||||
|
||||
if (TelephonyUtil.isAnyPstnLineBusy(context)) {
|
||||
Log.i(tag, "PSTN line is busy.");
|
||||
currentState = currentState.getActionProcessor().handleSendBusy(currentState, callMetadata, true);
|
||||
if (receivedOfferMetadata.getDestinationServiceId() instanceof ServiceId.PNI) {
|
||||
if (RecipientUtil.isCallRequestAccepted(callMetadata.getRemotePeer().getRecipient())) {
|
||||
Log.i(tag, "Caller is trusted but called our PNI, insert missed call and send hangup as we can't proceed.");
|
||||
currentState = currentState.getActionProcessor().handleSendHangup(currentState, callMetadata, WebRtcData.HangupMetadata.fromType(HangupMessage.Type.NORMAL), true);
|
||||
} else {
|
||||
Log.i(tag, "Caller is untrusted but called our PNI, insert missed call and do not send hangup.");
|
||||
}
|
||||
webRtcInteractor.insertMissedCall(callMetadata.getRemotePeer(), receivedOfferMetadata.getServerReceivedTimestamp(), offerMetadata.getOfferType() == OfferMessage.Type.VIDEO_CALL);
|
||||
return currentState;
|
||||
}
|
||||
@@ -188,6 +193,13 @@ public abstract class WebRtcActionProcessor {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
if (TelephonyUtil.isAnyPstnLineBusy(context)) {
|
||||
Log.i(tag, "PSTN line is busy.");
|
||||
currentState = currentState.getActionProcessor().handleSendBusy(currentState, callMetadata, true);
|
||||
webRtcInteractor.insertMissedCall(callMetadata.getRemotePeer(), receivedOfferMetadata.getServerReceivedTimestamp(), offerMetadata.getOfferType() == OfferMessage.Type.VIDEO_CALL);
|
||||
return currentState;
|
||||
}
|
||||
|
||||
if (offerMetadata.getOpaque() == null) {
|
||||
Log.w(tag, "Opaque data is required.");
|
||||
currentState = currentState.getActionProcessor().handleSendHangup(currentState, callMetadata, WebRtcData.HangupMetadata.fromType(HangupMessage.Type.NORMAL), true);
|
||||
@@ -195,6 +207,13 @@ public abstract class WebRtcActionProcessor {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
if (receivedOfferMetadata.getRemoteIdentityKey() == null) {
|
||||
Log.w(tag, "Unable to locate remote identity key for caller, bailing");
|
||||
currentState = currentState.getActionProcessor().handleSendHangup(currentState, callMetadata, WebRtcData.HangupMetadata.fromType(HangupMessage.Type.NORMAL), true);
|
||||
webRtcInteractor.insertMissedCall(callMetadata.getRemotePeer(), receivedOfferMetadata.getServerReceivedTimestamp(), offerMetadata.getOfferType() == OfferMessage.Type.VIDEO_CALL);
|
||||
return currentState;
|
||||
}
|
||||
|
||||
NotificationProfile activeProfile = NotificationProfiles.getActiveProfile(SignalDatabase.notificationProfiles().getProfiles());
|
||||
if (activeProfile != null && !(activeProfile.isRecipientAllowed(callMetadata.getRemotePeer().getId()) || activeProfile.getAllowAllCalls())) {
|
||||
Log.w(tag, "Caller is excluded by notification profile.");
|
||||
@@ -202,7 +221,15 @@ public abstract class WebRtcActionProcessor {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
Log.i(tag, "add remotePeer callId: " + callMetadata.getRemotePeer().getCallId() + " key: " + callMetadata.getRemotePeer().hashCode());
|
||||
return handleValidatedReceivedOffer(currentState, callMetadata, offerMetadata, receivedOfferMetadata);
|
||||
}
|
||||
|
||||
protected @NonNull WebRtcServiceState handleValidatedReceivedOffer(@NonNull WebRtcServiceState currentState,
|
||||
@NonNull CallMetadata callMetadata,
|
||||
@NonNull OfferMetadata offerMetadata,
|
||||
@NonNull ReceivedOfferMetadata receivedOfferMetadata)
|
||||
{
|
||||
Log.i(tag, "handleValidatedReceivedOffer(): add remotePeer callId: " + callMetadata.getRemotePeer().getCallId() + " key: " + callMetadata.getRemotePeer().hashCode());
|
||||
|
||||
callMetadata.getRemotePeer().setCallStartTimestamp(receivedOfferMetadata.getServerReceivedTimestamp());
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.signal.ringrtc.CallManager;
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
|
||||
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -66,17 +67,27 @@ public class WebRtcData {
|
||||
* Additional metadata for a received call.
|
||||
*/
|
||||
public static class ReceivedOfferMetadata {
|
||||
private final @NonNull byte[] remoteIdentityKey;
|
||||
private final long serverReceivedTimestamp;
|
||||
private final long serverDeliveredTimestamp;
|
||||
private final @NonNull ServiceId destinationServiceId;
|
||||
private final @Nullable byte[] remoteIdentityKey;
|
||||
private final long serverReceivedTimestamp;
|
||||
private final long serverDeliveredTimestamp;
|
||||
|
||||
public ReceivedOfferMetadata(@NonNull byte[] remoteIdentityKey, long serverReceivedTimestamp, long serverDeliveredTimestamp) {
|
||||
public ReceivedOfferMetadata(@NonNull ServiceId destinationServiceId,
|
||||
@Nullable byte[] remoteIdentityKey,
|
||||
long serverReceivedTimestamp,
|
||||
long serverDeliveredTimestamp)
|
||||
{
|
||||
this.destinationServiceId = destinationServiceId;
|
||||
this.remoteIdentityKey = remoteIdentityKey;
|
||||
this.serverReceivedTimestamp = serverReceivedTimestamp;
|
||||
this.serverDeliveredTimestamp = serverDeliveredTimestamp;
|
||||
}
|
||||
|
||||
@NonNull byte[] getRemoteIdentityKey() {
|
||||
@NonNull ServiceId getDestinationServiceId() {
|
||||
return destinationServiceId;
|
||||
}
|
||||
|
||||
@Nullable byte[] getRemoteIdentityKey() {
|
||||
return remoteIdentityKey;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user