run RtpSessionService during phone calls

This commit is contained in:
Daniel Gultsch 2023-02-24 15:52:23 +01:00
parent 1be1334794
commit 49b4f54285
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
14 changed files with 289 additions and 59 deletions

View file

@ -81,6 +81,8 @@
android:name=".service.ForegroundService" android:name=".service.ForegroundService"
android:exported="false" /> android:exported="false" />
<service android:name=".service.RtpSessionService" android:exported="false" android:foregroundServiceType="phoneCall"/>
<receiver <receiver
android:name=".receiver.EventReceiver" android:name=".receiver.EventReceiver"
android:exported="true"> android:exported="true">

View file

@ -1,6 +1,7 @@
package eu.siacs.conversations.xmpp.jingle; package eu.siacs.conversations.xmpp.jingle;
import android.content.Context; import android.content.Context;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
@ -84,6 +85,14 @@ public abstract class AbstractJingleConnection extends XmppConnection.Delegate {
public int hashCode() { public int hashCode() {
return Objects.hashCode(with, sessionId); return Objects.hashCode(with, sessionId);
} }
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("with", with)
.add("sessionId", sessionId)
.toString();
}
} }
public enum State { public enum State {

View file

@ -30,7 +30,9 @@ import im.conversations.android.BuildConfig;
import im.conversations.android.axolotl.AxolotlEncryptionException; import im.conversations.android.axolotl.AxolotlEncryptionException;
import im.conversations.android.axolotl.AxolotlService; import im.conversations.android.axolotl.AxolotlService;
import im.conversations.android.dns.IP; import im.conversations.android.dns.IP;
import im.conversations.android.notification.OngoingCall;
import im.conversations.android.notification.RtpSessionNotification; import im.conversations.android.notification.RtpSessionNotification;
import im.conversations.android.service.RtpSessionService;
import im.conversations.android.transformer.CallLogTransformation; import im.conversations.android.transformer.CallLogTransformation;
import im.conversations.android.util.BooleanFutures; import im.conversations.android.util.BooleanFutures;
import im.conversations.android.xml.Element; import im.conversations.android.xml.Element;
@ -592,7 +594,10 @@ public class JingleRtpConnection extends AbstractJingleConnection
this.incomingContentAdd = null; this.incomingContentAdd = null;
updateEndUserState(); updateEndUserState();
} else { } else {
LOGGER.info("content add summary {} did not match remove summary {}", contentAddSummary, removeSummary); LOGGER.info(
"content add summary {} did not match remove summary {}",
contentAddSummary,
removeSummary);
webRTCWrapper.close(); webRTCWrapper.close();
sendSessionTerminate( sendSessionTerminate(
Reason.FAILED_APPLICATION, Reason.FAILED_APPLICATION,
@ -2424,6 +2429,7 @@ public class JingleRtpConnection extends AbstractJingleConnection
this.webRTCWrapper.switchSpeakerPhonePreference( this.webRTCWrapper.switchSpeakerPhonePreference(
AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia())); AppRTCAudioManager.SpeakerPhonePreference.of(activeContents.getMedia()));
updateEndUserState(); updateEndUserState();
updateOngoingCallNotification();
} }
private SessionDescription setLocalSessionDescription() private SessionDescription setLocalSessionDescription()
@ -2505,21 +2511,26 @@ public class JingleRtpConnection extends AbstractJingleConnection
private void updateOngoingCallNotification() { private void updateOngoingCallNotification() {
final State state = this.state; final State state = this.state;
if (STATES_SHOWING_ONGOING_CALL.contains(state)) { if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
final boolean reconnecting; RtpSessionService.updateOngoingCall(context, getAccount().id, id);
} else {
RtpSessionService.stop(context);
}
}
public OngoingCall getOngoingCall() {
final State state = this.state;
final boolean reconnecting;
if (STATES_SHOWING_ONGOING_CALL.contains(state)) {
if (state == State.SESSION_ACCEPTED) { if (state == State.SESSION_ACCEPTED) {
reconnecting = reconnecting =
getPeerConnectionStateAsEndUserState() == RtpEndUserState.RECONNECTING; getPeerConnectionStateAsEndUserState() == RtpEndUserState.RECONNECTING;
} else { } else {
reconnecting = false; reconnecting = false;
} }
// TODO decide what we want to do with ongoing call? create a foreground service of
// RtpSessionService?
// xmppConnectionService.setOngoingCall(id, getMedia(), reconnecting);
} else { } else {
// xmppConnectionService.removeOngoingCall(); reconnecting = false;
} }
return new OngoingCall(id, getMedia(), reconnecting);
} }
private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) { private void discoverIceServers(final OnIceServersDiscovered onIceServersDiscovered) {

View file

@ -38,7 +38,9 @@ class TrackWrapper<T extends MediaStreamTrack> {
final RtpTransceiver transceiver = final RtpTransceiver transceiver =
peerConnection == null ? null : getTransceiver(peerConnection, trackWrapper); peerConnection == null ? null : getTransceiver(peerConnection, trackWrapper);
if (transceiver == null) { if (transceiver == null) {
Log.w(Config.LOGTAG, "unable to detect transceiver for " + trackWrapper.getRtpSenderId()); Log.w(
Config.LOGTAG,
"unable to detect transceiver for " + trackWrapper.getRtpSenderId());
return Optional.of(trackWrapper.track); return Optional.of(trackWrapper.track);
} }
final RtpTransceiver.RtpTransceiverDirection direction = transceiver.getDirection(); final RtpTransceiver.RtpTransceiverDirection direction = transceiver.getDirection();
@ -62,10 +64,13 @@ class TrackWrapper<T extends MediaStreamTrack> {
public static <T extends MediaStreamTrack> RtpTransceiver getTransceiver( public static <T extends MediaStreamTrack> RtpTransceiver getTransceiver(
@Nonnull final PeerConnection peerConnection, final TrackWrapper<T> trackWrapper) { @Nonnull final PeerConnection peerConnection, final TrackWrapper<T> trackWrapper) {
final String rtpSenderId = trackWrapper.getRtpSenderId(); final String rtpSenderId = trackWrapper.getRtpSenderId();
if (rtpSenderId == null) {
return null;
}
for (final RtpTransceiver transceiver : peerConnection.getTransceivers()) { for (final RtpTransceiver transceiver : peerConnection.getTransceivers()) {
if (transceiver.getSender().id().equals(rtpSenderId)) { if (transceiver.getSender().id().equals(rtpSenderId)) {
return transceiver; return transceiver;
} }
} }
return null; return null;
} }

View file

@ -12,7 +12,8 @@ import im.conversations.android.R;
public final class Channels { public final class Channels {
static final String CHANNEL_FOREGROUND = "foreground"; static final String CHANNEL_FOREGROUND = "foreground";
static final String INCOMING_CALLS_NOTIFICATION_CHANNEL = "incoming_calls_channel"; static final String CHANNEL_INCOMING_CALL = "incoming_calls_channel";
static final String CHANNEL_ONGOING_CALL = "ongoing_call";
static final String CHANNEL_GROUP_STATUS = "status"; static final String CHANNEL_GROUP_STATUS = "status";
static final String CHANNEL_GROUP_CALLS = "calls"; static final String CHANNEL_GROUP_CALLS = "calls";
private final Application application; private final Application application;
@ -32,9 +33,22 @@ public final class Channels {
this.initializeForegroundChannel(notificationManager); this.initializeForegroundChannel(notificationManager);
this.initializeIncomingCallChannel(notificationManager); this.initializeIncomingCallChannel(notificationManager);
this.initializeOngoingCallChannel(notificationManager);
} }
} }
@RequiresApi(api = Build.VERSION_CODES.O)
private void initializeOngoingCallChannel(NotificationManager notificationManager) {
final NotificationChannel ongoingCallsChannel =
new NotificationChannel(
CHANNEL_ONGOING_CALL,
application.getString(R.string.ongoing_calls_channel_name),
NotificationManager.IMPORTANCE_LOW);
ongoingCallsChannel.setShowBadge(false);
ongoingCallsChannel.setGroup(CHANNEL_GROUP_CALLS);
notificationManager.createNotificationChannel(ongoingCallsChannel);
}
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
private void initializeGroups(NotificationManager notificationManager) { private void initializeGroups(NotificationManager notificationManager) {
notificationManager.createNotificationChannelGroup( notificationManager.createNotificationChannelGroup(
@ -67,7 +81,7 @@ public final class Channels {
private void initializeIncomingCallChannel(final NotificationManager notificationManager) { private void initializeIncomingCallChannel(final NotificationManager notificationManager) {
final NotificationChannel incomingCallsChannel = final NotificationChannel incomingCallsChannel =
new NotificationChannel( new NotificationChannel(
INCOMING_CALLS_NOTIFICATION_CHANNEL, CHANNEL_INCOMING_CALL,
application.getString(R.string.incoming_calls_channel_name), application.getString(R.string.incoming_calls_channel_name),
NotificationManager.IMPORTANCE_HIGH); NotificationManager.IMPORTANCE_HIGH);
incomingCallsChannel.setSound(null, null); incomingCallsChannel.setSound(null, null);

View file

@ -1,5 +1,6 @@
package im.conversations.android.notification; package im.conversations.android.notification;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection; import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.Media;
@ -31,4 +32,13 @@ public class OngoingCall {
public int hashCode() { public int hashCode() {
return Objects.hashCode(id, media, reconnecting); return Objects.hashCode(id, media, reconnecting);
} }
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("media", media)
.add("reconnecting", reconnecting)
.toString();
}
} }

View file

@ -15,6 +15,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Vibrator; import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.service.notification.StatusBarNotification;
import android.util.Log; import android.util.Log;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
@ -40,6 +41,7 @@ public class RtpSessionNotification extends AbstractNotification {
private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionNotification.class); private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionNotification.class);
public static final int INCOMING_CALL_ID = 2; public static final int INCOMING_CALL_ID = 2;
public static final int ONGOING_CALL_ID = 3;
public static final int LED_COLOR = 0xff00ff00; public static final int LED_COLOR = 0xff00ff00;
@ -149,8 +151,7 @@ public class RtpSessionNotification extends AbstractNotification {
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
final NotificationCompat.Builder builder = final NotificationCompat.Builder builder =
new NotificationCompat.Builder( new NotificationCompat.Builder(context, Channels.CHANNEL_INCOMING_CALL);
context, Channels.INCOMING_CALLS_NOTIFICATION_CHANNEL);
if (media.contains(Media.VIDEO)) { if (media.contains(Media.VIDEO)) {
builder.setSmallIcon(R.drawable.ic_videocam_24dp); builder.setSmallIcon(R.drawable.ic_videocam_24dp);
builder.setContentTitle(context.getString(R.string.rtp_state_incoming_video_call)); builder.setContentTitle(context.getString(R.string.rtp_state_incoming_video_call));
@ -181,7 +182,7 @@ public class RtpSessionNotification extends AbstractNotification {
R.drawable.ic_call_end_24dp, R.drawable.ic_call_end_24dp,
context.getString(R.string.dismiss_call), context.getString(R.string.dismiss_call),
createCallAction( createCallAction(
id.sessionId, RtpSessionService.ACTION_DISMISS_CALL, 102)) account, id, RtpSessionService.ACTION_REJECT_CALL, 102))
.build()); .build());
builder.addAction( builder.addAction(
new NotificationCompat.Action.Builder( new NotificationCompat.Action.Builder(
@ -199,7 +200,7 @@ public class RtpSessionNotification extends AbstractNotification {
public Notification getOngoingCallNotification(final Account account, OngoingCall ongoingCall) { public Notification getOngoingCallNotification(final Account account, OngoingCall ongoingCall) {
final AbstractJingleConnection.Id id = ongoingCall.id; final AbstractJingleConnection.Id id = ongoingCall.id;
final NotificationCompat.Builder builder = final NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, "ongoing_calls"); new NotificationCompat.Builder(context, Channels.CHANNEL_ONGOING_CALL);
if (ongoingCall.media.contains(Media.VIDEO)) { if (ongoingCall.media.contains(Media.VIDEO)) {
builder.setSmallIcon(R.drawable.ic_videocam_24dp); builder.setSmallIcon(R.drawable.ic_videocam_24dp);
if (ongoingCall.reconnecting) { if (ongoingCall.reconnecting) {
@ -216,7 +217,8 @@ public class RtpSessionNotification extends AbstractNotification {
} }
} }
// TODO fix me when we have a Contact model // TODO fix me when we have a Contact model
// builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName()); builder.setContentText(
"Contact Name"); // id.account.getRoster().getContact(id.with).getDisplayName());
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setCategory(NotificationCompat.CATEGORY_CALL); builder.setCategory(NotificationCompat.CATEGORY_CALL);
@ -227,11 +229,26 @@ public class RtpSessionNotification extends AbstractNotification {
R.drawable.ic_call_end_24dp, R.drawable.ic_call_end_24dp,
context.getString(R.string.hang_up), context.getString(R.string.hang_up),
createCallAction( createCallAction(
id.sessionId, RtpSessionService.ACTION_END_CALL, 104)) account, id, RtpSessionService.ACTION_END_CALL, 104))
.build()); .build());
return builder.build(); return builder.build();
} }
public static boolean isShowingOngoingCallNotification(final Context context) {
final var notificationManager =
ContextCompat.getSystemService(context, NotificationManager.class);
if (notificationManager == null) {
return false;
}
for (final StatusBarNotification statusBarNotification :
notificationManager.getActiveNotifications()) {
if (statusBarNotification.getId() == ONGOING_CALL_ID) {
return true;
}
}
return false;
}
private PendingIntent createPendingRtpSession( private PendingIntent createPendingRtpSession(
final Account account, final Account account,
final AbstractJingleConnection.Id id, final AbstractJingleConnection.Id id,
@ -249,11 +266,14 @@ public class RtpSessionNotification extends AbstractNotification {
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
} }
private PendingIntent createCallAction(String sessionId, final String action, int requestCode) { private PendingIntent createCallAction(
Account account, AbstractJingleConnection.Id id, final String action, int requestCode) {
final Intent intent = new Intent(context, RtpSessionService.class); final Intent intent = new Intent(context, RtpSessionService.class);
intent.setAction(action); intent.setAction(action);
intent.setPackage(context.getPackageName()); intent.setPackage(context.getPackageName());
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId); intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account.id);
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
return PendingIntent.getService( return PendingIntent.getService(
context, context,
requestCode, requestCode,
@ -273,6 +293,8 @@ public class RtpSessionNotification extends AbstractNotification {
public void pushMissedCallNow(CallLogTransformation message) {} public void pushMissedCallNow(CallLogTransformation message) {}
public void cancelOngoingCallNotification() {}
private class VibrationRunnable implements Runnable { private class VibrationRunnable implements Runnable {
@Override @Override

View file

@ -5,6 +5,7 @@ import android.content.Intent;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleService; import androidx.lifecycle.LifecycleService;
import im.conversations.android.notification.ForegroundServiceNotification; import im.conversations.android.notification.ForegroundServiceNotification;
import im.conversations.android.notification.RtpSessionNotification;
import im.conversations.android.xmpp.ConnectionPool; import im.conversations.android.xmpp.ConnectionPool;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,7 +20,6 @@ public class ForegroundService extends LifecycleService {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
LOGGER.info("Creating service");
final var pool = ConnectionPool.getInstance(this); final var pool = ConnectionPool.getInstance(this);
startForeground( startForeground(
ForegroundServiceNotification.ID, ForegroundServiceNotification.ID,
@ -39,6 +39,14 @@ public class ForegroundService extends LifecycleService {
} }
public static void start(final Context context) { public static void start(final Context context) {
if (RtpSessionNotification.isShowingOngoingCallNotification(context)) {
LOGGER.info("Do not start foreground service. Ongoing call.");
return;
}
startForegroundService(context);
}
static void startForegroundService(final Context context) {
try { try {
ContextCompat.startForegroundService( ContextCompat.startForegroundService(
context, new Intent(context, ForegroundService.class)); context, new Intent(context, ForegroundService.class));
@ -46,4 +54,9 @@ public class ForegroundService extends LifecycleService {
LOGGER.error("Could not start foreground service", e); LOGGER.error("Could not start foreground service", e);
} }
} }
public static void stop(final Context context) {
final var intent = new Intent(context, ForegroundService.class);
context.stopService(intent);
}
} }

View file

@ -1,16 +1,153 @@
package im.conversations.android.service; package im.conversations.android.service;
import android.app.Service;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleService; import androidx.lifecycle.LifecycleService;
import com.google.common.base.Strings;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import im.conversations.android.notification.RtpSessionNotification;
import im.conversations.android.ui.activity.RtpSessionActivity;
import im.conversations.android.xmpp.ConnectionPool;
import im.conversations.android.xmpp.manager.JingleConnectionManager;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RtpSessionService extends LifecycleService { public class RtpSessionService extends LifecycleService {
private static final Logger LOGGER = LoggerFactory.getLogger(RtpSessionService.class);
public static final String ACTION_DISMISS_CALL = "dismiss_call"; public static final String ACTION_REJECT_CALL = "dismiss_call";
public static final String ACTION_END_CALL = "end_call"; public static final String ACTION_END_CALL = "end_call";
public static final String ACTION_UPDATE_ONGOING_CALL = "update_ongoing_call";
@Override @Override
public int onStartCommand(final Intent intent, final int flags, final int startId) { public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent == null) {
LOGGER.info("Intent was null");
return Service.START_NOT_STICKY;
}
final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
final long accountId = intent.getLongExtra(RtpSessionActivity.EXTRA_ACCOUNT, -1);
final String with = intent.getStringExtra(RtpSessionActivity.EXTRA_WITH);
if (Strings.isNullOrEmpty(sessionId) || accountId < 0 || Strings.isNullOrEmpty(with)) {
LOGGER.warn("intent was missing mandatory extras");
return Service.START_NOT_STICKY;
}
final String action = intent.getAction();
switch (Strings.nullToEmpty(action)) {
case ACTION_UPDATE_ONGOING_CALL:
updateOngoingCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
break;
case ACTION_REJECT_CALL:
rejectCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
break;
case ACTION_END_CALL:
endCall(accountId, JidCreate.fromOrThrowUnchecked(with), sessionId);
break;
default:
LOGGER.error("Service does not know how to handle {} action", action);
}
return super.onStartCommand(intent, flags, startId); return super.onStartCommand(intent, flags, startId);
} }
private void endCall(final long account, final Jid with, final String sessionId) {
final var jmc =
ConnectionPool.getInstance(this)
.getOptional(account)
.transform(xc -> xc.getManager(JingleConnectionManager.class));
if (jmc.isPresent()) {
endCall(jmc.get(), with, sessionId);
} else {
LOGGER.error("Could not end call. JingleConnectionManager not found");
}
}
private void endCall(
final JingleConnectionManager jingleConnectionManager,
final Jid with,
final String sessionId) {
final var rtpConnection = jingleConnectionManager.getJingleRtpConnection(with, sessionId);
if (rtpConnection.isPresent()) {
rtpConnection.get().endCall();
} else {
LOGGER.error("Could not end {} with {}", sessionId, with);
}
}
private void rejectCall(long account, Jid with, String sessionId) {
final var jmc =
ConnectionPool.getInstance(this)
.getOptional(account)
.transform(xc -> xc.getManager(JingleConnectionManager.class));
if (jmc.isPresent()) {
rejectCall(jmc.get(), with, sessionId);
} else {
LOGGER.error("Could not reject call. JingleConnectionManager not found");
}
}
private void rejectCall(
JingleConnectionManager jingleConnectionManager, Jid with, String sessionId) {
final var rtpConnection = jingleConnectionManager.getJingleRtpConnection(with, sessionId);
if (rtpConnection.isPresent()) {
rtpConnection.get().rejectCall();
} else {
LOGGER.error("Could not reject call {} with {}", sessionId, with);
}
}
private void updateOngoingCall(final long account, final Jid with, final String sessionId) {
final var jmc =
ConnectionPool.getInstance(this)
.getOptional(account)
.transform(xc -> xc.getManager(JingleConnectionManager.class));
if (jmc.isPresent()) {
updateOngoingCall(jmc.get(), with, sessionId);
} else {
LOGGER.error("JingleConnectionManager not found for {}", account);
}
}
private void updateOngoingCall(
final JingleConnectionManager jingleConnectionManager,
final Jid with,
final String sessionId) {
final var ongoingCall =
jingleConnectionManager
.getJingleRtpConnection(with, sessionId)
.transform(JingleRtpConnection::getOngoingCall);
if (ongoingCall.isPresent()) {
LOGGER.info("Updating notification for {}", ongoingCall.get());
ForegroundService.stop(this);
startForeground(
RtpSessionNotification.ONGOING_CALL_ID,
jingleConnectionManager
.getNotificationService()
.getOngoingCallNotification(
jingleConnectionManager.getAccount(), ongoingCall.get()));
} else {
LOGGER.error("JingleRtpConnection not found for {}", sessionId);
}
}
public static void updateOngoingCall(
final Context context, final long account, final AbstractJingleConnection.Id id) {
final var intent = new Intent(context, RtpSessionService.class);
intent.setAction(ACTION_UPDATE_ONGOING_CALL);
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, account);
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toString());
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
ContextCompat.startForegroundService(context, intent);
}
public static void stop(final Context context) {
final var intent = new Intent(context, RtpSessionService.class);
context.stopService(intent);
ForegroundService.startForegroundService(context);
}
} }

View file

@ -15,7 +15,6 @@ public class MainActivity extends BaseActivity {
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ForegroundService.start(this);
final ActivityMainBinding binding = final ActivityMainBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_main); DataBindingUtil.setContentView(this, R.layout.activity_main);
final ViewModelProvider viewModelProvider = final ViewModelProvider viewModelProvider =
@ -33,4 +32,10 @@ public class MainActivity extends BaseActivity {
}); });
Activities.setStatusAndNavigationBarColors(this, binding.getRoot()); Activities.setStatusAndNavigationBarColors(this, binding.getRoot());
} }
@Override
public void onStart() {
super.onStart();
ForegroundService.start(this);
}
} }

View file

@ -732,7 +732,7 @@ public class RtpSessionActivity extends BaseActivity
private boolean initializeActivityWithRunningRtpSession( private boolean initializeActivityWithRunningRtpSession(
final Account account, Jid with, String sessionId) { final Account account, Jid with, String sessionId) {
final WeakReference<JingleRtpConnection> reference = final WeakReference<JingleRtpConnection> reference =
requireJingleConnectionManager().findJingleRtpConnection(with, sessionId); requireJingleConnectionManager().getWeakJingleRtpConnection(with, sessionId);
if (reference == null || reference.get() == null) { if (reference == null || reference.get() == null) {
final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession = final JingleConnectionManager.TerminatedRtpSession terminatedRtpSession =
requireJingleConnectionManager().getTerminalSessionState(with, sessionId); requireJingleConnectionManager().getTerminalSessionState(with, sessionId);

View file

@ -13,7 +13,6 @@ public class SettingsActivity extends BaseActivity {
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ForegroundService.start(this);
final ActivitySettingsBinding binding = final ActivitySettingsBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_settings); DataBindingUtil.setContentView(this, R.layout.activity_settings);
setSupportActionBar(binding.materialToolbar); setSupportActionBar(binding.materialToolbar);
@ -36,4 +35,10 @@ public class SettingsActivity extends BaseActivity {
} }
}); });
} }
@Override
public void onStart() {
super.onStart();
ForegroundService.start(this);
}
} }

View file

@ -109,6 +109,10 @@ public class ConnectionPool {
reconfigurationExecutor); reconfigurationExecutor);
} }
public synchronized Optional<XmppConnection> getOptional(final long id) {
return Iterables.tryFind(this.connections, c -> id == c.getAccount().id);
}
public synchronized ListenableFuture<XmppConnection> get(final long id) { public synchronized ListenableFuture<XmppConnection> get(final long id) {
final var configured = Iterables.tryFind(this.connections, c -> id == c.getAccount().id); final var configured = Iterables.tryFind(this.connections, c -> id == c.getAccount().id);
if (configured.isPresent()) { if (configured.isPresent()) {

View file

@ -26,6 +26,7 @@ import eu.siacs.conversations.xmpp.jingle.stanzas.Reason;
import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription; import eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription;
import im.conversations.android.IDs; import im.conversations.android.IDs;
import im.conversations.android.database.model.Account; import im.conversations.android.database.model.Account;
import im.conversations.android.notification.OngoingCall;
import im.conversations.android.notification.RtpSessionNotification; import im.conversations.android.notification.RtpSessionNotification;
import im.conversations.android.xml.Element; import im.conversations.android.xml.Element;
import im.conversations.android.xml.Namespace; import im.conversations.android.xml.Namespace;
@ -522,6 +523,18 @@ public class JingleConnectionManager extends AbstractManager {
return Optional.absent(); return Optional.absent();
} }
public OngoingCall getOngoingCall(final String sessionId) {
for (final Map.Entry<AbstractJingleConnection.Id, AbstractJingleConnection> entry :
this.connections.entrySet()) {
if (entry.getValue() instanceof JingleRtpConnection
&& entry.getKey().sessionId.equals(sessionId)) {
final var jingleRtpConnection = (JingleRtpConnection) entry.getValue();
return jingleRtpConnection.getOngoingCall();
}
}
return null;
}
void finishConnection(final AbstractJingleConnection connection) { void finishConnection(final AbstractJingleConnection connection) {
this.connections.remove(connection.getId()); this.connections.remove(connection.getId());
} }
@ -646,13 +659,21 @@ public class JingleConnectionManager extends AbstractManager {
resendSessionProposals(); resendSessionProposals();
} }
public WeakReference<JingleRtpConnection> findJingleRtpConnection(Jid with, String sessionId) { public WeakReference<JingleRtpConnection> getWeakJingleRtpConnection(
Jid with, String sessionId) {
final var jingleRtpConnection = getJingleRtpConnection(with, sessionId);
return jingleRtpConnection.isPresent()
? new WeakReference<>(jingleRtpConnection.get())
: null;
}
public Optional<JingleRtpConnection> getJingleRtpConnection(Jid with, String sessionId) {
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId); final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
final AbstractJingleConnection connection = connections.get(id); final AbstractJingleConnection connection = connections.get(id);
if (connection instanceof JingleRtpConnection) { if (connection instanceof JingleRtpConnection) {
return new WeakReference<>((JingleRtpConnection) connection); return Optional.of((JingleRtpConnection) connection);
} }
return null; return Optional.absent();
} }
private void resendSessionProposals() { private void resendSessionProposals() {
@ -701,34 +722,6 @@ public class JingleConnectionManager extends AbstractManager {
} }
} }
public void rejectRtpSession(final String sessionId) {
for (final AbstractJingleConnection connection : this.connections.values()) {
if (connection.getId().sessionId.equals(sessionId)) {
if (connection instanceof JingleRtpConnection) {
try {
((JingleRtpConnection) connection).rejectCall();
return;
} catch (final IllegalStateException e) {
Log.w(
Config.LOGTAG,
"race condition on rejecting call from notification",
e);
}
}
}
}
}
public void endRtpSession(final String sessionId) {
for (final AbstractJingleConnection connection : this.connections.values()) {
if (connection.getId().sessionId.equals(sessionId)) {
if (connection instanceof JingleRtpConnection) {
((JingleRtpConnection) connection).endCall();
}
}
}
}
public void failProceed(final Jid with, final String sessionId, final String message) { public void failProceed(final Jid with, final String sessionId, final String message) {
final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId); final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(with, sessionId);
final AbstractJingleConnection existingJingleConnection = connections.get(id); final AbstractJingleConnection existingJingleConnection = connections.get(id);