give TonManager control over audio mode to play dial tones on earpiece. fixes #3738
This commit is contained in:
		
							parent
							
								
									a2a7256682
								
							
						
					
					
						commit
						685e01e83f
					
				| 
						 | 
				
			
			@ -38,6 +38,7 @@ import eu.siacs.conversations.services.AbstractConnectionManager;
 | 
			
		|||
import eu.siacs.conversations.services.XmppConnectionService;
 | 
			
		||||
import eu.siacs.conversations.xml.Element;
 | 
			
		||||
import eu.siacs.conversations.xml.Namespace;
 | 
			
		||||
import eu.siacs.conversations.xmpp.Jid;
 | 
			
		||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.Content;
 | 
			
		||||
import eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription;
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +49,10 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
 | 
			
		|||
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 | 
			
		||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 | 
			
		||||
import eu.siacs.conversations.xmpp.Jid;
 | 
			
		||||
 | 
			
		||||
public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		||||
    static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
 | 
			
		||||
    final ToneManager toneManager = new ToneManager();
 | 
			
		||||
    final ToneManager toneManager;
 | 
			
		||||
    private final HashMap<RtpSessionProposal, DeviceDiscoveryState> rtpSessionProposals = new HashMap<>();
 | 
			
		||||
    private final ConcurrentHashMap<AbstractJingleConnection.Id, AbstractJingleConnection> connections = new ConcurrentHashMap<>();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +64,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		|||
 | 
			
		||||
    public JingleConnectionManager(XmppConnectionService service) {
 | 
			
		||||
        super(service);
 | 
			
		||||
        this.toneManager = new ToneManager(service);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String nextRandomId() {
 | 
			
		||||
| 
						 | 
				
			
			@ -333,11 +334,11 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if (addressedDirectly && "reject".equals(message.getName())) {
 | 
			
		||||
            final RtpSessionProposal proposal = new RtpSessionProposal(account, from.asBareJid(), sessionId);
 | 
			
		||||
            final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
 | 
			
		||||
            synchronized (rtpSessionProposals) {
 | 
			
		||||
                if (rtpSessionProposals.remove(proposal) != null) {
 | 
			
		||||
                if (proposal != null && rtpSessionProposals.remove(proposal) != null) {
 | 
			
		||||
                    writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
 | 
			
		||||
                    toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY);
 | 
			
		||||
                    toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media);
 | 
			
		||||
                    mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
 | 
			
		||||
                } else {
 | 
			
		||||
                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
 | 
			
		||||
| 
						 | 
				
			
			@ -511,7 +512,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		|||
 | 
			
		||||
    private void retractSessionProposal(RtpSessionProposal rtpSessionProposal) {
 | 
			
		||||
        final Account account = rtpSessionProposal.account;
 | 
			
		||||
        toneManager.transition(RtpEndUserState.ENDED);
 | 
			
		||||
        toneManager.transition(RtpEndUserState.ENDED, rtpSessionProposal.media);
 | 
			
		||||
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retracting rtp session proposal with " + rtpSessionProposal.with);
 | 
			
		||||
        this.rtpSessionProposals.remove(rtpSessionProposal);
 | 
			
		||||
        final MessagePacket messagePacket = mXmppConnectionService.getMessageGenerator().sessionRetract(rtpSessionProposal);
 | 
			
		||||
| 
						 | 
				
			
			@ -527,7 +528,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		|||
                    final DeviceDiscoveryState preexistingState = entry.getValue();
 | 
			
		||||
                    if (preexistingState != null && preexistingState != DeviceDiscoveryState.FAILED) {
 | 
			
		||||
                        final RtpEndUserState endUserState = preexistingState.toEndUserState();
 | 
			
		||||
                        toneManager.transition(endUserState);
 | 
			
		||||
                        toneManager.transition(endUserState, media);
 | 
			
		||||
                        mXmppConnectionService.notifyJingleRtpConnectionUpdate(
 | 
			
		||||
                                account,
 | 
			
		||||
                                with,
 | 
			
		||||
| 
						 | 
				
			
			@ -623,7 +624,7 @@ public class JingleConnectionManager extends AbstractConnectionManager {
 | 
			
		|||
            }
 | 
			
		||||
            this.rtpSessionProposals.put(sessionProposal, target);
 | 
			
		||||
            final RtpEndUserState endUserState = target.toEndUserState();
 | 
			
		||||
            toneManager.transition(endUserState);
 | 
			
		||||
            toneManager.transition(endUserState, sessionProposal.media);
 | 
			
		||||
            mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, sessionProposal.with, sessionProposal.sessionId, endUserState);
 | 
			
		||||
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": flagging session " + sessionId + " as " + target);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
package eu.siacs.conversations.xmpp.jingle;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.media.AudioManager;
 | 
			
		||||
import android.media.ToneGenerator;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.ScheduledFuture;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,27 +16,22 @@ import static java.util.Arrays.asList;
 | 
			
		|||
class ToneManager {
 | 
			
		||||
 | 
			
		||||
    private final ToneGenerator toneGenerator;
 | 
			
		||||
    private final Context context;
 | 
			
		||||
 | 
			
		||||
    private ToneState state = null;
 | 
			
		||||
    private ScheduledFuture<?> currentTone;
 | 
			
		||||
    private boolean appRtcAudioManagerHasControl = false;
 | 
			
		||||
 | 
			
		||||
    ToneManager() {
 | 
			
		||||
    ToneManager(final Context context) {
 | 
			
		||||
        ToneGenerator toneGenerator;
 | 
			
		||||
        try {
 | 
			
		||||
            toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 35);
 | 
			
		||||
            toneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 60);
 | 
			
		||||
        } catch (final RuntimeException e) {
 | 
			
		||||
            Log.e(Config.LOGTAG, "unable to instantiate ToneGenerator", e);
 | 
			
		||||
            toneGenerator = null;
 | 
			
		||||
        }
 | 
			
		||||
        this.toneGenerator = toneGenerator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void transition(final RtpEndUserState state) {
 | 
			
		||||
        transition(of(true, state, Collections.emptySet()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void transition(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
 | 
			
		||||
        transition(of(isInitiator, state, media));
 | 
			
		||||
        this.context = context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static ToneState of(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +60,15 @@ class ToneManager {
 | 
			
		|||
        return ToneState.NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void transition(ToneState state) {
 | 
			
		||||
    void transition(final RtpEndUserState state, final Set<Media> media) {
 | 
			
		||||
        transition(of(true, state, media), media);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void transition(final boolean isInitiator, final RtpEndUserState state, final Set<Media> media) {
 | 
			
		||||
        transition(of(isInitiator, state, media), media);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private synchronized void transition(ToneState state, final Set<Media> media) {
 | 
			
		||||
        if (this.state == state) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -74,6 +77,9 @@ class ToneManager {
 | 
			
		|||
        }
 | 
			
		||||
        cancelCurrentTone();
 | 
			
		||||
        Log.d(Config.LOGTAG, getClass().getName() + ".transition(" + state + ")");
 | 
			
		||||
        if (state != ToneState.NULL) {
 | 
			
		||||
            configureAudioManagerForCall(media);
 | 
			
		||||
        }
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case RINGING:
 | 
			
		||||
                scheduleWaitingTone();
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +97,10 @@ class ToneManager {
 | 
			
		|||
        this.state = state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setAppRtcAudioManagerHasControl(final boolean appRtcAudioManagerHasControl) {
 | 
			
		||||
        this.appRtcAudioManagerHasControl = appRtcAudioManagerHasControl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void scheduleConnected() {
 | 
			
		||||
        this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
 | 
			
		||||
            startTone(ToneGenerator.TONE_PROP_PROMPT, 200);
 | 
			
		||||
| 
						 | 
				
			
			@ -101,12 +111,14 @@ class ToneManager {
 | 
			
		|||
        this.currentTone = JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(() -> {
 | 
			
		||||
            startTone(ToneGenerator.TONE_CDMA_CALLDROP_LITE, 375);
 | 
			
		||||
        }, 0, TimeUnit.SECONDS);
 | 
			
		||||
        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);
 | 
			
		||||
        JingleConnectionManager.SCHEDULED_EXECUTOR_SERVICE.schedule(this::resetAudioManager, 2500, TimeUnit.MILLISECONDS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void scheduleWaitingTone() {
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +144,35 @@ class ToneManager {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,7 @@ import javax.annotation.Nullable;
 | 
			
		|||
 | 
			
		||||
import eu.siacs.conversations.Config;
 | 
			
		||||
import eu.siacs.conversations.services.AppRTCAudioManager;
 | 
			
		||||
import eu.siacs.conversations.services.XmppConnectionService;
 | 
			
		||||
 | 
			
		||||
public class WebRTCWrapper {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,6 +175,7 @@ public class WebRTCWrapper {
 | 
			
		|||
    private PeerConnection peerConnection = null;
 | 
			
		||||
    private AudioTrack localAudioTrack = null;
 | 
			
		||||
    private AppRTCAudioManager appRTCAudioManager = null;
 | 
			
		||||
    private ToneManager toneManager = null;
 | 
			
		||||
    private Context context = null;
 | 
			
		||||
    private EglBase eglBase = null;
 | 
			
		||||
    private CapturerChoice capturerChoice;
 | 
			
		||||
| 
						 | 
				
			
			@ -206,18 +208,20 @@ public class WebRTCWrapper {
 | 
			
		|||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setup(final Context context, final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) throws InitializationException {
 | 
			
		||||
    public void setup(final XmppConnectionService service, final AppRTCAudioManager.SpeakerPhonePreference speakerPhonePreference) throws InitializationException {
 | 
			
		||||
        try {
 | 
			
		||||
            PeerConnectionFactory.initialize(
 | 
			
		||||
                    PeerConnectionFactory.InitializationOptions.builder(context).createInitializationOptions()
 | 
			
		||||
                    PeerConnectionFactory.InitializationOptions.builder(service).createInitializationOptions()
 | 
			
		||||
            );
 | 
			
		||||
        } catch (final UnsatisfiedLinkError e) {
 | 
			
		||||
            throw new InitializationException(e);
 | 
			
		||||
        }
 | 
			
		||||
        this.eglBase = EglBase.create();
 | 
			
		||||
        this.context = context;
 | 
			
		||||
        this.context = service;
 | 
			
		||||
        this.toneManager = service.getJingleConnectionManager().toneManager;
 | 
			
		||||
        mainHandler.post(() -> {
 | 
			
		||||
            appRTCAudioManager = AppRTCAudioManager.create(context, speakerPhonePreference);
 | 
			
		||||
            appRTCAudioManager = AppRTCAudioManager.create(service, speakerPhonePreference);
 | 
			
		||||
            toneManager.setAppRtcAudioManagerHasControl(true);
 | 
			
		||||
            appRTCAudioManager.start(audioManagerEvents);
 | 
			
		||||
            eventCallback.onAudioDeviceChanged(appRTCAudioManager.getSelectedAudioDevice(), appRTCAudioManager.getAudioDevices());
 | 
			
		||||
        });
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +292,7 @@ public class WebRTCWrapper {
 | 
			
		|||
            this.peerConnection = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (audioManager != null) {
 | 
			
		||||
            toneManager.setAppRtcAudioManagerHasControl(false);
 | 
			
		||||
            mainHandler.post(audioManager::stop);
 | 
			
		||||
        }
 | 
			
		||||
        this.localVideoTrack = null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue