Ensure serial handling of calling events and improve busy UX.

master
Jim Gustafson 2020-09-03 14:41:15 -07:00 committed by Cody Henthorne
parent 7b24e66ed3
commit ed9acd25f9
4 changed files with 226 additions and 153 deletions

View File

@ -64,7 +64,6 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
private static final String TAG = WebRtcCallActivity.class.getSimpleName(); private static final String TAG = WebRtcCallActivity.class.getSimpleName();
private static final int STANDARD_DELAY_FINISH = 1000; private static final int STANDARD_DELAY_FINISH = 1000;
public static final int BUSY_SIGNAL_DELAY_FINISH = 5500;
public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION"; public static final String ANSWER_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".ANSWER_ACTION";
public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION"; public static final String DENY_ACTION = WebRtcCallActivity.class.getCanonicalName() + ".DENY_ACTION";
@ -408,8 +407,7 @@ public class WebRtcCallActivity extends AppCompatActivity implements SafetyNumbe
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
callScreen.setRecipient(event.getRecipient()); callScreen.setRecipient(event.getRecipient());
callScreen.setStatus(getString(R.string.RedPhone_busy)); callScreen.setStatus(getString(R.string.RedPhone_busy));
delayedFinish(WebRtcCallService.BUSY_TONE_LENGTH);
delayedFinish(BUSY_SIGNAL_DELAY_FINISH);
} }
private void handleCallConnected(@NonNull WebRtcViewModel event) { private void handleCallConnected(@NonNull WebRtcViewModel event) {

View File

@ -10,10 +10,10 @@ public enum CallState {
/** Idle, setting up objects */ /** Idle, setting up objects */
IDLE, IDLE,
/** Dialing. Outgoing call is signaling the remote peer */ /** Dialing. Outgoing call is signaling the remote peer */
DIALING, DIALING,
/** Answering. Incoming call is responding to remote peer */ /** Answering. Incoming call is responding to remote peer */
ANSWERING, ANSWERING,
/** Remote ringing. Outgoing call, ICE negotiation is complete */ /** Remote ringing. Outgoing call, ICE negotiation is complete */
@ -25,10 +25,9 @@ public enum CallState {
/** Connected. Incoming/Outgoing call, the call is connected */ /** Connected. Incoming/Outgoing call, the call is connected */
CONNECTED, CONNECTED,
/** Terminated. Incoming/Outgoing call, the call is terminated */ /** Terminated. Incoming/Outgoing call, the call is terminated */
TERMINATED, TERMINATED,
/** Busy. Outgoing call received a busy notification */ /** Busy. Outgoing call received a busy notification */
RECEIVED_BUSY; RECEIVED_BUSY;
} }

View File

@ -40,6 +40,10 @@ public final class RemotePeer implements Remote, Parcelable
return callId; return callId;
} }
public void setCallId(@NonNull CallId callId) {
this.callId = callId;
}
public @NonNull CallState getState() { public @NonNull CallState getState() {
return callState; return callState;
} }
@ -73,21 +77,19 @@ public final class RemotePeer implements Remote, Parcelable
return remotePeer != null && this.callId.equals(remotePeer.callId); return remotePeer != null && this.callId.equals(remotePeer.callId);
} }
public void dialing(@NonNull CallId callId) { public void dialing() {
if (callState != CallState.IDLE) { if (callState != CallState.IDLE) {
throw new IllegalStateException("Cannot transition to DIALING from state: " + callState); throw new IllegalStateException("Cannot transition to DIALING from state: " + callState);
} }
this.callId = callId;
this.callState = CallState.DIALING; this.callState = CallState.DIALING;
} }
public void answering(@NonNull CallId callId) { public void answering() {
if (callState != CallState.IDLE) { if (callState != CallState.IDLE) {
throw new IllegalStateException("Cannot transition to ANSWERING from state: " + callState); throw new IllegalStateException("Cannot transition to ANSWERING from state: " + callState);
} }
this.callId = callId;
this.callState = CallState.ANSWERING; this.callState = CallState.ANSWERING;
} }
@ -99,14 +101,6 @@ public final class RemotePeer implements Remote, Parcelable
this.callState = CallState.REMOTE_RINGING; this.callState = CallState.REMOTE_RINGING;
} }
public void receivedBusy() {
if (callState != CallState.DIALING) {
Log.w(TAG, "RECEIVED_BUSY from unexpected state: " + callState);
}
this.callState = CallState.RECEIVED_BUSY;
}
public void localRinging() { public void localRinging() {
if (callState != CallState.ANSWERING) { if (callState != CallState.ANSWERING) {
throw new IllegalStateException("Cannot transition to LOCAL_RINGING from state: " + callState); throw new IllegalStateException("Cannot transition to LOCAL_RINGING from state: " + callState);
@ -123,6 +117,14 @@ public final class RemotePeer implements Remote, Parcelable
this.callState = CallState.CONNECTED; this.callState = CallState.CONNECTED;
} }
public void receivedBusy() {
if (callState != CallState.DIALING) {
Log.w(TAG, "RECEIVED_BUSY from unexpected state: " + callState);
}
this.callState = CallState.RECEIVED_BUSY;
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;

View File

@ -12,6 +12,7 @@ import android.os.IBinder;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.telephony.PhoneStateListener; import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.util.SparseArray;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -47,6 +48,7 @@ import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TelephonyUtil; import org.thoughtcrime.securesms.util.TelephonyUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder; import org.thoughtcrime.securesms.webrtc.CallNotificationBuilder;
import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver; import org.thoughtcrime.securesms.webrtc.IncomingPstnCallReceiver;
import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager; import org.thoughtcrime.securesms.webrtc.UncaughtExceptionHandlerManager;
@ -68,7 +70,6 @@ import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage; import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo; import org.whispersystems.signalservice.api.messages.calls.TurnServerInfo;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException; import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import java.io.IOException; import java.io.IOException;
@ -102,6 +103,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public static final String EXTRA_SPEAKER = "audio_speaker"; public static final String EXTRA_SPEAKER = "audio_speaker";
public static final String EXTRA_BLUETOOTH = "audio_bluetooth"; public static final String EXTRA_BLUETOOTH = "audio_bluetooth";
public static final String EXTRA_REMOTE_PEER = "remote_peer"; public static final String EXTRA_REMOTE_PEER = "remote_peer";
public static final String EXTRA_REMOTE_PEER_KEY = "remote_peer_key";
public static final String EXTRA_REMOTE_DEVICE = "remote_device"; public static final String EXTRA_REMOTE_DEVICE = "remote_device";
public static final String EXTRA_OFFER_OPAQUE = "offer_opaque"; public static final String EXTRA_OFFER_OPAQUE = "offer_opaque";
public static final String EXTRA_OFFER_SDP = "offer_sdp"; public static final String EXTRA_OFFER_SDP = "offer_sdp";
@ -161,6 +163,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public static final String ACTION_ENDED_RX_OFFER_WHILE_ACTIVE = "ENDED_RX_OFFER_WHILE_ACTIVE"; 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_CALL_CONCLUDED = "CALL_CONCLUDED";
public static final int BUSY_TONE_LENGTH = 2000;
private CameraState localCameraState = CameraState.UNKNOWN; private CameraState localCameraState = CameraState.UNKNOWN;
private boolean microphoneEnabled = true; private boolean microphoneEnabled = true;
private boolean remoteVideoEnabled = false; private boolean remoteVideoEnabled = false;
@ -181,12 +185,14 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
private IncomingPstnCallReceiver callReceiver; private IncomingPstnCallReceiver callReceiver;
private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager; private UncaughtExceptionHandlerManager uncaughtExceptionHandlerManager;
@Nullable private CallManager callManager; @Nullable private CallManager callManager;
@Nullable private RemotePeer activePeer; @Nullable private RemotePeer activePeer;
@Nullable private TextureViewRenderer localRenderer; @Nullable private RemotePeer busyPeer;
@Nullable private TextureViewRenderer remoteRenderer; @Nullable private SparseArray<RemotePeer> peerMap;
@Nullable private EglBase eglBase; @Nullable private TextureViewRenderer localRenderer;
@Nullable private Camera camera; @Nullable private TextureViewRenderer remoteRenderer;
@Nullable private EglBase eglBase;
@Nullable private Camera camera;
private final ExecutorService serviceExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService serviceExecutor = Executors.newSingleThreadExecutor();
private final ExecutorService networkExecutor = Executors.newSingleThreadExecutor(); private final ExecutorService networkExecutor = Executors.newSingleThreadExecutor();
@ -257,7 +263,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
else if (intent.getAction().equals(ACTION_ENDED_RX_OFFER_EXPIRED)) handleEndedReceivedOfferExpired(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_ENDED_RX_OFFER_WHILE_ACTIVE)) handleEndedReceivedOfferWhileActive(intent);
else if (intent.getAction().equals(ACTION_CALL_CONCLUDED)) handleCallConcluded(intent); else if (intent.getAction().equals(ACTION_CALL_CONCLUDED)) handleCallConcluded(intent);
}); });
return START_NOT_STICKY; return START_NOT_STICKY;
@ -322,15 +327,13 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
} }
// Initializers
private void initializeResources() { private void initializeResources() {
this.messageSender = ApplicationDependencies.getSignalServiceMessageSender(); this.messageSender = ApplicationDependencies.getSignalServiceMessageSender();
this.accountManager = ApplicationDependencies.getSignalServiceAccountManager(); this.accountManager = ApplicationDependencies.getSignalServiceAccountManager();
this.lockManager = new LockManager(this); this.lockManager = new LockManager(this);
this.audioManager = new SignalAudioManager(this); this.audioManager = new SignalAudioManager(this);
this.bluetoothStateManager = new BluetoothStateManager(this, this); this.bluetoothStateManager = new BluetoothStateManager(this, this);
this.peerMap = new SparseArray<>();
this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); this.messageSender.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10)); this.accountManager.setSoTimeoutMillis(TimeUnit.SECONDS.toMillis(10));
@ -340,7 +343,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} catch (CallException e) { } catch (CallException e) {
callFailure("Unable to create Call Manager: ", e); callFailure("Unable to create Call Manager: ", e);
} }
} }
private void registerIncomingPstnCallReceiver() { private void registerIncomingPstnCallReceiver() {
@ -383,8 +385,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
} }
// Handlers
private void handleReceivedOffer(Intent intent) { private void handleReceivedOffer(Intent intent) {
CallId callId = getCallId(intent); CallId callId = getCallId(intent);
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeer(intent);
@ -399,7 +399,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
Log.i(TAG, "handleReceivedOffer(): id: " + callId.format(remoteDevice)); Log.i(TAG, "handleReceivedOffer(): id: " + callId.format(remoteDevice));
if (TelephonyUtil.isAnyPstnLineBusy(this)) { if (TelephonyUtil.isAnyPstnLineBusy(this)) {
Log.i(TAG, "handleReceivedOffer(): PSTN line is busy."); Log.i(TAG, "PSTN line is busy.");
intent.putExtra(EXTRA_BROADCAST, true); intent.putExtra(EXTRA_BROADCAST, true);
handleSendBusy(intent); handleSendBusy(intent);
insertMissedCall(remotePeer, true); insertMissedCall(remotePeer, true);
@ -407,7 +407,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
if (remotePeer.getRecipient() == null || !RecipientUtil.isCallRequestAccepted(getApplicationContext(), remotePeer.getRecipient())) { if (remotePeer.getRecipient() == null || !RecipientUtil.isCallRequestAccepted(getApplicationContext(), remotePeer.getRecipient())) {
Log.i(TAG, "handleReceivedOffer(): Caller is untrusted."); Log.w(TAG, "Caller is untrusted.");
intent.putExtra(EXTRA_BROADCAST, true); intent.putExtra(EXTRA_BROADCAST, true);
intent.putExtra(EXTRA_HANGUP_TYPE, HangupMessage.Type.NEED_PERMISSION.getCode()); intent.putExtra(EXTRA_HANGUP_TYPE, HangupMessage.Type.NEED_PERMISSION.getCode());
handleSendHangup(intent); handleSendHangup(intent);
@ -415,12 +415,15 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
return; return;
} }
peerMap.append(remotePeer.hashCode(), remotePeer);
Log.i(TAG, "add remotePeer callId: " + remotePeer.getCallId() + " key: " + remotePeer.hashCode());
isRemoteVideoOffer = offerType == OfferMessage.Type.VIDEO_CALL; isRemoteVideoOffer = offerType == OfferMessage.Type.VIDEO_CALL;
CallManager.CallMediaType callType = getCallMediaTypeFromOfferType(offerType); CallManager.CallMediaType callType = getCallMediaTypeFromOfferType(offerType);
long messageAgeSec = Math.max(serverDeliveredTimestamp - serverReceivedTimestamp, 0) / 1000; long messageAgeSec = Math.max(serverDeliveredTimestamp - serverReceivedTimestamp, 0) / 1000;
Log.i(TAG, "handleReceivedOffer(): messageAgeSec: " + messageAgeSec + ", serverReceivedTimestamp: " + serverReceivedTimestamp + ", serverDeliveredTimestamp: " + serverDeliveredTimestamp); Log.i(TAG, "messageAgeSec: " + messageAgeSec + ", serverReceivedTimestamp: " + serverReceivedTimestamp + ", serverDeliveredTimestamp: " + serverDeliveredTimestamp);
try { try {
callManager.receivedOffer(callId, remotePeer, remoteDevice, opaque, sdp, messageAgeSec, callType, 1, isMultiRing, true); callManager.receivedOffer(callId, remotePeer, remoteDevice, opaque, sdp, messageAgeSec, callType, 1, isMultiRing, true);
@ -430,15 +433,19 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleOutgoingCall(Intent intent) { private void handleOutgoingCall(Intent intent) {
Log.i(TAG, "handleOutgoingCall():");
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeer(intent);
if (remotePeer.getState() != CallState.IDLE) { if (remotePeer.getState() != CallState.IDLE) {
throw new IllegalStateException("Dialing from non-idle?"); throw new IllegalStateException("Dialing from non-idle?");
} }
Log.i(TAG, "handleOutgoingCall():");
EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class); EventBus.getDefault().removeStickyEvent(WebRtcViewModel.class);
peerMap.append(remotePeer.hashCode(), remotePeer);
Log.i(TAG, "add remotePeer callId: " + remotePeer.getCallId() + " key: " + remotePeer.hashCode());
initializeVideo(); initializeVideo();
OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE)); OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE));
@ -480,6 +487,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
try { try {
callManager.hangup(); callManager.hangup();
DatabaseFactory.getSmsDatabase(this).insertMissedCall(activePeer.getId()); DatabaseFactory.getSmsDatabase(this).insertMissedCall(activePeer.getId());
terminate(activePeer);
} catch (CallException e) { } catch (CallException e) {
callFailure("hangup() failed: ", e); callFailure("hangup() failed: ", e);
} }
@ -591,7 +599,19 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleStartOutgoingCall(Intent intent) { private void handleStartOutgoingCall(Intent intent) {
Log.i(TAG, "handleStartOutgoingCall(): callId: " + activePeer.getCallId()); Log.i(TAG, "handleStartOutgoingCall():");
if (activePeer != null) {
throw new IllegalStateException("handleStartOutgoingCall(): activePeer already set");
}
RemotePeer remotePeer = getRemotePeerFromMap(intent);
activePeer = remotePeer;
activePeer.dialing();
Log.i(TAG, "assign activePeer callId: " + activePeer.getCallId() + " key: " + activePeer.hashCode());
AudioManager androidAudioManager = ServiceUtil.getAudioManager(this);
androidAudioManager.setSpeakerphoneOn(false);
sendMessage(WebRtcViewModel.State.CALL_OUTGOING, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_OUTGOING, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
lockManager.updatePhoneState(getInCallPhoneState()); lockManager.updatePhoneState(getInCallPhoneState());
@ -609,9 +629,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this);
LinkedList<Integer> deviceList = new LinkedList<Integer>();
deviceList.add(SignalServiceAddress.DEFAULT_DEVICE_ID);
try { try {
callManager.proceed(activePeer.getCallId(), callManager.proceed(activePeer.getCallId(),
WebRtcCallService.this, WebRtcCallService.this,
@ -635,11 +652,19 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleStartIncomingCall(Intent intent) { private void handleStartIncomingCall(Intent intent) {
if (activePeer.getState() != CallState.ANSWERING) { Log.i(TAG, "handleStartIncomingCall():");
throw new IllegalStateException("StartIncoming while non-ANSWERING");
if (activePeer != null) {
throw new IllegalStateException("handleStartIncomingCall(): activePeer already set");
} }
Log.i(TAG, "handleStartIncomingCall(): callId: " + activePeer.getCallId()); RemotePeer remotePeer = getRemotePeerFromMap(intent);
activePeer = remotePeer;
activePeer.answering();
Log.i(TAG, "assign activePeer callId: " + activePeer.getCallId() + " key: " + activePeer.hashCode());
AudioManager androidAudioManager = ServiceUtil.getAudioManager(this);
androidAudioManager.setSpeakerphoneOn(false);
initializeVideo(); initializeVideo();
@ -648,12 +673,9 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
retrieveTurnServers().addListener(new SuccessOnlyListener<List<PeerConnection.IceServer>>(this.activePeer.getState(), this.activePeer.getCallId()) { retrieveTurnServers().addListener(new SuccessOnlyListener<List<PeerConnection.IceServer>>(this.activePeer.getState(), this.activePeer.getCallId()) {
@Override @Override
public void onSuccessContinue(List<PeerConnection.IceServer> iceServers) { public void onSuccessContinue(List<PeerConnection.IceServer> iceServers) {
boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this); boolean isAlwaysTurn = TextSecurePreferences.isTurnOnly(WebRtcCallService.this);
boolean hideIp = !activePeer.getRecipient().isSystemContact() || isAlwaysTurn; boolean hideIp = !activePeer.getRecipient().isSystemContact() || isAlwaysTurn;
LinkedList<Integer> deviceList = new LinkedList<>();
try { try {
callManager.proceed(activePeer.getCallId(), callManager.proceed(activePeer.getCallId(),
WebRtcCallService.this, WebRtcCallService.this,
@ -677,7 +699,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleAcceptCall(Intent intent) { private void handleAcceptCall(Intent intent) {
if (activePeer != null && activePeer.getState() != CallState.LOCAL_RINGING) { if (activePeer != null && activePeer.getState() != CallState.LOCAL_RINGING) {
Log.w(TAG, "handleAcceptCall(): Ignoring for inactive call."); Log.w(TAG, "handleAcceptCall(): Ignoring for inactive call.");
return; return;
@ -705,7 +726,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
String sdp = intent.getStringExtra(EXTRA_OFFER_SDP); String sdp = intent.getStringExtra(EXTRA_OFFER_SDP);
OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE)); OfferMessage.Type offerType = OfferMessage.Type.fromCode(intent.getStringExtra(EXTRA_OFFER_TYPE));
Log.i(TAG, "handleSendOffer: id: " + callId.format(remoteDevice)); Log.i(TAG, "handleSendOffer(): id: " + callId.format(remoteDevice));
OfferMessage offerMessage = new OfferMessage(callId.longValue(), sdp, offerType, opaque); OfferMessage offerMessage = new OfferMessage(callId.longValue(), sdp, offerType, opaque);
Integer destinationDeviceId = broadcast ? null : remoteDevice; Integer destinationDeviceId = broadcast ? null : remoteDevice;
@ -722,7 +743,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
byte[] opaque = intent.getByteArrayExtra(EXTRA_ANSWER_OPAQUE); byte[] opaque = intent.getByteArrayExtra(EXTRA_ANSWER_OPAQUE);
String sdp = intent.getStringExtra(EXTRA_ANSWER_SDP); String sdp = intent.getStringExtra(EXTRA_ANSWER_SDP);
Log.i(TAG, "handleSendAnswer: id: " + callId.format(remoteDevice)); Log.i(TAG, "handleSendAnswer(): id: " + callId.format(remoteDevice));
AnswerMessage answerMessage = new AnswerMessage(callId.longValue(), sdp, opaque); AnswerMessage answerMessage = new AnswerMessage(callId.longValue(), sdp, opaque);
Integer destinationDeviceId = broadcast ? null : remoteDevice; Integer destinationDeviceId = broadcast ? null : remoteDevice;
@ -738,7 +759,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false); boolean broadcast = intent.getBooleanExtra(EXTRA_BROADCAST, false);
ArrayList<IceCandidateParcel> iceCandidates = intent.getParcelableArrayListExtra(EXTRA_ICE_CANDIDATES); ArrayList<IceCandidateParcel> iceCandidates = intent.getParcelableArrayListExtra(EXTRA_ICE_CANDIDATES);
Log.i(TAG, "handleSendIceCandidates: id: " + callId.format(remoteDevice)); Log.i(TAG, "handleSendIceCandidates(): id: " + callId.format(remoteDevice));
LinkedList<IceUpdateMessage> iceUpdateMessages = new LinkedList(); LinkedList<IceUpdateMessage> iceUpdateMessages = new LinkedList();
for (IceCandidateParcel parcel : iceCandidates) { for (IceCandidateParcel parcel : iceCandidates) {
@ -760,7 +781,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
boolean isLegacy = intent.getBooleanExtra(EXTRA_HANGUP_IS_LEGACY, true); boolean isLegacy = intent.getBooleanExtra(EXTRA_HANGUP_IS_LEGACY, true);
int deviceId = intent.getIntExtra(EXTRA_HANGUP_DEVICE_ID, 0); int deviceId = intent.getIntExtra(EXTRA_HANGUP_DEVICE_ID, 0);
Log.i(TAG, "handleSendHangup: id: " + callId.format(remoteDevice)); Log.i(TAG, "handleSendHangup(): id: " + callId.format(remoteDevice));
HangupMessage hangupMessage = new HangupMessage(callId.longValue(), type, deviceId, isLegacy); HangupMessage hangupMessage = new HangupMessage(callId.longValue(), type, deviceId, isLegacy);
Integer destinationDeviceId = broadcast ? null : remoteDevice; Integer destinationDeviceId = broadcast ? null : remoteDevice;
@ -775,7 +796,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); 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)); Log.i(TAG, "handleSendBusy(): id: " + callId.format(remoteDevice));
BusyMessage busyMessage = new BusyMessage(callId.longValue()); BusyMessage busyMessage = new BusyMessage(callId.longValue());
Integer destinationDeviceId = broadcast ? null : remoteDevice; Integer destinationDeviceId = broadcast ? null : remoteDevice;
@ -805,7 +826,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1); Integer remoteDevice = intent.getIntExtra(EXTRA_REMOTE_DEVICE, -1);
ArrayList<IceCandidateParcel> iceCandidateParcels = intent.getParcelableArrayListExtra(EXTRA_ICE_CANDIDATES); ArrayList<IceCandidateParcel> iceCandidateParcels = intent.getParcelableArrayListExtra(EXTRA_ICE_CANDIDATES);
Log.i(TAG, "handleReceivedIceCandidates: id: " + callId.format(remoteDevice) + ", count: " + iceCandidateParcels.size()); Log.i(TAG, "handleReceivedIceCandidates(): id: " + callId.format(remoteDevice) + ", count: " + iceCandidateParcels.size());
LinkedList<IceCandidate> iceCandidates = new LinkedList(); LinkedList<IceCandidate> iceCandidates = new LinkedList();
for (IceCandidateParcel parcel : iceCandidateParcels) { for (IceCandidateParcel parcel : iceCandidateParcels) {
@ -849,16 +870,16 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleLocalRinging(Intent intent) { private void handleLocalRinging(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Recipient recipient = remotePeer.getRecipient(); Recipient recipient = remotePeer.getRecipient();
Log.i(TAG, "handleLocalRinging(): call_id: " + remotePeer.getCallId());
if (!remotePeer.callIdEquals(activePeer)) { if (!remotePeer.callIdEquals(activePeer)) {
Log.w(TAG, "handleLocalRinging(): Ignoring for inactive call."); Log.w(TAG, "handleLocalRinging(): Ignoring for inactive call.");
return; return;
} }
Log.i(TAG, "handleLocalRinging(): call_id: " + remotePeer.getCallId());
activePeer.localRinging(); activePeer.localRinging();
lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE); lockManager.updatePhoneState(LockManager.PhoneState.INTERACTIVE);
@ -883,30 +904,29 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleRemoteRinging(Intent intent) { private void handleRemoteRinging(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Recipient recipient = remotePeer.getRecipient();
Log.i(TAG, "handleRemoteRinging(): call_id: " + remotePeer.getCallId());
if (!remotePeer.callIdEquals(activePeer)) { if (!remotePeer.callIdEquals(activePeer)) {
Log.w(TAG, "handleRemoteRinging(): Ignoring for inactive call."); Log.w(TAG, "handleRemoteRinging(): Ignoring for inactive call.");
return; return;
} }
Log.i(TAG, "handleRemoteRinging(): call_id: " + remotePeer.getCallId());
activePeer.remoteRinging(); activePeer.remoteRinging();
sendMessage(WebRtcViewModel.State.CALL_RINGING, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_RINGING, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
private void handleCallConnected(Intent intent) { private void handleCallConnected(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleCallConnected: call_id: " + remotePeer.getCallId());
if (!remotePeer.callIdEquals(activePeer)) { if (!remotePeer.callIdEquals(activePeer)) {
Log.w(TAG, "handleCallConnected(): Ignoring for inactive call."); Log.w(TAG, "handleCallConnected(): Ignoring for inactive call.");
return; return;
} }
Log.i(TAG, "handleCallConnected(): call_id: " + remotePeer.getCallId());
audioManager.startCommunication(activePeer.getState() == CallState.REMOTE_RINGING); audioManager.startCommunication(activePeer.getState() == CallState.REMOTE_RINGING);
bluetoothStateManager.setWantsConnection(true); bluetoothStateManager.setWantsConnection(true);
@ -947,7 +967,7 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
return; return;
} }
Log.i(TAG, "handleRemoteVideoEnable: call_id: " + activePeer.getCallId()); Log.i(TAG, "handleRemoteVideoEnable(): call_id: " + activePeer.getCallId());
remoteVideoEnabled = enable; remoteVideoEnabled = enable;
sendMessage(WebRtcViewModel.State.CALL_CONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_CONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
@ -1007,46 +1027,49 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
private void handleLocalHangup(Intent intent) { private void handleLocalHangup(Intent intent) {
if (activePeer == null) { if (activePeer == null) {
if (busyPeer != null) {
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, busyPeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
busyPeer = null;
}
Log.w(TAG, "handleLocalHangup(): Ignoring for inactive call."); Log.w(TAG, "handleLocalHangup(): Ignoring for inactive call.");
return; return;
} }
Log.i(TAG, "handleLocalHangup(): call_id: " + activePeer.getCallId()); Log.i(TAG, "handleLocalHangup(): call_id: " + activePeer.getCallId());
if (activePeer.getState() == CallState.RECEIVED_BUSY) { accountManager.cancelInFlightRequests();
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); messageSender.cancelInFlightRequests();
terminate();
} else {
accountManager.cancelInFlightRequests();
messageSender.cancelInFlightRequests();
try {
callManager.hangup();
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
terminate(activePeer);
try { } catch (CallException e) {
callManager.hangup(); callFailure("hangup() failed: ", e);
} catch (CallException e) {
callFailure("hangup() failed: ", e);
}
} }
} }
private void handleEndedReceivedOfferExpired(Intent intent) { private void handleEndedReceivedOfferExpired(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedReceivedOfferExpired(): call_id: " + remotePeer.getCallId()); Log.i(TAG, "handleEndedReceivedOfferExpired(): call_id: " + remotePeer.getCallId());
insertMissedCall(remotePeer, true); insertMissedCall(remotePeer, true);
terminate(remotePeer);
} }
private void handleEndedReceivedOfferWhileActive(Intent intent) { private void handleEndedReceivedOfferWhileActive(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedReceivedOfferWhileActive(): call_id: " + remotePeer.getCallId());
if (activePeer == null) { if (activePeer == null) {
Log.w(TAG, "handleEndedReceivedOfferWhileActive(): ignoring call with null activePeer"); Log.w(TAG, "handleEndedReceivedOfferWhileActive(): Ignoring for inactive call.");
return; return;
} }
Log.i(TAG, "handleEndedReceivedOfferWhileActive(): call_id: " + remotePeer.getCallId());
switch (activePeer.getState()) { switch (activePeer.getState()) {
case DIALING: case DIALING:
case REMOTE_RINGING: setCallInProgressNotification(TYPE_OUTGOING_RINGING, activePeer); break; case REMOTE_RINGING: setCallInProgressNotification(TYPE_OUTGOING_RINGING, activePeer); break;
@ -1062,10 +1085,12 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
insertMissedCall(remotePeer, true); insertMissedCall(remotePeer, true);
terminate(remotePeer);
} }
private void handleEndedRemoteHangup(Intent intent) { private void handleEndedRemoteHangup(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteHangup(): call_id: " + remotePeer.getCallId()); Log.i(TAG, "handleEndedRemoteHangup(): call_id: " + remotePeer.getCallId());
@ -1082,80 +1107,102 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
if (incomingBeforeAccept) { if (incomingBeforeAccept) {
insertMissedCall(remotePeer, true); insertMissedCall(remotePeer, true);
} }
terminate(remotePeer);
} }
private void handleEndedRemoteHangupAccepted(Intent intent) { private void handleEndedRemoteHangupAccepted(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteHangupAccepted(): call_id: " + remotePeer.getCallId());
if (remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.CALL_ACCEPTED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_ACCEPTED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
terminate(remotePeer);
} }
private void handleEndedRemoteHangupBusy(Intent intent) { private void handleEndedRemoteHangupBusy(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteHangupBusy(): call_id: " + remotePeer.getCallId());
if (remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.CALL_ONGOING_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_ONGOING_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
terminate(remotePeer);
} }
private void handleEndedRemoteHangupDeclined(Intent intent) { private void handleEndedRemoteHangupDeclined(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteHangupDeclined(): call_id: " + remotePeer.getCallId());
if (remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.CALL_DECLINED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_DECLINED_ELSEWHERE, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
}
private void delayedBusyFinish(CallId callId) { terminate(remotePeer);
if (activePeer != null && callId.equals(activePeer.getCallId())) {
Log.i(TAG, "delayedBusyFinish(): calling terminate()");
terminate();
}
} }
private void handleEndedRemoteBusy(Intent intent) { private void handleEndedRemoteBusy(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
CallId callId = remotePeer.getCallId();
Log.i(TAG, "handleEndedRemoteBusy(): call_id: " + callId); Log.i(TAG, "handleEndedRemoteBusy(): call_id: " + remotePeer.getCallId());
if (!remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
Log.w(TAG, "handleEndedRemoteBusy(): Ignoring for inactive call."); activePeer.receivedBusy();
return; busyPeer = activePeer;
OutgoingRinger ringer = new OutgoingRinger(this);
ringer.start(OutgoingRinger.Type.BUSY);
Util.runOnMainDelayed(() -> {
ringer.stop();
busyPeer = null;
}, BUSY_TONE_LENGTH);
sendMessage(WebRtcViewModel.State.CALL_BUSY, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
activePeer.receivedBusy(); terminate(remotePeer);
sendMessage(WebRtcViewModel.State.CALL_BUSY, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
audioManager.startOutgoingRinger(OutgoingRinger.Type.BUSY);
Util.runOnMainDelayed(() -> {
delayedBusyFinish(callId);
}, WebRtcCallActivity.BUSY_SIGNAL_DELAY_FINISH);
} }
private void handleEndedRemoteNeedPermission(Intent intent) { private void handleEndedRemoteNeedPermission(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteNeedPermission(): call_id: " + remotePeer.getCallId()); Log.i(TAG, "handleEndedRemoteNeedPermission(): call_id: " + remotePeer.getCallId());
if (remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.CALL_NEEDS_PERMISSION, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_NEEDS_PERMISSION, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
terminate(remotePeer);
} }
private void handleEndedRemoteGlare(Intent intent) { private void handleEndedRemoteGlare(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedRemoteGlare(): call_id: " + remotePeer.getCallId()); Log.i(TAG, "handleEndedRemoteGlare(): call_id: " + remotePeer.getCallId());
handleEndedRemoteBusy(intent);
if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, remotePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
}
boolean incomingBeforeAccept = remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING;
if (incomingBeforeAccept) {
insertMissedCall(remotePeer, true);
}
terminate(remotePeer);
} }
private void handleEndedFailure(Intent intent) { private void handleEndedFailure(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); RemotePeer remotePeer = getRemotePeerFromMap(intent);
Log.i(TAG, "handleEndedFailure(): call_id: " + remotePeer.getCallId()); Log.i(TAG, "handleEndedFailure(): call_id: " + remotePeer.getCallId());
if (remotePeer.callIdEquals(activePeer)) { if (remotePeer.callIdEquals(activePeer)) {
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
@ -1163,6 +1210,8 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
if (remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING) { if (remotePeer.getState() == CallState.ANSWERING || remotePeer.getState() == CallState.LOCAL_RINGING) {
insertMissedCall(remotePeer, true); insertMissedCall(remotePeer, true);
} }
terminate(remotePeer);
} }
private void handleEndedTimeout(Intent intent) { private void handleEndedTimeout(Intent intent) {
@ -1190,22 +1239,15 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
private void handleCallConcluded(Intent intent) { private void handleCallConcluded(Intent intent) {
RemotePeer remotePeer = getRemotePeer(intent); Log.i(TAG, "handleCallConcluded():");
Log.i(TAG, "handleCallConcluded(): call_id: " + remotePeer.getCallId()); RemotePeer remotePeer = getRemotePeerFromMap(intent);
if (!remotePeer.callIdEquals(activePeer)) {
Log.w(TAG, "handleCallConcluded(): Ignoring for inactive call.");
return;
}
boolean terminateAlreadyScheduled = activePeer.getState() == CallState.RECEIVED_BUSY; Log.i(TAG, "delete remotePeer callId: " + remotePeer.getCallId() + " key: " + remotePeer.hashCode());
if (!terminateAlreadyScheduled) {
terminate(); peerMap.delete(remotePeer.hashCode());
}
} }
/// Helper Methods
private boolean isIdle() { private boolean isIdle() {
return activePeer == null; return activePeer == null;
} }
@ -1222,7 +1264,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
camera = new Camera(WebRtcCallService.this, WebRtcCallService.this, eglBase); camera = new Camera(WebRtcCallService.this, WebRtcCallService.this, eglBase);
localCameraState = camera.getCameraState(); localCameraState = camera.getCameraState();
}); });
} }
@ -1231,11 +1272,16 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
CallNotificationBuilder.getCallInProgressNotification(this, type, remotePeer.getRecipient())); CallNotificationBuilder.getCallInProgressNotification(this, type, remotePeer.getRecipient()));
} }
private synchronized void terminate() { private synchronized void terminate(RemotePeer remotePeer) {
Log.i(TAG, "terminate()"); Log.i(TAG, "terminate():");
if (activePeer == null) { if (activePeer == null) {
Log.i(TAG, "terminate(): skipping with no active peer"); Log.i(TAG, "skipping with no active peer");
return;
}
if (!remotePeer.callIdEquals(activePeer)) {
Log.i(TAG, "skipping remotePeer is not active peer");
return; return;
} }
@ -1264,11 +1310,13 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
this.localCameraState = CameraState.UNKNOWN; this.localCameraState = CameraState.UNKNOWN;
this.activePeer = null;
this.microphoneEnabled = true; this.microphoneEnabled = true;
this.remoteVideoEnabled = false; this.remoteVideoEnabled = false;
this.enableVideoOnCreate = false; this.enableVideoOnCreate = false;
Log.i(TAG, "clear activePeer callId: " + activePeer.getCallId() + " key: " + activePeer.hashCode());
this.activePeer = null;
lockManager.updatePhoneState(LockManager.PhoneState.IDLE); lockManager.updatePhoneState(LockManager.PhoneState.IDLE);
} }
@ -1356,8 +1404,29 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
return remotePeer; return remotePeer;
} }
private static @NonNull int getRemotePeerKey(Intent intent) {
if (!intent.getExtras().containsKey(EXTRA_REMOTE_PEER_KEY)) {
throw new AssertionError("No RemotePeer key in intent!");
}
// The default of -1 should never be applied since the key exists.
int remotePeerKey = intent.getIntExtra(EXTRA_REMOTE_PEER_KEY, -1);
return remotePeerKey;
}
private @NonNull RemotePeer getRemotePeerFromMap(Intent intent) {
int remotePeerKey = getRemotePeerKey(intent);
RemotePeer remotePeer = peerMap.get(remotePeerKey);
if (remotePeer == null) {
throw new AssertionError("No RemotePeer in map for key: " + remotePeerKey + "!");
}
return remotePeer;
}
private void callFailure(String message, Throwable error) { private void callFailure(String message, Throwable error) {
Log.w(TAG, message, error); Log.w(TAG, "callFailure(): " + message, error);
if (activePeer != null) { if (activePeer != null) {
sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.CALL_DISCONNECTED, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
@ -1373,7 +1442,9 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
Log.w(TAG, "No call manager, not reseting. Error message: " + message , error); Log.w(TAG, "No call manager, not reseting. Error message: " + message , error);
} }
terminate(); terminate(activePeer);
peerMap.clear();
} }
private static @NonNull CallManager.CallMediaType getCallMediaTypeFromOfferType(@NonNull OfferMessage.Type offerType) { private static @NonNull CallManager.CallMediaType getCallMediaTypeFromOfferType(@NonNull OfferMessage.Type offerType) {
@ -1645,7 +1716,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} else if (error instanceof IOException) { } else if (error instanceof IOException) {
sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer); sendMessage(WebRtcViewModel.State.NETWORK_FAILURE, activePeer, localCameraState, remoteVideoEnabled, bluetoothAvailable, microphoneEnabled, isRemoteVideoOffer);
} }
} }
} }
@ -1663,34 +1733,32 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
} }
} }
// CallManager observer callbacks
@Override @Override
public void onStartCall(Remote remote, CallId callId, Boolean isOutgoing, CallManager.CallMediaType callMediaType) { public void onStartCall(Remote remote, CallId callId, Boolean isOutgoing, CallManager.CallMediaType callMediaType) {
Log.i(TAG, "onStartCall: callId: " + callId + ", outgoing: " + isOutgoing + ", type: " + callMediaType); Log.i(TAG, "onStartCall(): callId: " + callId + ", outgoing: " + isOutgoing + ", type: " + callMediaType);
if (activePeer != null) {
throw new IllegalStateException("activePeer already set for START_OUTGOING_CALL");
}
if (remote instanceof RemotePeer) { if (remote instanceof RemotePeer) {
activePeer = (RemotePeer)remote; RemotePeer remotePeer = (RemotePeer) remote;
if (peerMap.get(remotePeer.hashCode()) == null) {
Log.w(TAG, "remotePeer not found in map with key: " + remotePeer.hashCode() + "! Dropping.");
try {
callManager.drop(callId);
} catch (CallException e) {
callFailure("callManager.drop() failed: ", e);
}
}
remotePeer.setCallId(callId);
Intent intent = new Intent(this, WebRtcCallService.class); Intent intent = new Intent(this, WebRtcCallService.class);
AudioManager audioManager = ServiceUtil.getAudioManager(this);
audioManager.setSpeakerphoneOn(false);
if (isOutgoing) { if (isOutgoing) {
intent.setAction(ACTION_START_OUTGOING_CALL); intent.setAction(ACTION_START_OUTGOING_CALL);
activePeer.dialing(callId);
} else { } else {
intent.setAction(ACTION_START_INCOMING_CALL); intent.setAction(ACTION_START_INCOMING_CALL);
activePeer.answering(callId);
} }
intent.putExtra(EXTRA_REMOTE_PEER, activePeer) intent.putExtra(EXTRA_REMOTE_PEER_KEY, remotePeer.hashCode());
.putExtra(EXTRA_CALL_ID, callId.longValue());
startService(intent); startService(intent);
} else { } else {
@ -1701,11 +1769,15 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
@Override @Override
public void onCallEvent(Remote remote, CallEvent event) { public void onCallEvent(Remote remote, CallEvent event) {
if (remote instanceof RemotePeer) { if (remote instanceof RemotePeer) {
RemotePeer remotePeer = (RemotePeer)remote; RemotePeer remotePeer = (RemotePeer) remote;
Intent intent = new Intent(this, WebRtcCallService.class); if (peerMap.get(remotePeer.hashCode()) == null) {
throw new AssertionError("remotePeer not found in map!");
}
Log.i(TAG, "onCallEvent: call_id: " + remotePeer.getCallId() + ", event: " + event); Log.i(TAG, "onCallEvent(): call_id: " + remotePeer.getCallId() + ", state: " + remotePeer.getState() + ", event: " + event);
intent.putExtra(EXTRA_REMOTE_PEER, remotePeer);
Intent intent = new Intent(this, WebRtcCallService.class);
intent.putExtra(EXTRA_REMOTE_PEER_KEY, remotePeer.hashCode());
switch (event) { switch (event) {
case LOCAL_RINGING: case LOCAL_RINGING:
@ -1790,11 +1862,13 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public void onCallConcluded(Remote remote) { public void onCallConcluded(Remote remote) {
if (remote instanceof RemotePeer) { if (remote instanceof RemotePeer) {
RemotePeer remotePeer = (RemotePeer)remote; RemotePeer remotePeer = (RemotePeer)remote;
Intent intent = new Intent(this, WebRtcCallService.class);
Log.i(TAG, "onCallConcluded: call_id: " + remotePeer.getCallId()); Log.i(TAG, "onCallConcluded: call_id: " + remotePeer.getCallId());
Intent intent = new Intent(this, WebRtcCallService.class);
intent.setAction(ACTION_CALL_CONCLUDED) intent.setAction(ACTION_CALL_CONCLUDED)
.putExtra(EXTRA_REMOTE_PEER, remotePeer); .putExtra(EXTRA_REMOTE_PEER_KEY, remotePeer.hashCode());
startService(intent); startService(intent);
} else { } else {
throw new AssertionError("Received remote is not instanceof RemotePeer"); throw new AssertionError("Received remote is not instanceof RemotePeer");