From 26e582d806710604add3cc8b136954e0fd81afbd Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 8 May 2020 14:39:32 -0300 Subject: [PATCH] Integrate RingRTC v2.0.1 --- app/build.gradle | 2 +- .../securesms/WebRtcCallActivity.java | 40 +- .../components/webrtc/WebRtcCallView.java | 20 + .../securesms/events/WebRtcViewModel.java | 5 + .../securesms/jobs/PushProcessMessageJob.java | 24 +- .../securesms/service/WebRtcCallService.java | 367 +++++++++++------- app/src/main/res/values/strings.xml | 3 + app/witness-verifications.gradle | 4 +- .../api/SignalServiceMessageSender.java | 21 +- .../api/messages/SignalServiceContent.java | 15 +- .../api/messages/calls/HangupMessage.java | 69 +++- .../calls/SignalServiceCallMessage.java | 112 ++++-- .../src/main/proto/SignalService.proto | 25 +- 13 files changed, 493 insertions(+), 214 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5da524da0c..e0467e2682 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -302,7 +302,7 @@ dependencies { implementation 'org.signal:argon2:13.1@aar' - implementation 'org.signal:ringrtc-android:1.3.2' + implementation 'org.signal:ringrtc-android:2.0.1' implementation "me.leolin:ShortcutBadger:1.1.16" implementation 'se.emilsjolander:stickylistheaders:2.7.0' diff --git a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java index 28f5ec17bd..77b6a5082c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/WebRtcCallActivity.java @@ -35,6 +35,7 @@ import android.view.Window; import android.view.WindowManager; import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.AppCompatTextView; @@ -63,6 +64,7 @@ import org.thoughtcrime.securesms.util.ViewUtil; import org.webrtc.SurfaceViewRenderer; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.signalservice.api.messages.calls.HangupMessage; import static org.whispersystems.libsignal.SessionCipher.SESSION_LOCK; @@ -379,11 +381,12 @@ public class WebRtcCallActivity extends AppCompatActivity { callScreen.setStatus(getString(R.string.WebRtcCallActivity__calling)); } - private void handleTerminate(@NonNull Recipient recipient, @NonNull SurfaceViewRenderer localRenderer /*, int terminationType */) { - Log.i(TAG, "handleTerminate called"); + private void handleTerminate(@NonNull Recipient recipient, @NonNull HangupMessage.Type hangupType) { + Log.i(TAG, "handleTerminate called: " + hangupType.name()); callScreen.setRecipient(recipient); - callScreen.setStatus(getString(R.string.RedPhone_ending_call)); + callScreen.setStatusFromHangupType(hangupType); + EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); delayedFinish(); @@ -431,13 +434,13 @@ public class WebRtcCallActivity extends AppCompatActivity { dialog.setPositiveButton(R.string.RedPhone_got_it, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - WebRtcCallActivity.this.handleTerminate(event.getRecipient(), event.getLocalRenderer()); + WebRtcCallActivity.this.handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL); } }); dialog.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - WebRtcCallActivity.this.handleTerminate(event.getRecipient(), event.getLocalRenderer()); + WebRtcCallActivity.this.handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL); } }); dialog.show(); @@ -448,7 +451,7 @@ public class WebRtcCallActivity extends AppCompatActivity { final Recipient recipient = event.getRecipient(); if (theirKey == null) { - handleTerminate(recipient, event.getLocalRenderer()); + handleTerminate(recipient, HangupMessage.Type.NORMAL); } String name = recipient.getDisplayName(this); @@ -479,7 +482,7 @@ public class WebRtcCallActivity extends AppCompatActivity { }) .setNegativeButton(R.string.WebRtcCallScreen_end_call, (d, w) -> { d.dismiss(); - handleTerminate(recipient, event.getLocalRenderer()); + handleTerminate(recipient, HangupMessage.Type.NORMAL); }) .show(); } @@ -505,16 +508,19 @@ public class WebRtcCallActivity extends AppCompatActivity { viewModel.setRecipient(event.getRecipient()); switch (event.getState()) { - case CALL_CONNECTED: handleCallConnected(event); break; - case NETWORK_FAILURE: handleServerFailure(event); break; - case CALL_RINGING: handleCallRinging(event); break; - case CALL_DISCONNECTED: handleTerminate(event.getRecipient(), event.getLocalRenderer()); break; - case NO_SUCH_USER: handleNoSuchUser(event); break; - case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break; - case CALL_INCOMING: handleIncomingCall(event); break; - case CALL_OUTGOING: handleOutgoingCall(event); break; - case CALL_BUSY: handleCallBusy(event); break; - case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break; + case CALL_CONNECTED: handleCallConnected(event); break; + case NETWORK_FAILURE: handleServerFailure(event); break; + case CALL_RINGING: handleCallRinging(event); break; + case CALL_DISCONNECTED: handleTerminate(event.getRecipient(), HangupMessage.Type.NORMAL); break; + case CALL_ACCEPTED_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.ACCEPTED); break; + case CALL_DECLINED_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.DECLINED); break; + case CALL_ONGOING_ELSEWHERE: handleTerminate(event.getRecipient(), HangupMessage.Type.BUSY); break; + case NO_SUCH_USER: handleNoSuchUser(event); break; + case RECIPIENT_UNAVAILABLE: handleRecipientUnavailable(event); break; + case CALL_INCOMING: handleIncomingCall(event); break; + case CALL_OUTGOING: handleOutgoingCall(event); break; + case CALL_BUSY: handleCallBusy(event); break; + case UNTRUSTED_IDENTITY: handleUntrustedIdentity(event); break; } callScreen.setLocalRenderer(event.getLocalRenderer()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index d18c2f16a5..ec77f57180 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.util.AvatarUtil; import org.thoughtcrime.securesms.util.ViewUtil; import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; +import org.whispersystems.signalservice.api.messages.calls.HangupMessage; public class WebRtcCallView extends FrameLayout { @@ -290,6 +291,25 @@ public class WebRtcCallView extends FrameLayout { this.status.setText(status); } + public void setStatusFromHangupType(@NonNull HangupMessage.Type hangupType) { + switch (hangupType) { + case NORMAL: + status.setText(R.string.RedPhone_ending_call); + break; + case ACCEPTED: + status.setText(R.string.WebRtcCallActivity__answered_on_a_linked_device); + break; + case DECLINED: + status.setText(R.string.WebRtcCallActivity__declined_on_a_linked_device); + break; + case BUSY: + status.setText(R.string.WebRtcCallActivity__busy_on_a_linked_device); + break; + default: + throw new IllegalStateException("Unknown hangup type: " + hangupType); + } + } + public void setWebRtcControls(WebRtcControls webRtcControls) { answerWithVoiceGroup.setVisibility(View.GONE); diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java index f5944719db..5785045c4e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/events/WebRtcViewModel.java @@ -24,6 +24,11 @@ public class WebRtcViewModel { RECIPIENT_UNAVAILABLE, NO_SUCH_USER, UNTRUSTED_IDENTITY, + + // Multiring Hangup States + CALL_ACCEPTED_ELSEWHERE, + CALL_DECLINED_ELSEWHERE, + CALL_ONGOING_ELSEWHERE } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index 3c02645124..f8c63b3e7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -350,7 +350,14 @@ public final class PushProcessMessageJob extends BaseJob { else Log.w(TAG, "Contains no known sync types..."); } else if (content.getCallMessage().isPresent()) { Log.i(TAG, "Got call message..."); - SignalServiceCallMessage message = content.getCallMessage().get(); + + SignalServiceCallMessage message = content.getCallMessage().get(); + Optional destinationDeviceId = message.getDestinationDeviceId(); + + if (destinationDeviceId.isPresent() && destinationDeviceId.get() != 1) { + Log.i(TAG, String.format(Locale.US, "Ignoring call message that is not for this device! intended: %d, this: %d", destinationDeviceId.get(), 1)); + return; + } if (message.getOfferMessage().isPresent()) handleCallOfferMessage(content, message.getOfferMessage().get(), smsMessageId); else if (message.getAnswerMessage().isPresent()) handleCallAnswerMessage(content, message.getAnswerMessage().get()); @@ -454,7 +461,8 @@ public final class PushProcessMessageJob extends BaseJob { .putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice()) .putExtra(WebRtcCallService.EXTRA_OFFER_DESCRIPTION, message.getDescription()) .putExtra(WebRtcCallService.EXTRA_TIMESTAMP, content.getTimestamp()) - .putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, message.getType().getCode()); + .putExtra(WebRtcCallService.EXTRA_OFFER_TYPE, message.getType().getCode()) + .putExtra(WebRtcCallService.EXTRA_MULTI_RING, content.getCallMessage().get().isMultiRing()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) context.startForegroundService(intent); else context.startService(intent); @@ -472,7 +480,8 @@ public final class PushProcessMessageJob extends BaseJob { .putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()) .putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, remotePeer) .putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice()) - .putExtra(WebRtcCallService.EXTRA_ANSWER_DESCRIPTION, message.getDescription()); + .putExtra(WebRtcCallService.EXTRA_ANSWER_DESCRIPTION, message.getDescription()) + .putExtra(WebRtcCallService.EXTRA_MULTI_RING, content.getCallMessage().get().isMultiRing()); context.startService(intent); } @@ -513,9 +522,12 @@ public final class PushProcessMessageJob extends BaseJob { RemotePeer remotePeer = new RemotePeer(Recipient.externalPush(context, content.getSender()).getId()); intent.setAction(WebRtcCallService.ACTION_RECEIVE_HANGUP) - .putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()) - .putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, remotePeer) - .putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice()); + .putExtra(WebRtcCallService.EXTRA_CALL_ID, message.getId()) + .putExtra(WebRtcCallService.EXTRA_REMOTE_PEER, remotePeer) + .putExtra(WebRtcCallService.EXTRA_REMOTE_DEVICE, content.getSenderDevice()) + .putExtra(WebRtcCallService.EXTRA_HANGUP_IS_LEGACY, message.isLegacy()) + .putExtra(WebRtcCallService.EXTRA_HANGUP_DEVICE_ID, message.getDeviceId()) + .putExtra(WebRtcCallService.EXTRA_HANGUP_TYPE, message.getType().getCode()); context.startService(intent); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java index 7219357f5c..67bc4b6352 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -10,19 +10,19 @@ import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.os.ResultReceiver; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Pair; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.greenrobot.eventbus.EventBus; import org.signal.ringrtc.CallException; import org.signal.ringrtc.CallId; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.CallManager.CallEvent; import org.signal.ringrtc.Remote; - -import org.greenrobot.eventbus.EventBus; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.WebRtcCallActivity; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; @@ -73,7 +73,6 @@ import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; -import java.lang.Thread; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; @@ -107,51 +106,58 @@ public class WebRtcCallService extends Service implements CallManager.Observer, public static final String EXTRA_REMOTE_DEVICE = "remote_device"; public static final String EXTRA_OFFER_DESCRIPTION = "offer_description"; public static final String EXTRA_OFFER_TYPE = "offer_type"; + public static final String EXTRA_MULTI_RING = "multi_ring"; + public static final String EXTRA_HANGUP_TYPE = "hangup_type"; + public static final String EXTRA_HANGUP_IS_LEGACY = "hangup_is_legacy"; + public static final String EXTRA_HANGUP_DEVICE_ID = "hangup_device_id"; public static final String EXTRA_ANSWER_DESCRIPTION = "answer_description"; public static final String EXTRA_ICE_CANDIDATES = "ice_candidates"; public static final String EXTRA_ENABLE = "enable_value"; public static final String EXTRA_BROADCAST = "broadcast"; public static final String EXTRA_ANSWER_WITH_VIDEO = "enable_video"; - public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING"; - public static final String ACTION_DENY_CALL = "DENY_CALL"; - public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; - public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"; - public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA"; - public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"; - public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"; - public static final String ACTION_SCREEN_OFF = "SCREEN_OFF"; - public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL"; - public static final String ACTION_SET_AUDIO_SPEAKER = "SET_AUDIO_SPEAKER"; - public static final String ACTION_SET_AUDIO_BLUETOOTH = "SET_AUDIO_BLUETOOTH"; - public static final String ACTION_CALL_CONNECTED = "CALL_CONNECTED"; - public static final String ACTION_START_OUTGOING_CALL = "START_OUTGOING_CALL"; - public static final String ACTION_START_INCOMING_CALL = "START_INCOMING_CALL"; - public static final String ACTION_LOCAL_RINGING = "LOCAL_RINGING"; - public static final String ACTION_REMOTE_RINGING = "REMOTE_RINGING"; - public static final String ACTION_ACCEPT_CALL = "ACCEPT_CALL"; - public static final String ACTION_SEND_OFFER = "SEND_OFFER"; - public static final String ACTION_SEND_ANSWER = "SEND_ANSWER"; - public static final String ACTION_SEND_ICE_CANDIDATES = "SEND_ICE_CANDIDATES"; - public static final String ACTION_SEND_HANGUP = "SEND_HANGUP"; - public static final String ACTION_SEND_BUSY = "SEND_BUSY"; - public static final String ACTION_RECEIVE_OFFER = "RECEIVE_OFFER"; - public static final String ACTION_RECEIVE_ANSWER = "RECEIVE_ANSWER"; - public static final String ACTION_RECEIVE_ICE_CANDIDATES = "RECEIVE_ICE_CANDIDATES"; - public static final String ACTION_RECEIVE_HANGUP = "RECEIVE_HANGUP"; - public static final String ACTION_RECEIVE_BUSY = "RECEIVE_BUSY"; - public static final String ACTION_REMOTE_VIDEO_ENABLE = "REMOTE_VIDEO_ENABLE"; - public static final String ACTION_SET_ENABLE_VIDEO = "SET_ENABLE_VIDEO"; - public static final String ACTION_ENDED_REMOTE_HANGUP = "ENDED_REMOTE_HANGUP"; - public static final String ACTION_ENDED_REMOTE_BUSY = "ENDED_REMOTE_BUSY"; - public static final String ACTION_ENDED_REMOTE_GLARE = "ENDED_REMOTE_GLARE"; - public static final String ACTION_ENDED_TIMEOUT = "ENDED_TIMEOUT"; - public static final String ACTION_ENDED_INTERNAL_FAILURE = "ENDED_INTERNAL_FAILURE"; - public static final String ACTION_ENDED_SIGNALING_FAILURE = "ENDED_SIGNALING_FAILURE"; - public static final String ACTION_ENDED_CONNECTION_FAILURE = "ENDED_CONNECTION_FAILURE"; - public static final String ACTION_ENDED_RX_OFFER_EXPIRED = "ENDED_RX_OFFER_EXPIRED"; - public static final String ACTION_ENDED_RX_OFFER_WHILE_ACTIVE = "ENDED_RX_OFFER_WHILE_ACTIVE"; - public static final String ACTION_CALL_CONCLUDED = "CALL_CONCLUDED"; + public static final String ACTION_OUTGOING_CALL = "CALL_OUTGOING"; + public static final String ACTION_DENY_CALL = "DENY_CALL"; + public static final String ACTION_LOCAL_HANGUP = "LOCAL_HANGUP"; + public static final String ACTION_SET_MUTE_AUDIO = "SET_MUTE_AUDIO"; + public static final String ACTION_FLIP_CAMERA = "FLIP_CAMERA"; + public static final String ACTION_BLUETOOTH_CHANGE = "BLUETOOTH_CHANGE"; + public static final String ACTION_WIRED_HEADSET_CHANGE = "WIRED_HEADSET_CHANGE"; + public static final String ACTION_SCREEN_OFF = "SCREEN_OFF"; + public static final String ACTION_IS_IN_CALL_QUERY = "IS_IN_CALL"; + public static final String ACTION_SET_AUDIO_SPEAKER = "SET_AUDIO_SPEAKER"; + public static final String ACTION_SET_AUDIO_BLUETOOTH = "SET_AUDIO_BLUETOOTH"; + public static final String ACTION_CALL_CONNECTED = "CALL_CONNECTED"; + public static final String ACTION_START_OUTGOING_CALL = "START_OUTGOING_CALL"; + public static final String ACTION_START_INCOMING_CALL = "START_INCOMING_CALL"; + public static final String ACTION_LOCAL_RINGING = "LOCAL_RINGING"; + public static final String ACTION_REMOTE_RINGING = "REMOTE_RINGING"; + public static final String ACTION_ACCEPT_CALL = "ACCEPT_CALL"; + public static final String ACTION_SEND_OFFER = "SEND_OFFER"; + public static final String ACTION_SEND_ANSWER = "SEND_ANSWER"; + public static final String ACTION_SEND_ICE_CANDIDATES = "SEND_ICE_CANDIDATES"; + public static final String ACTION_SEND_HANGUP = "SEND_HANGUP"; + public static final String ACTION_SEND_BUSY = "SEND_BUSY"; + public static final String ACTION_RECEIVE_OFFER = "RECEIVE_OFFER"; + public static final String ACTION_RECEIVE_ANSWER = "RECEIVE_ANSWER"; + public static final String ACTION_RECEIVE_ICE_CANDIDATES = "RECEIVE_ICE_CANDIDATES"; + public static final String ACTION_RECEIVE_HANGUP = "RECEIVE_HANGUP"; + public static final String ACTION_RECEIVE_BUSY = "RECEIVE_BUSY"; + public static final String ACTION_REMOTE_VIDEO_ENABLE = "REMOTE_VIDEO_ENABLE"; + public static final String ACTION_SET_ENABLE_VIDEO = "SET_ENABLE_VIDEO"; + public static final String ACTION_ENDED_REMOTE_HANGUP = "ENDED_REMOTE_HANGUP"; + public static final String ACTION_ENDED_REMOTE_HANGUP_ACCEPTED = "ENDED_REMOTE_HANGUP_ACCEPTED"; + public static final String ACTION_ENDED_REMOTE_HANGUP_DECLINED = "ENDED_REMOTE_HANGUP_DECLINED"; + public static final String ACTION_ENDED_REMOTE_HANGUP_BUSY = "ENDED_REMOTE_HANGUP_BUSY"; + public static final String ACTION_ENDED_REMOTE_BUSY = "ENDED_REMOTE_BUSY"; + public static final String ACTION_ENDED_REMOTE_GLARE = "ENDED_REMOTE_GLARE"; + public static final String ACTION_ENDED_TIMEOUT = "ENDED_TIMEOUT"; + public static final String ACTION_ENDED_INTERNAL_FAILURE = "ENDED_INTERNAL_FAILURE"; + public static final String ACTION_ENDED_SIGNALING_FAILURE = "ENDED_SIGNALING_FAILURE"; + public static final String ACTION_ENDED_CONNECTION_FAILURE = "ENDED_CONNECTION_FAILURE"; + public static final String ACTION_ENDED_RX_OFFER_EXPIRED = "ENDED_RX_OFFER_EXPIRED"; + public static final String ACTION_ENDED_RX_OFFER_WHILE_ACTIVE = "ENDED_RX_OFFER_WHILE_ACTIVE"; + public static final String ACTION_CALL_CONCLUDED = "CALL_CONCLUDED"; private CameraState localCameraState = CameraState.UNKNOWN; private boolean microphoneEnabled = true; @@ -204,45 +210,48 @@ public class WebRtcCallService extends Service implements CallManager.Observer, if (intent == null || intent.getAction() == null) return START_NOT_STICKY; serviceExecutor.execute(() -> { - if (intent.getAction().equals(ACTION_RECEIVE_OFFER)) handleReceivedOffer(intent); - else if (intent.getAction().equals(ACTION_RECEIVE_BUSY)) handleReceivedBusy(intent); - else if (intent.getAction().equals(ACTION_OUTGOING_CALL) && isIdle()) handleOutgoingCall(intent); - else if (intent.getAction().equals(ACTION_DENY_CALL)) handleDenyCall(intent); - else if (intent.getAction().equals(ACTION_LOCAL_HANGUP)) handleLocalHangup(intent); - else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent); - else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent); - else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent); - else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent); - else if (intent.getAction().equals(ACTION_SCREEN_OFF)) handleScreenOffChange(intent); - else if (intent.getAction().equals(ACTION_CALL_CONNECTED)) handleCallConnected(intent); - else if (intent.getAction().equals(ACTION_IS_IN_CALL_QUERY)) handleIsInCallQuery(intent); - else if (intent.getAction().equals(ACTION_SET_AUDIO_SPEAKER)) handleSetSpeakerAudio(intent); - else if (intent.getAction().equals(ACTION_SET_AUDIO_BLUETOOTH)) handleSetBluetoothAudio(intent); - else if (intent.getAction().equals(ACTION_START_OUTGOING_CALL)) handleStartOutgoingCall(intent); - else if (intent.getAction().equals(ACTION_START_INCOMING_CALL)) handleStartIncomingCall(intent); - else if (intent.getAction().equals(ACTION_ACCEPT_CALL)) handleAcceptCall(intent); - else if (intent.getAction().equals(ACTION_LOCAL_RINGING)) handleLocalRinging(intent); - else if (intent.getAction().equals(ACTION_REMOTE_RINGING)) handleRemoteRinging(intent); - else if (intent.getAction().equals(ACTION_SEND_OFFER)) handleSendOffer(intent); - else if (intent.getAction().equals(ACTION_SEND_ANSWER)) handleSendAnswer(intent); - else if (intent.getAction().equals(ACTION_SEND_ICE_CANDIDATES)) handleSendIceCandidates(intent); - else if (intent.getAction().equals(ACTION_SEND_HANGUP)) handleSendHangup(intent); - else if (intent.getAction().equals(ACTION_SEND_BUSY)) handleSendBusy(intent); - else if (intent.getAction().equals(ACTION_RECEIVE_ANSWER)) handleReceivedAnswer(intent); - else if (intent.getAction().equals(ACTION_RECEIVE_ICE_CANDIDATES)) handleReceivedIceCandidates(intent); - else if (intent.getAction().equals(ACTION_RECEIVE_HANGUP)) handleReceivedHangup(intent); - else if (intent.getAction().equals(ACTION_REMOTE_VIDEO_ENABLE)) handleRemoteVideoEnable(intent); - else if (intent.getAction().equals(ACTION_SET_ENABLE_VIDEO)) handleSetEnableVideo(intent); - else if (intent.getAction().equals(ACTION_ENDED_REMOTE_HANGUP)) handleEndedRemoteHangup(intent); - else if (intent.getAction().equals(ACTION_ENDED_REMOTE_BUSY)) handleEndedRemoteBusy(intent); - else if (intent.getAction().equals(ACTION_ENDED_REMOTE_GLARE)) handleEndedRemoteGlare(intent); - else if (intent.getAction().equals(ACTION_ENDED_TIMEOUT)) handleEndedTimeout(intent); - else if (intent.getAction().equals(ACTION_ENDED_INTERNAL_FAILURE)) handleEndedInternalFailure(intent); - else if (intent.getAction().equals(ACTION_ENDED_SIGNALING_FAILURE)) handleEndedSignalingFailure(intent); - else if (intent.getAction().equals(ACTION_ENDED_CONNECTION_FAILURE)) handleEndedConnectionFailure(intent); - else if (intent.getAction().equals(ACTION_ENDED_RX_OFFER_EXPIRED)) handleEndedReceivedOfferExpired(intent); - else if (intent.getAction().equals(ACTION_ENDED_RX_OFFER_WHILE_ACTIVE)) handleEndedReceivedOfferWhileActive(intent); - else if (intent.getAction().equals(ACTION_CALL_CONCLUDED)) handleCallConcluded(intent); + if (intent.getAction().equals(ACTION_RECEIVE_OFFER)) handleReceivedOffer(intent); + else if (intent.getAction().equals(ACTION_RECEIVE_BUSY)) handleReceivedBusy(intent); + else if (intent.getAction().equals(ACTION_OUTGOING_CALL) && isIdle()) handleOutgoingCall(intent); + else if (intent.getAction().equals(ACTION_DENY_CALL)) handleDenyCall(intent); + else if (intent.getAction().equals(ACTION_LOCAL_HANGUP)) handleLocalHangup(intent); + else if (intent.getAction().equals(ACTION_SET_MUTE_AUDIO)) handleSetMuteAudio(intent); + else if (intent.getAction().equals(ACTION_FLIP_CAMERA)) handleSetCameraFlip(intent); + else if (intent.getAction().equals(ACTION_BLUETOOTH_CHANGE)) handleBluetoothChange(intent); + else if (intent.getAction().equals(ACTION_WIRED_HEADSET_CHANGE)) handleWiredHeadsetChange(intent); + else if (intent.getAction().equals(ACTION_SCREEN_OFF)) handleScreenOffChange(intent); + else if (intent.getAction().equals(ACTION_CALL_CONNECTED)) handleCallConnected(intent); + else if (intent.getAction().equals(ACTION_IS_IN_CALL_QUERY)) handleIsInCallQuery(intent); + else if (intent.getAction().equals(ACTION_SET_AUDIO_SPEAKER)) handleSetSpeakerAudio(intent); + else if (intent.getAction().equals(ACTION_SET_AUDIO_BLUETOOTH)) handleSetBluetoothAudio(intent); + else if (intent.getAction().equals(ACTION_START_OUTGOING_CALL)) handleStartOutgoingCall(intent); + else if (intent.getAction().equals(ACTION_START_INCOMING_CALL)) handleStartIncomingCall(intent); + else if (intent.getAction().equals(ACTION_ACCEPT_CALL)) handleAcceptCall(intent); + else if (intent.getAction().equals(ACTION_LOCAL_RINGING)) handleLocalRinging(intent); + else if (intent.getAction().equals(ACTION_REMOTE_RINGING)) handleRemoteRinging(intent); + else if (intent.getAction().equals(ACTION_SEND_OFFER)) handleSendOffer(intent); + else if (intent.getAction().equals(ACTION_SEND_ANSWER)) handleSendAnswer(intent); + else if (intent.getAction().equals(ACTION_SEND_ICE_CANDIDATES)) handleSendIceCandidates(intent); + else if (intent.getAction().equals(ACTION_SEND_HANGUP)) handleSendHangup(intent); + else if (intent.getAction().equals(ACTION_SEND_BUSY)) handleSendBusy(intent); + else if (intent.getAction().equals(ACTION_RECEIVE_ANSWER)) handleReceivedAnswer(intent); + else if (intent.getAction().equals(ACTION_RECEIVE_ICE_CANDIDATES)) handleReceivedIceCandidates(intent); + else if (intent.getAction().equals(ACTION_RECEIVE_HANGUP)) handleReceivedHangup(intent); + else if (intent.getAction().equals(ACTION_REMOTE_VIDEO_ENABLE)) handleRemoteVideoEnable(intent); + else if (intent.getAction().equals(ACTION_SET_ENABLE_VIDEO)) handleSetEnableVideo(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_HANGUP)) handleEndedRemoteHangup(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_HANGUP_ACCEPTED)) handleEndedRemoteHangupAccepted(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_HANGUP_BUSY)) handleEndedRemoteHangupBusy(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_HANGUP_DECLINED)) handleEndedRemoteHangupDeclined(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_BUSY)) handleEndedRemoteBusy(intent); + else if (intent.getAction().equals(ACTION_ENDED_REMOTE_GLARE)) handleEndedRemoteGlare(intent); + else if (intent.getAction().equals(ACTION_ENDED_TIMEOUT)) handleEndedTimeout(intent); + else if (intent.getAction().equals(ACTION_ENDED_INTERNAL_FAILURE)) handleEndedInternalFailure(intent); + else if (intent.getAction().equals(ACTION_ENDED_SIGNALING_FAILURE)) handleEndedSignalingFailure(intent); + else if (intent.getAction().equals(ACTION_ENDED_CONNECTION_FAILURE)) handleEndedConnectionFailure(intent); + else if (intent.getAction().equals(ACTION_ENDED_RX_OFFER_EXPIRED)) handleEndedReceivedOfferExpired(intent); + else if (intent.getAction().equals(ACTION_ENDED_RX_OFFER_WHILE_ACTIVE)) handleEndedReceivedOfferWhileActive(intent); + else if (intent.getAction().equals(ACTION_CALL_CONCLUDED)) handleCallConcluded(intent); }); @@ -378,6 +387,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, String offer = intent.getStringExtra(EXTRA_OFFER_DESCRIPTION); Long timeStamp = intent.getLongExtra(EXTRA_TIMESTAMP, -1); OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE)); + boolean isMultiRing = intent.getBooleanExtra(EXTRA_MULTI_RING, false); Log.i(TAG, "handleReceivedOffer(): id: " + callId.format(remoteDevice)); @@ -399,8 +409,10 @@ public class WebRtcCallService extends Service implements CallManager.Observer, isRemoteVideoOffer = offerType == OfferMessage.Type.VIDEO_CALL; + CallManager.CallMediaType callType = getCallMediaTypeFromOfferType(offerType); + try { - callManager.receivedOffer(callId, remotePeer, remoteDevice, offer, timeStamp); + callManager.receivedOffer(callId, remotePeer, remoteDevice, offer, timeStamp, callType, isMultiRing, true); } catch (CallException e) { callFailure("Unable to process received offer: ", e); } @@ -417,8 +429,11 @@ public class WebRtcCallService extends Service implements CallManager.Observer, initializeVideo(); + OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE)); + CallManager.CallMediaType callMediaType = getCallMediaTypeFromOfferType(offerType); + try { - callManager.call(remotePeer); + callManager.call(remotePeer, callMediaType); } catch (CallException e) { callFailure("Unable to create outgoing call: ", e); } @@ -605,8 +620,10 @@ public class WebRtcCallService extends Service implements CallManager.Observer, camera, iceServers, isAlwaysTurn, + 1, deviceList, - enableVideoOnCreate); + enableVideoOnCreate, + true); } catch (CallException e) { callFailure("Unable to proceed with call: ", e); } @@ -637,7 +654,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); boolean hideIp = !activePeer.getRecipient().isSystemContact() || isAlwaysTurn; - LinkedList deviceList = new LinkedList(); + LinkedList deviceList = new LinkedList<>(); try { callManager.proceed(activePeer.getCallId(), @@ -648,8 +665,10 @@ public class WebRtcCallService extends Service implements CallManager.Observer, camera, iceServers, hideIp, + 1, deviceList, - false); + false, + true); } catch (CallException e) { callFailure("Unable to proceed with call: ", e); } @@ -686,7 +705,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, RemotePeer remotePeer = getRemotePeer(intent); CallId callId = getCallId(intent); Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - Boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); String offer = intent.getStringExtra(EXTRA_OFFER_DESCRIPTION); OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE)); @@ -697,32 +716,34 @@ public class WebRtcCallService extends Service implements CallManager.Observer, offerType = OfferMessage.Type.NEED_PERMISSION; } - OfferMessage offerMessage = new OfferMessage(callId.longValue(), offer, offerType); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOffer(offerMessage); + OfferMessage offerMessage = new OfferMessage(callId.longValue(), offer, offerType); + Integer destinationDeviceId = broadcast ? null : remoteDevice; + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forOffer(offerMessage, true, destinationDeviceId); - sendCallMessage(remotePeer, remoteDevice, broadcast, callMessage); + sendCallMessage(remotePeer, callMessage); } private void handleSendAnswer(Intent intent) { RemotePeer remotePeer = getRemotePeer(intent); CallId callId = getCallId(intent); Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - Boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); String answer = intent.getStringExtra(EXTRA_ANSWER_DESCRIPTION); Log.i(TAG, "handleSendAnswer: id: " + callId.format(remoteDevice)); - AnswerMessage answerMessage = new AnswerMessage(callId.longValue(), answer); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forAnswer(answerMessage); + AnswerMessage answerMessage = new AnswerMessage(callId.longValue(), answer); + Integer destinationDeviceId = broadcast ? null : remoteDevice; + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forAnswer(answerMessage, true, destinationDeviceId); - sendCallMessage(remotePeer, remoteDevice, broadcast, callMessage); + sendCallMessage(remotePeer, callMessage); } private void handleSendIceCandidates(Intent intent) { RemotePeer remotePeer = getRemotePeer(intent); CallId callId = getCallId(intent); Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - Boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); ArrayList iceCandidates = intent.getParcelableArrayListExtra(EXTRA_ICE_CANDIDATES); Log.i(TAG, "handleSendIceCandidates: id: " + callId.format(remoteDevice)); @@ -732,48 +753,55 @@ public class WebRtcCallService extends Service implements CallManager.Observer, iceUpdateMessages.add(parcel.getIceUpdateMessage(callId)); } - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forIceUpdates(iceUpdateMessages); + Integer destinationDeviceId = broadcast ? null : remoteDevice; + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forIceUpdates(iceUpdateMessages, true, destinationDeviceId); - sendCallMessage(remotePeer, remoteDevice, broadcast, callMessage); + sendCallMessage(remotePeer, callMessage); } private void handleSendHangup(Intent intent) { - RemotePeer remotePeer = getRemotePeer(intent); - CallId callId = getCallId(intent); - Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - Boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + RemotePeer remotePeer = getRemotePeer(intent); + CallId callId = getCallId(intent); + Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); + boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + HangupMessage.Type type = HangupMessage.Type.fromCode(intent.getStringExtra(EXTRA_HANGUP_TYPE)); + boolean isLegacy = intent.getBooleanExtra(EXTRA_HANGUP_IS_LEGACY, true); + int deviceId = intent.getIntExtra(EXTRA_HANGUP_DEVICE_ID, 0); Log.i(TAG, "handleSendHangup: id: " + callId.format(remoteDevice)); - HangupMessage hangupMessage = new HangupMessage(callId.longValue()); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forHangup(hangupMessage); + HangupMessage hangupMessage = new HangupMessage(callId.longValue(), type, deviceId, isLegacy); + Integer destinationDeviceId = broadcast ? null : remoteDevice; + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forHangup(hangupMessage, true, destinationDeviceId); - sendCallMessage(remotePeer, remoteDevice, broadcast, callMessage); + sendCallMessage(remotePeer, callMessage); } private void handleSendBusy(Intent intent) { RemotePeer remotePeer = getRemotePeer(intent); CallId callId = getCallId(intent); Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - Boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); + boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); Log.i(TAG, "handleSendBusy: id: " + callId.format(remoteDevice)); - BusyMessage busyMessage = new BusyMessage(callId.longValue()); - SignalServiceCallMessage callMessage = SignalServiceCallMessage.forBusy(busyMessage); + BusyMessage busyMessage = new BusyMessage(callId.longValue()); + Integer destinationDeviceId = broadcast ? null : remoteDevice; + SignalServiceCallMessage callMessage = SignalServiceCallMessage.forBusy(busyMessage, true, destinationDeviceId); - sendCallMessage(remotePeer, remoteDevice, broadcast, callMessage); + sendCallMessage(remotePeer, callMessage); } private void handleReceivedAnswer(Intent intent) { - CallId callId = getCallId(intent); - Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); - String description = intent.getStringExtra(EXTRA_ANSWER_DESCRIPTION); + CallId callId = getCallId(intent); + Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); + String description = intent.getStringExtra(EXTRA_ANSWER_DESCRIPTION); + boolean isMultiRing = intent.getBooleanExtra(EXTRA_MULTI_RING, false); Log.i(TAG, "handleReceivedAnswer(): id: " + callId.format(remoteDevice)); try { - callManager.receivedAnswer(callId, remoteDevice, description); + callManager.receivedAnswer(callId, remoteDevice, description , isMultiRing); } catch (CallException e) { callFailure("receivedAnswer() failed: ", e); } @@ -799,13 +827,16 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } private void handleReceivedHangup(Intent intent) { - CallId callId = getCallId(intent); - Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); + CallId callId = getCallId(intent); + Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); + HangupMessage.Type hangupType = HangupMessage.Type.fromCode(intent.getStringExtra(EXTRA_HANGUP_TYPE)); + Integer deviceId = intent.getIntExtra(EXTRA_HANGUP_DEVICE_ID, 0); + CallManager.HangupType callHangupType = getCallHangupTypeFromHangupType(hangupType); Log.i(TAG, "handleReceivedHangup(): id: " + callId.format(remoteDevice)); try { - callManager.receivedHangup(callId, remoteDevice); + callManager.receivedHangup(callId, remoteDevice, callHangupType, deviceId); } catch (CallException e) { callFailure("receivedHangup() failed: ", e); } @@ -1043,6 +1074,30 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } } + private void handleEndedRemoteHangupAccepted(Intent intent) { + RemotePeer remotePeer = getRemotePeer(intent); + + if (remotePeer.callIdEquals(activePeer)) { + sendMessage(WebRtcViewModel.State.CALL_ACCEPTED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); + } + } + + private void handleEndedRemoteHangupBusy(Intent intent) { + RemotePeer remotePeer = getRemotePeer(intent); + + if (remotePeer.callIdEquals(activePeer)) { + sendMessage(WebRtcViewModel.State.CALL_ONGOING_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); + } + } + + private void handleEndedRemoteHangupDeclined(Intent intent) { + RemotePeer remotePeer = getRemotePeer(intent); + + if (remotePeer.callIdEquals(activePeer)) { + sendMessage(WebRtcViewModel.State.CALL_DECLINED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); + } + } + private void delayedBusyFinish(CallId callId) { if (activePeer != null && callId.equals(activePeer.getCallId())) { Log.i(TAG, "delayedBusyFinish(): calling terminate()"); @@ -1268,11 +1323,11 @@ public class WebRtcCallService extends Service implements CallManager.Observer, this.startActivity(activityIntent); } - private @NonNull CallId getCallId(Intent intent) { + private static @NonNull CallId getCallId(Intent intent) { return new CallId(intent.getLongExtra(EXTRA_CALL_ID, -1)); } - private @NonNull RemotePeer getRemotePeer(Intent intent) { + private static @NonNull RemotePeer getRemotePeer(Intent intent) { RemotePeer remotePeer = intent.getParcelableExtra(EXTRA_REMOTE_PEER); if (remotePeer == null) throw new AssertionError("No RemotePeer in intent!"); @@ -1299,6 +1354,44 @@ public class WebRtcCallService extends Service implements CallManager.Observer, terminate(); } + private static @NonNull CallManager.CallMediaType getCallMediaTypeFromOfferType(@NonNull OfferMessage.Type offerType) { + return offerType == OfferMessage.Type.VIDEO_CALL ? CallManager.CallMediaType.VIDEO_CALL : CallManager.CallMediaType.AUDIO_CALL; + } + + private static @NonNull OfferMessage.Type getOfferTypeFromCallMediaType(@NonNull CallManager.CallMediaType mediaType) { + return mediaType == CallManager.CallMediaType.VIDEO_CALL ? OfferMessage.Type.VIDEO_CALL : OfferMessage.Type.AUDIO_CALL; + } + + private static @NonNull CallManager.HangupType getCallHangupTypeFromHangupType(@NonNull HangupMessage.Type hangupType) { + switch (hangupType) { + case ACCEPTED: + return CallManager.HangupType.ACCEPTED; + case BUSY: + return CallManager.HangupType.BUSY; + case NORMAL: + return CallManager.HangupType.NORMAL; + case DECLINED: + return CallManager.HangupType.DECLINED; + default: + throw new IllegalArgumentException("Unexpected hangup type: " + hangupType); + } + } + + private static @NonNull HangupMessage.Type getHangupTypeFromCallHangupType(@NonNull CallManager.HangupType hangupType) { + switch (hangupType) { + case ACCEPTED: + return HangupMessage.Type.ACCEPTED; + case BUSY: + return HangupMessage.Type.BUSY; + case NORMAL: + return HangupMessage.Type.NORMAL; + case DECLINED: + return HangupMessage.Type.DECLINED; + default: + throw new IllegalArgumentException("Unexpected hangup type: " + hangupType); + } + } + @Nullable @Override public IBinder onBind(Intent intent) { @@ -1530,9 +1623,9 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } } - private void sendCallMessage(RemotePeer remotePeer, Integer remoteDevice, Boolean broadcast, SignalServiceCallMessage callMessage) { + private void sendCallMessage(RemotePeer remotePeer, SignalServiceCallMessage callMessage) { ListenableFutureTask listenableFutureTask = sendMessage(remotePeer, callMessage); - listenableFutureTask.addListener(new SendCallMessageListener(remotePeer)); + listenableFutureTask.addListener(new SendCallMessageListener<>(remotePeer)); } private LockManager.PhoneState getInCallPhoneState() { @@ -1613,6 +1706,15 @@ public class WebRtcCallService extends Service implements CallManager.Observer, case ENDED_REMOTE_HANGUP: intent.setAction(ACTION_ENDED_REMOTE_HANGUP); break; + case ENDED_REMOTE_HANGUP_ACCEPTED: + intent.setAction(ACTION_ENDED_REMOTE_HANGUP_ACCEPTED); + break; + case ENDED_REMOTE_HANGUP_BUSY: + intent.setAction(ACTION_ENDED_REMOTE_HANGUP_BUSY); + break; + case ENDED_REMOTE_HANGUP_DECLINED: + intent.setAction(ACTION_ENDED_REMOTE_HANGUP_DECLINED); + break; case ENDED_REMOTE_BUSY: intent.setAction(ACTION_ENDED_REMOTE_BUSY); break; @@ -1639,6 +1741,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, break; case ENDED_LOCAL_HANGUP: case ENDED_APP_DROPPED_CALL: + case ENDED_IGNORE_CALLS_FROM_NON_MULTIRING_CALLERS: Log.i(TAG, "Ignoring event: " + event); return; default: @@ -1667,11 +1770,12 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } @Override - public void onSendOffer(CallId callId, Remote remote, Integer remoteDevice, Boolean broadcast, String offer) { - Log.i(TAG, "onSendOffer: id: " + callId.format(remoteDevice)); + public void onSendOffer(CallId callId, Remote remote, Integer remoteDevice, Boolean broadcast, String offer, CallManager.CallMediaType callMediaType) { + Log.i(TAG, "onSendOffer: id: " + callId.format(remoteDevice) + " type: " + callMediaType.name()); if (remote instanceof RemotePeer) { RemotePeer remotePeer = (RemotePeer)remote; + String offerType = getOfferTypeFromCallMediaType(callMediaType).getCode(); Intent intent = new Intent(this, WebRtcCallService.class); intent.setAction(ACTION_SEND_OFFER) @@ -1680,7 +1784,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer, .putExtra(EXTRA_REMOTE_DEVICE, remoteDevice) .putExtra(EXTRA_BROADCAST, broadcast) .putExtra(EXTRA_OFFER_DESCRIPTION, offer) - .putExtra(EXTRA_OFFER_TYPE, (enableVideoOnCreate ? OfferMessage.Type.VIDEO_CALL : OfferMessage.Type.AUDIO_CALL).getCode()); + .putExtra(EXTRA_OFFER_TYPE, offerType); startService(intent); } else { @@ -1736,18 +1840,21 @@ public class WebRtcCallService extends Service implements CallManager.Observer, } @Override - public void onSendHangup(CallId callId, Remote remote, Integer remoteDevice, Boolean broadcast) { - Log.i(TAG, "onSendHangup: id: " + callId.format(remoteDevice)); + public void onSendHangup(CallId callId, Remote remote, Integer remoteDevice, Boolean broadcast, CallManager.HangupType hangupType, Integer deviceId, Boolean useLegacyHangupMessage) { + Log.i(TAG, "onSendHangup: id: " + callId.format(remoteDevice) + " type: " + hangupType.name() + " isLegacy: " + useLegacyHangupMessage); if (remote instanceof RemotePeer) { RemotePeer remotePeer = (RemotePeer)remote; Intent intent = new Intent(this, WebRtcCallService.class); intent.setAction(ACTION_SEND_HANGUP) - .putExtra(EXTRA_CALL_ID, callId.longValue()) - .putExtra(EXTRA_REMOTE_PEER, remotePeer) - .putExtra(EXTRA_REMOTE_DEVICE, remoteDevice) - .putExtra(EXTRA_BROADCAST, broadcast); + .putExtra(EXTRA_CALL_ID, callId.longValue()) + .putExtra(EXTRA_REMOTE_PEER, remotePeer) + .putExtra(EXTRA_REMOTE_DEVICE, remoteDevice) + .putExtra(EXTRA_BROADCAST, broadcast) + .putExtra(EXTRA_HANGUP_DEVICE_ID, deviceId.intValue()) + .putExtra(EXTRA_HANGUP_IS_LEGACY, useLegacyHangupMessage.booleanValue()) + .putExtra(EXTRA_HANGUP_TYPE, getHangupTypeFromCallHangupType(hangupType).getCode()); startService(intent); } else { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ca575c2ee..0e0839ca3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1258,6 +1258,9 @@ To answer the call from %s, give Signal access to your microphone. Signal requires Microphone and Camera permissions in order to make or receive calls, but they have been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\" and \"Camera\". + Answered on a linked device. + Declined on a linked device. + Busy on a linked device. The safety number for your conversation with %1$s has changed. This could either mean that someone is trying to intercept your communication, or that %2$s simply re-installed Signal. diff --git a/app/witness-verifications.gradle b/app/witness-verifications.gradle index 60a67600d6..c55b9827c3 100644 --- a/app/witness-verifications.gradle +++ b/app/witness-verifications.gradle @@ -372,8 +372,8 @@ dependencyVerification { ['org.signal:argon2:13.1', '0f686ccff0d4842bfcc74d92e8dc780a5f159b9376e37a1189fabbcdac458bef'], - ['org.signal:ringrtc-android:1.3.2', - 'f59abe9f33a835cf2ab00953994fe5de477cc4107caec3fe2b2bb778325b6384'], + ['org.signal:ringrtc-android:2.0.1', + '1b5d87ad449fe90d337727e6f307439b65c0c57d410a7fc021970de0a0244a58'], ['org.signal:signal-metadata-java:0.1.2', '6aaeb6a33bf3161a3e6ac9db7678277f7a4cf5a2c96b84342e4007ee49bab1bd'], diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 7d90a94eea..ba8ce9ec0d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -684,11 +684,30 @@ public class SignalServiceMessageSender { .setSdpMLineIndex(update.getSdpMLineIndex())); } } else if (callMessage.getHangupMessage().isPresent()) { - builder.setHangup(CallMessage.Hangup.newBuilder().setId(callMessage.getHangupMessage().get().getId())); + CallMessage.Hangup.Type protoType = callMessage.getHangupMessage().get().getType().getProtoType(); + CallMessage.Hangup.Builder builderForHangup = CallMessage.Hangup.newBuilder() + .setType(protoType) + .setId(callMessage.getHangupMessage().get().getId()); + + if (protoType != CallMessage.Hangup.Type.HANGUP_NORMAL) { + builderForHangup.setDeviceId(callMessage.getHangupMessage().get().getDeviceId()); + } + + if (callMessage.getHangupMessage().get().isLegacy()) { + builder.setLegacyHangup(builderForHangup); + } else { + builder.setHangup(builderForHangup); + } } else if (callMessage.getBusyMessage().isPresent()) { builder.setBusy(CallMessage.Busy.newBuilder().setId(callMessage.getBusyMessage().get().getId())); } + builder.setMultiRing(callMessage.isMultiRing()); + + if (callMessage.getDestinationDeviceId().isPresent()) { + builder.setDestinationDeviceId(callMessage.getDestinationDeviceId().get()); + } + container.setCallMessage(builder); return container.build().toByteArray(); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java index 8074942735..cbfef2b1de 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java @@ -530,12 +530,15 @@ public final class SignalServiceContent { } private static SignalServiceCallMessage createCallMessage(SignalServiceProtos.CallMessage content) { + boolean isMultiRing = content.getMultiRing(); + Integer destinationDeviceId = content.hasDestinationDeviceId() ? content.getDestinationDeviceId() : null; + if (content.hasOffer()) { SignalServiceProtos.CallMessage.Offer offerContent = content.getOffer(); - return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.getDescription(), OfferMessage.Type.fromProto(offerContent.getType()))); + return SignalServiceCallMessage.forOffer(new OfferMessage(offerContent.getId(), offerContent.getDescription(), OfferMessage.Type.fromProto(offerContent.getType())), isMultiRing, destinationDeviceId); } else if (content.hasAnswer()) { SignalServiceProtos.CallMessage.Answer answerContent = content.getAnswer(); - return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.getDescription())); + return SignalServiceCallMessage.forAnswer(new AnswerMessage(answerContent.getId(), answerContent.getDescription()), isMultiRing, destinationDeviceId); } else if (content.getIceUpdateCount() > 0) { List iceUpdates = new LinkedList<>(); @@ -543,13 +546,13 @@ public final class SignalServiceContent { iceUpdates.add(new IceUpdateMessage(iceUpdate.getId(), iceUpdate.getSdpMid(), iceUpdate.getSdpMLineIndex(), iceUpdate.getSdp())); } - return SignalServiceCallMessage.forIceUpdates(iceUpdates); + return SignalServiceCallMessage.forIceUpdates(iceUpdates, isMultiRing, destinationDeviceId); } else if (content.hasHangup()) { - SignalServiceProtos.CallMessage.Hangup hangup = content.getHangup(); - return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId())); + SignalServiceProtos.CallMessage.Hangup hangup = content.hasLegacyHangup() ? content.getLegacyHangup() : content.getHangup(); + return SignalServiceCallMessage.forHangup(new HangupMessage(hangup.getId(), HangupMessage.Type.fromProto(hangup.getType()), hangup.getDeviceId(), content.hasLegacyHangup()), isMultiRing, destinationDeviceId); } else if (content.hasBusy()) { SignalServiceProtos.CallMessage.Busy busy = content.getBusy(); - return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId())); + return SignalServiceCallMessage.forBusy(new BusyMessage(busy.getId()), isMultiRing, destinationDeviceId); } return SignalServiceCallMessage.empty(); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java index f04ddc2886..0c21da801d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/HangupMessage.java @@ -1,15 +1,78 @@ package org.whispersystems.signalservice.api.messages.calls; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; + public class HangupMessage { - private final long id; + private final long id; + private final Type type; + private final int deviceId; + private final boolean isLegacy; - public HangupMessage(long id) { - this.id = id; + public HangupMessage(long id, Type type, int deviceId, boolean isLegacy) { + this.id = id; + this.type = type; + this.deviceId = deviceId; + this.isLegacy = isLegacy; } public long getId() { return id; } + + public Type getType() { + return type; + } + + public int getDeviceId() { + return deviceId; + } + + public boolean isLegacy() { + return isLegacy; + } + + public enum Type { + NORMAL("normal", SignalServiceProtos.CallMessage.Hangup.Type.HANGUP_NORMAL), + ACCEPTED("accepted", SignalServiceProtos.CallMessage.Hangup.Type.HANGUP_ACCEPTED), + DECLINED("declined", SignalServiceProtos.CallMessage.Hangup.Type.HANGUP_DECLINED), + BUSY("busy", SignalServiceProtos.CallMessage.Hangup.Type.HANGUP_BUSY); + + private final String code; + private final SignalServiceProtos.CallMessage.Hangup.Type protoType; + + Type(String code, SignalServiceProtos.CallMessage.Hangup.Type protoType) { + this.code = code; + this.protoType = protoType; + } + + public String getCode() { + return code; + } + + public SignalServiceProtos.CallMessage.Hangup.Type getProtoType() { + return protoType; + } + + public static Type fromCode(String code) { + for (Type type : Type.values()) { + if (type.getCode().equals(code)) { + return type; + } + } + + throw new IllegalArgumentException("Unexpected code: " + code); + } + + public static Type fromProto(SignalServiceProtos.CallMessage.Hangup.Type hangupType) { + for (Type type : Type.values()) { + if (type.getProtoType().equals(hangupType)) { + return type; + } + } + + throw new IllegalArgumentException("Unexpected type: " + hangupType.name()); + } + } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java index db5b1e7038..aa4beb6eb7 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/calls/SignalServiceCallMessage.java @@ -12,78 +12,98 @@ public class SignalServiceCallMessage { private final Optional hangupMessage; private final Optional busyMessage; private final Optional> iceUpdateMessages; + private final Optional destinationDeviceId; + private final boolean isMultiRing; private SignalServiceCallMessage(Optional offerMessage, Optional answerMessage, Optional> iceUpdateMessages, Optional hangupMessage, - Optional busyMessage) + Optional busyMessage, + boolean isMultiRing, + Optional destinationDeviceId) { - this.offerMessage = offerMessage; - this.answerMessage = answerMessage; - this.iceUpdateMessages = iceUpdateMessages; - this.hangupMessage = hangupMessage; - this.busyMessage = busyMessage; + this.offerMessage = offerMessage; + this.answerMessage = answerMessage; + this.iceUpdateMessages = iceUpdateMessages; + this.hangupMessage = hangupMessage; + this.busyMessage = busyMessage; + this.isMultiRing = isMultiRing; + this.destinationDeviceId = destinationDeviceId; } - public static SignalServiceCallMessage forOffer(OfferMessage offerMessage) { + public static SignalServiceCallMessage forOffer(OfferMessage offerMessage, boolean isMultiRing, Integer destinationDeviceId) { return new SignalServiceCallMessage(Optional.of(offerMessage), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent()); + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } - public static SignalServiceCallMessage forAnswer(AnswerMessage answerMessage) { - return new SignalServiceCallMessage(Optional.absent(), + public static SignalServiceCallMessage forAnswer(AnswerMessage answerMessage, boolean isMultiRing, Integer destinationDeviceId) { + return new SignalServiceCallMessage(Optional.absent(), Optional.of(answerMessage), - Optional.>absent(), - Optional.absent(), - Optional.absent()); + Optional.absent(), + Optional.absent(), + Optional.absent(), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } - public static SignalServiceCallMessage forIceUpdates(List iceUpdateMessages) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), + public static SignalServiceCallMessage forIceUpdates(List iceUpdateMessages, boolean isMultiRing, Integer destinationDeviceId) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), Optional.of(iceUpdateMessages), - Optional.absent(), - Optional.absent()); + Optional.absent(), + Optional.absent(), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } - public static SignalServiceCallMessage forIceUpdate(final IceUpdateMessage iceUpdateMessage) { + public static SignalServiceCallMessage forIceUpdate(final IceUpdateMessage iceUpdateMessage, boolean isMultiRing, Integer destinationDeviceId) { List iceUpdateMessages = new LinkedList<>(); iceUpdateMessages.add(iceUpdateMessage); - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), Optional.of(iceUpdateMessages), - Optional.absent(), - Optional.absent()); + Optional.absent(), + Optional.absent(), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } - public static SignalServiceCallMessage forHangup(HangupMessage hangupMessage) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), + public static SignalServiceCallMessage forHangup(HangupMessage hangupMessage, boolean isMultiRing, Integer destinationDeviceId) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), Optional.of(hangupMessage), - Optional.absent()); + Optional.absent(), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } - public static SignalServiceCallMessage forBusy(BusyMessage busyMessage) { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.of(busyMessage)); + public static SignalServiceCallMessage forBusy(BusyMessage busyMessage, boolean isMultiRing, Integer destinationDeviceId) { + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.of(busyMessage), + isMultiRing, + Optional.fromNullable(destinationDeviceId)); } public static SignalServiceCallMessage empty() { - return new SignalServiceCallMessage(Optional.absent(), - Optional.absent(), - Optional.>absent(), - Optional.absent(), - Optional.absent()); + return new SignalServiceCallMessage(Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + Optional.absent(), + false, + Optional.absent()); } public Optional> getIceUpdateMessages() { @@ -105,4 +125,12 @@ public class SignalServiceCallMessage { public Optional getBusyMessage() { return busyMessage; } + + public boolean isMultiRing() { + return isMultiRing; + } + + public Optional getDestinationDeviceId() { + return destinationDeviceId; + } } diff --git a/libsignal/service/src/main/proto/SignalService.proto b/libsignal/service/src/main/proto/SignalService.proto index 2d7904069f..1d351a8ae3 100644 --- a/libsignal/service/src/main/proto/SignalService.proto +++ b/libsignal/service/src/main/proto/SignalService.proto @@ -71,15 +71,28 @@ message CallMessage { } message Hangup { - optional uint64 id = 1; + enum Type { + HANGUP_NORMAL = 0; + HANGUP_ACCEPTED = 1; + HANGUP_DECLINED = 2; + HANGUP_BUSY = 3; + } + + optional uint64 id = 1; + optional Type type = 2; + optional uint32 deviceId = 3; } - optional Offer offer = 1; - optional Answer answer = 2; - repeated IceUpdate iceUpdate = 3; - optional Hangup hangup = 4; - optional Busy busy = 5; + optional Offer offer = 1; + optional Answer answer = 2; + repeated IceUpdate iceUpdate = 3; + optional Hangup legacyHangup = 4; + optional Busy busy = 5; + // 6 is reserved. + optional Hangup hangup = 7; + optional bool multiRing = 8; + optional uint32 destinationDeviceId = 9; } message DataMessage {