Detailed changes
@@ -120,7 +120,7 @@ public final class Config {
public static final boolean DISABLE_PROXY_LOOKUP =
false; // disables STUN/TURN and Proxy65 look up (useful to debug IBB fallback)
public static final boolean USE_DIRECT_JINGLE_CANDIDATES = true;
- public static final boolean USE_JINGLE_DIRECT_INIT = true;
+ public static final boolean USE_JINGLE_MESSAGE_INIT = true;
public static final boolean DISABLE_HTTP_UPLOAD = false;
public static final boolean EXTENDED_SM_LOGGING = false; // log stanza counts
public static final boolean BACKGROUND_STANZA_LOGGING =
@@ -1,6 +1,8 @@
package eu.siacs.conversations.services;
import android.content.Context;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
import android.net.Uri;
import android.os.Build;
import android.telecom.CallAudioState;
@@ -20,11 +22,13 @@ import com.google.common.collect.Lists;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.ui.util.MainThreadExecutor;
import eu.siacs.conversations.xmpp.Jid;
+import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.Media;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class CallIntegration extends Connection {
@@ -32,6 +36,7 @@ public class CallIntegration extends Connection {
private final AppRTCAudioManager appRTCAudioManager;
private AudioDevice initialAudioDevice = null;
private final AtomicBoolean initialAudioDeviceConfigured = new AtomicBoolean(false);
+ private final AtomicBoolean delayedDestructionInitiated = new AtomicBoolean(false);
private List<CallEndpoint> availableEndpoints = Collections.emptyList();
@@ -302,7 +307,9 @@ public class CallIntegration extends Connection {
public void success() {
Log.d(Config.LOGTAG, "CallIntegration.success()");
- this.destroyWith(new DisconnectCause(DisconnectCause.LOCAL, null));
+ final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 100);
+ toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375);
+ this.destroyWithDelay(new DisconnectCause(DisconnectCause.LOCAL, null), 375);
}
public void accepted() {
@@ -316,6 +323,9 @@ public class CallIntegration extends Connection {
public void error() {
Log.d(Config.LOGTAG, "CallIntegration.error()");
+ final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80);
+ toneGenerator.startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375);
+ this.destroyWithDelay(new DisconnectCause(DisconnectCause.ERROR, null), 375);
this.destroyWith(new DisconnectCause(DisconnectCause.ERROR, null));
}
@@ -332,16 +342,33 @@ public class CallIntegration extends Connection {
public void busy() {
Log.d(Config.LOGTAG, "CallIntegration.busy()");
- this.destroyWith(new DisconnectCause(DisconnectCause.BUSY, null));
+ final var toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80);
+ toneGenerator.startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500);
+ this.destroyWithDelay(new DisconnectCause(DisconnectCause.BUSY, null), 2500);
+ }
+
+ private void destroyWithDelay(final DisconnectCause disconnectCause, final int delay) {
+ if (this.delayedDestructionInitiated.compareAndSet(false, true)) {
+ JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(
+ () -> {
+ this.setDisconnected(disconnectCause);
+ this.destroy();
+ },
+ delay,
+ TimeUnit.MILLISECONDS);
+ } else {
+ Log.w(Config.LOGTAG, "CallIntegration destruction has already been scheduled!");
+ }
}
private void destroyWith(final DisconnectCause disconnectCause) {
- if (this.getState() == STATE_DISCONNECTED) {
+ if (this.getState() == STATE_DISCONNECTED || this.delayedDestructionInitiated.get()) {
Log.d(Config.LOGTAG, "CallIntegration has already been destroyed");
return;
}
this.setDisconnected(disconnectCause);
this.destroy();
+ Log.d(Config.LOGTAG, "destroyed!");
}
public static Uri address(final Jid contact) {
@@ -349,7 +376,7 @@ public class CallIntegration extends Connection {
}
public void verifyDisconnected() {
- if (this.getState() == STATE_DISCONNECTED) {
+ if (this.getState() == STATE_DISCONNECTED || this.delayedDestructionInitiated.get()) {
return;
}
throw new AssertionError("CallIntegration has not been disconnected");
@@ -1633,7 +1633,7 @@ public class ConversationFragment extends XmppFragment
activity.xmppConnectionService.updateAccount(account);
}
final Contact contact = conversation.getContact();
- if (Config.USE_JINGLE_DIRECT_INIT && RtpCapability.jmiSupport(contact)) {
+ if (Config.USE_JINGLE_MESSAGE_INIT && RtpCapability.jmiSupport(contact)) {
triggerRtpSession(contact.getAccount(), contact.getJid().asBareJid(), action);
} else {
final RtpCapability.Capability capability;
@@ -54,9 +54,8 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class JingleConnectionManager extends AbstractConnectionManager {
- static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
+ public static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE =
Executors.newSingleThreadScheduledExecutor();
- final ToneManager toneManager;
private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals =
new HashMap<>();
private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection>
@@ -67,7 +66,6 @@ public class JingleConnectionManager extends AbstractConnectionManager {
public JingleConnectionManager(XmppConnectionService service) {
super(service);
- this.toneManager = new ToneManager(service);
}
static String nextRandomId() {
@@ -490,7 +488,6 @@ public class JingleConnectionManager extends AbstractConnectionManager {
proposal.callIntegration.busy();
writeLogMissedOutgoing(
account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
- toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
account,
proposal.with,
@@ -667,7 +664,6 @@ public class JingleConnectionManager extends AbstractConnectionManager {
private void retractSessionProposal(final RtpSessionProposal rtpSessionProposal) {
final Account account = rtpSessionProposal.account;
- toneManager.transition(RtpEndUserState.ENDED, rtpSessionProposal.media);
Log.d(
Config.LOGTAG,
account.getJid().asBareJid()
@@ -713,7 +709,6 @@ public class JingleConnectionManager extends AbstractConnectionManager {
if (preexistingState != null
&& preexistingState != DeviceDiscoveryState.FAILED) {
final RtpEndUserState endUserState = preexistingState.toEndUserState();
- toneManager.transition(endUserState, media);
mXmppConnectionService.notifyJingleRtpConnectionUpdate(
account, with, proposal.sessionId, endUserState);
return proposal;
@@ -2727,7 +2727,6 @@ public class JingleRtpConnection extends AbstractJingleConnection
private void updateEndUserState() {
final RtpEndUserState endUserState = getEndUserState();
- jingleConnectionManager.toneManager.transition(isInitiator(), endUserState, getMedia());
this.updateCallIntegrationState();
xmppConnectionService.notifyJingleRtpConnectionUpdate(
id.account, id.with, id.sessionId, endUserState);
@@ -1,238 +0,0 @@
-package eu.siacs.conversations.xmpp.jingle;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.os.Build;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import eu.siacs.conversations.Config;
-
-import static java.util.Arrays.asList;
-
-import androidx.core.content.ContextCompat;
-
-class ToneManager {
-
- private ToneGenerator toneGenerator;
- private final Context context;
-
- private ToneState state = null;
- private RtpEndUserState endUserState = null;
- private ScheduledFuture<?> currentTone;
- private ScheduledFuture<?> currentResetFuture;
- private boolean appRtcAudioManagerHasControl = false;
-
- ToneManager(final Context context) {
- this.context = context;
- }
-
- private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
- if (isInitiator) {
- if (asList(RtpEndUserState.FINDING_DEVICE, RtpEndUserState.RINGING, RtpEndUserState.CONNECTING).contains(state)) {
- return ToneState.RINGING;
- }
- if (state == RtpEndUserState.DECLINED_OR_BUSY) {
- return ToneState.BUSY;
- }
- }
- if (state == RtpEndUserState.ENDING_CALL) {
- if (media.contains(Media.VIDEO)) {
- return ToneState.NULL;
- } else {
- return ToneState.ENDING_CALL;
- }
- }
- if (Arrays.asList(
- RtpEndUserState.CONNECTED,
- RtpEndUserState.RECONNECTING,
- RtpEndUserState.INCOMING_CONTENT_ADD)
- .contains(state)) {
- if (media.contains(Media.VIDEO)) {
- return ToneState.NULL;
- } else {
- return ToneState.CONNECTED;
- }
- }
- return ToneState.NULL;
- }
-
- void transition(final RtpEndUserState state, final Set<Media> media) {
- transition(state, of(true, state, media), media);
- }
-
- void transition(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
- transition(state, of(isInitiator, state, media), media);
- }
-
- private synchronized void transition(final RtpEndUserState endUserState, final ToneState state, final Set<Media> media) {
- final RtpEndUserState normalizeEndUserState = normalize(endUserState);
- if (this.endUserState == normalizeEndUserState) {
- return;
- }
- this.endUserState = normalizeEndUserState;
- if (this.state == state) {
- return;
- }
- if (state == ToneState.NULL && this.state == ToneState.ENDING_CALL) {
- return;
- }
- cancelCurrentTone();
- Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")");
- if (state != ToneState.NULL) {
- configureAudioManagerForCall(media);
- }
- switch (state) {
- case RINGING:
- // ringing can be removed as this is now handled by 'CallIntegration'
- //scheduleWaitingTone();
- break;
- case CONNECTED:
- scheduleConnected();
- break;
- case BUSY:
- scheduleBusy();
- break;
- case ENDING_CALL:
- scheduleEnding();
- break;
- case NULL:
- if (noResetScheduled()) {
- resetAudioManager();
- }
- break;
- default:
- throw new IllegalStateException("Unable to handle transition to "+state);
- }
- this.state = state;
- }
-
- private static RtpEndUserState normalize(final RtpEndUserState endUserState) {
- if (Arrays.asList(
- RtpEndUserState.CONNECTED,
- RtpEndUserState.RECONNECTING,
- RtpEndUserState.INCOMING_CONTENT_ADD)
- .contains(endUserState)) {
- return RtpEndUserState.CONNECTED;
- } else {
- return endUserState;
- }
- }
-
- void setAppRtcAudioManagerHasControl(final boolean appRtcAudioManagerHasControl) {
- this.appRtcAudioManagerHasControl = appRtcAudioManagerHasControl;
- }
-
- private void scheduleConnected() {
- this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
- startTone(ToneGenerator.TONE_PROP_PROMPT, 200);
- }, 0, TimeUnit.SECONDS);
- }
-
- private void scheduleEnding() {
- this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
- startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375);
- }, 0, TimeUnit.SECONDS);
- this.currentResetFuture = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(this::resetAudioManager, 375, TimeUnit.MILLISECONDS);
- }
-
- private void scheduleBusy() {
- this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
- startTone(ToneGenerator.TONE_CDMA_NETWORK_BUSY, 2500);
- }, 0, TimeUnit.SECONDS);
- this.currentResetFuture = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(this::resetAudioManager, 2500, TimeUnit.MILLISECONDS);
- }
-
- private void scheduleWaitingTone() {
- this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(() -> {
- startTone(ToneGenerator.TONE_CDMA_DIAL_TONE_LITE, 750);
- }, 0, 3, TimeUnit.SECONDS);
- }
-
- private boolean noResetScheduled() {
- return this.currentResetFuture == null || this.currentResetFuture.isDone();
- }
-
- private void cancelCurrentTone() {
- if (currentTone != null) {
- currentTone.cancel(true);
- }
- stopTone(toneGenerator);
- }
-
- private static void stopTone(final ToneGenerator toneGenerator) {
- if (toneGenerator == null) {
- return;
- }
- try {
- toneGenerator.stopTone();
- } catch (final RuntimeException e) {
- Log.w(Config.LOGTAG,"tone has already stopped");
- }
- }
-
- private void startTone(final int toneType, final int durationMs) {
- if (this.toneGenerator != null) {
- this.toneGenerator.release();;
-
- }
- final AudioManager audioManager = ContextCompat.getSystemService(context, AudioManager.class);
- final boolean ringerModeNormal = audioManager == null || audioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
- this.toneGenerator = getToneGenerator(ringerModeNormal);
- if (toneGenerator != null) {
- this.toneGenerator.startTone(toneType, durationMs);
- }
- }
-
- private static ToneGenerator getToneGenerator(final boolean ringerModeNormal) {
- try {
- // when silent and on Android 12+ use STREAM_MUSIC
- if (ringerModeNormal || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
- return new ToneGenerator(AudioManager.STREAM_VOICE_CALL,60);
- } else {
- return new ToneGenerator(AudioManager.STREAM_MUSIC,100);
- }
- } catch (final Exception e) {
- Log.d(Config.LOGTAG,"could not create tone generator",e);
- return null;
- }
- }
-
- private void configureAudioManagerForCall(final Set<Media> media) {
- if (appRtcAudioManagerHasControl) {
- Log.d(Config.LOGTAG, ToneManager.class.getName() + ": do not configure audio manager because RTC has control");
- return;
- }
- final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- if (audioManager == null) {
- return;
- }
- final boolean isSpeakerPhone = media.contains(Media.VIDEO);
- Log.d(Config.LOGTAG, ToneManager.class.getName() + ": putting AudioManager into communication mode. speaker=" + isSpeakerPhone);
- audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
- audioManager.setSpeakerphoneOn(isSpeakerPhone);
- }
-
- private void resetAudioManager() {
- if (appRtcAudioManagerHasControl) {
- Log.d(Config.LOGTAG, ToneManager.class.getName() + ": do not reset audio manager because RTC has control");
- return;
- }
- final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- if (audioManager == null) {
- return;
- }
- Log.d(Config.LOGTAG, ToneManager.class.getName() + ": putting AudioManager back into normal mode");
- audioManager.setMode(AudioManager.MODE_NORMAL);
- audioManager.setSpeakerphoneOn(false);
- }
-
- private enum ToneState {
- NULL, RINGING, CONNECTED, BUSY, ENDING_CALL
- }
-}
@@ -2,8 +2,6 @@ package eu.siacs.conversations.xmpp.jingle;
import android.content.Context;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import com.google.common.base.Optional;
@@ -15,8 +13,6 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import eu.siacs.conversations.Config;
-import eu.siacs.conversations.services.AppRTCAudioManager;
-import eu.siacs.conversations.services.CallIntegration;
import eu.siacs.conversations.services.XmppConnectionService;
import org.webrtc.AudioSource;
@@ -52,7 +48,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-@SuppressWarnings("UnstableApiUsage")
public class WebRTCWrapper {
private static final String EXTENDED_LOGGING_TAG = WebRTCWrapper.class.getSimpleName();
@@ -205,7 +200,6 @@ public class WebRTCWrapper {
};
@Nullable private PeerConnectionFactory peerConnectionFactory = null;
@Nullable private PeerConnection peerConnection = null;
- private ToneManager toneManager = null;
private Context context = null;
private EglBase eglBase = null;
private VideoSourceWrapper videoSourceWrapper;
@@ -222,8 +216,7 @@ public class WebRTCWrapper {
}
}
- public void setup(final XmppConnectionService service)
- throws InitializationException {
+ public void setup(final XmppConnectionService service) throws InitializationException {
try {
PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions.builder(service)
@@ -238,7 +231,6 @@ public class WebRTCWrapper {
throw new InitializationException("Unable to create EGL base", e);
}
this.context = service;
- this.toneManager = service.getJingleConnectionManager().toneManager;
}
synchronized void initializePeerConnection(