Compare commits

...

5 commits

Author SHA1 Message Date
Peter Cai 0f581f889a do not use Dialer UI for incoming calls if audio permission is not granted
We cannot always request new permissions when Dialer UI is shown. For
example, Dialer UI can be shown over keyguard, which doesn't allow other
dialogs (like the permission request dialog) to be displayed. However it
doesn't return the failed response either, making ConnectionService kind
of stuck.

If we just do not use the Dialer UI when the permission is not granted,
it is at most a minor annoyance for the first time. After the user has
accepted an incoming call even just once, the permission will be
granted, and the Dialer integration will start to work just fine.
2022-03-11 17:46:02 -05:00
Peter Cai 9cb59cc735 ConnectionService: miscellaneous fixes
* Fix a few potential errors due to the use of newer APIs (minSDK is
  still only 24)
* Fix one remaining case of raw usage of generic types.
2022-03-11 16:57:04 -05:00
Peter Cai d3078dfd8b ConnectionService: Dialer UI integration for incoming calls 2022-03-11 15:17:06 -05:00
Peter Cai 0872f24128 ConnectionService: fix unchecked type assignments 2022-03-11 13:45:46 -05:00
Peter Cai 6ff1a2366f ConnectionService: reformat 2022-03-11 13:43:00 -05:00
3 changed files with 332 additions and 240 deletions

View file

@ -1,292 +1,328 @@
package com.cheogram.android; package com.cheogram.android;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
import com.intentfilter.androidpermissions.NotificationSettings;
import com.intentfilter.androidpermissions.PermissionManager;
import com.intentfilter.androidpermissions.models.DeniedPermissions;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.Vector;
import com.google.common.collect.ImmutableSet;
import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.Manifest;
import androidx.core.content.ContextCompat;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.ServiceConnection;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.util.Log;
import com.intentfilter.androidpermissions.PermissionManager;
import com.intentfilter.androidpermissions.NotificationSettings;
import com.intentfilter.androidpermissions.models.DeniedPermissions;
import io.michaelrocks.libphonenumber.android.NumberParseException;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.AppRTCAudioManager; import eu.siacs.conversations.services.AppRTCAudioManager;
import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.ui.RtpSessionActivity; import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper; import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media; import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState; import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import io.michaelrocks.libphonenumber.android.NumberParseException;
public class ConnectionService extends android.telecom.ConnectionService { public class ConnectionService extends android.telecom.ConnectionService {
public XmppConnectionService xmppConnectionService = null; public XmppConnectionService xmppConnectionService = null;
protected ServiceConnection mConnection = new ServiceConnection() { protected ServiceConnection mConnection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
XmppConnectionBinder binder = (XmppConnectionBinder) service; XmppConnectionBinder binder = (XmppConnectionBinder) service;
xmppConnectionService = binder.getService(); xmppConnectionService = binder.getService();
} }
@Override @Override
public void onServiceDisconnected(ComponentName arg0) { public void onServiceDisconnected(ComponentName arg0) {
xmppConnectionService = null; xmppConnectionService = null;
} }
}; };
private PermissionManager mPermissionManager;
@Override @Override
public void onCreate() { public void onCreate() {
// From XmppActivity.connectToBackend mPermissionManager = PermissionManager.getInstance(this);
Intent intent = new Intent(this, XmppConnectionService.class); mPermissionManager.setNotificationSettings(
intent.setAction("ui"); new NotificationSettings.Builder()
try { .withMessage(R.string.microphone_permission_for_call)
startService(intent); .withSmallIcon(R.drawable.ic_notification).build()
} catch (IllegalStateException e) { );
Log.w("com.cheogram.android.ConnectionService", "unable to start service from " + getClass().getSimpleName());
}
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override // From XmppActivity.connectToBackend
public void onDestroy() { Intent intent = new Intent(this, XmppConnectionService.class);
unbindService(mConnection); intent.setAction("ui");
} try {
startService(intent);
} catch (IllegalStateException e) {
Log.w("com.cheogram.android.ConnectionService", "unable to start service from " + getClass().getSimpleName());
}
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override @Override
public Connection onCreateOutgoingConnection( public void onDestroy() {
PhoneAccountHandle phoneAccountHandle, unbindService(mConnection);
ConnectionRequest request }
) {
String[] gateway = phoneAccountHandle.getId().split("/", 2);
String rawTel = request.getAddress().getSchemeSpecificPart(); @Override
String postDial = PhoneNumberUtils.extractPostDialPortion(rawTel); public Connection onCreateOutgoingConnection(
PhoneAccountHandle phoneAccountHandle,
ConnectionRequest request
) {
String[] gateway = phoneAccountHandle.getId().split("/", 2);
String tel = PhoneNumberUtils.extractNetworkPortion(rawTel); String rawTel = request.getAddress().getSchemeSpecificPart();
try { String postDial = PhoneNumberUtils.extractPostDialPortion(rawTel);
tel = PhoneNumberUtilWrapper.normalize(this, tel);
} catch (NumberParseException e) {
return Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR)
);
}
if (xmppConnectionService.getJingleConnectionManager().isBusy() != null) { String tel = PhoneNumberUtils.extractNetworkPortion(rawTel);
return Connection.createFailedConnection( try {
new DisconnectCause(DisconnectCause.BUSY) tel = PhoneNumberUtilWrapper.normalize(this, tel);
); } catch (NumberParseException e) {
} return Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR)
);
}
Account account = xmppConnectionService.findAccountByJid(Jid.of(gateway[0])); if (xmppConnectionService.getJingleConnectionManager().isBusy() != null) {
Jid with = Jid.ofLocalAndDomain(tel, gateway[1]); return Connection.createFailedConnection(
CheogramConnection connection = new CheogramConnection(account, with, postDial); new DisconnectCause(DisconnectCause.BUSY)
);
}
PermissionManager permissionManager = PermissionManager.getInstance(this); Account account = xmppConnectionService.findAccountByJid(Jid.of(gateway[0]));
permissionManager.setNotificationSettings( Jid with = Jid.ofLocalAndDomain(tel, gateway[1]);
new NotificationSettings.Builder() CheogramConnection connection = new CheogramConnection(account, with, postDial);
.withMessage(R.string.microphone_permission_for_call)
.withSmallIcon(R.drawable.ic_notification).build()
);
Set<String> permissions = new HashSet(); Set<String> permissions = new HashSet<>();
permissions.add(Manifest.permission.RECORD_AUDIO); permissions.add(Manifest.permission.RECORD_AUDIO);
permissionManager.checkPermissions(permissions, new PermissionManager.PermissionRequestListener() { mPermissionManager.checkPermissions(permissions, new PermissionManager.PermissionRequestListener() {
@Override @Override
public void onPermissionGranted() { public void onPermissionGranted() {
connection.setSessionId(xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession( connection.setSessionId(xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(
account, account,
with, with,
ImmutableSet.of(Media.AUDIO) ImmutableSet.of(Media.AUDIO)
)); ));
} }
@Override @Override
public void onPermissionDenied(DeniedPermissions deniedPermissions) { public void onPermissionDenied(DeniedPermissions deniedPermissions) {
connection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); connection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
} }
}); });
connection.setInitializing(); connection.setInitializing();
connection.setAddress( connection.setAddress(
Uri.fromParts("tel", tel, null), // Normalized tel as tel: URI Uri.fromParts("tel", tel, null), // Normalized tel as tel: URI
TelecomManager.PRESENTATION_ALLOWED TelecomManager.PRESENTATION_ALLOWED
); );
xmppConnectionService.setOnRtpConnectionUpdateListener( xmppConnectionService.setOnRtpConnectionUpdateListener(
(XmppConnectionService.OnJingleRtpConnectionUpdate) connection (XmppConnectionService.OnJingleRtpConnectionUpdate) connection
); );
return connection; return connection;
} }
public class CheogramConnection extends Connection implements XmppConnectionService.OnJingleRtpConnectionUpdate { @Override
protected Account account; public Connection onCreateIncomingConnection(PhoneAccountHandle handle, ConnectionRequest request) {
protected Jid with; Bundle extras = request.getExtras();
protected String sessionId = null; String accountJid = extras.getString("account");
protected Stack<String> postDial = new Stack(); String withJid = extras.getString("with");
protected Icon gatewayIcon; String sessionId = extras.getString("sessionId");
protected WeakReference<JingleRtpConnection> rtpConnection = null;
CheogramConnection(Account account, Jid with, String postDialString) { Account account = xmppConnectionService.findAccountByJid(Jid.of(accountJid));
super(); Jid with = Jid.of(withJid);
this.account = account;
this.with = with;
gatewayIcon = Icon.createWithBitmap(xmppConnectionService.getAvatarService().get( CheogramConnection connection = new CheogramConnection(account, with, null);
account.getRoster().getContact(Jid.of(with.getDomain())), connection.setSessionId(sessionId);
AvatarService.getSystemUiAvatarSize(xmppConnectionService), connection.setAddress(
false Uri.fromParts("tel", with.getLocal(), null),
)); TelecomManager.PRESENTATION_ALLOWED
);
connection.setRinging();
if (postDialString != null) { xmppConnectionService.setOnRtpConnectionUpdateListener(connection);
for (int i = postDialString.length() - 1; i >= 0; i--) {
postDial.push("" + postDialString.charAt(i));
}
}
setCallerDisplayName( return connection;
account.getDisplayName(), }
TelecomManager.PRESENTATION_ALLOWED
);
setAudioModeIsVoip(true);
setConnectionCapabilities(
Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION
);
}
public void setSessionId(final String sessionId) { public class CheogramConnection extends Connection implements XmppConnectionService.OnJingleRtpConnectionUpdate {
this.sessionId = sessionId; protected Account account;
} protected Jid with;
protected String sessionId = null;
protected Stack<String> postDial = new Stack<>();
protected Icon gatewayIcon;
protected WeakReference<JingleRtpConnection> rtpConnection = null;
@Override CheogramConnection(Account account, Jid with, String postDialString) {
public void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) { super();
if (sessionId == null || !sessionId.equals(this.sessionId)) return; this.account = account;
if (rtpConnection == null) { this.with = with;
this.with = with; // Store full JID of connection
rtpConnection = xmppConnectionService.getJingleConnectionManager().findJingleRtpConnection(account, with, sessionId);
}
setStatusHints(new StatusHints(null, gatewayIcon, null)); gatewayIcon = Icon.createWithBitmap(xmppConnectionService.getAvatarService().get(
account.getRoster().getContact(Jid.of(with.getDomain())),
AvatarService.getSystemUiAvatarSize(xmppConnectionService),
false
));
if (state == RtpEndUserState.FINDING_DEVICE) { if (postDialString != null) {
setInitialized(); for (int i = postDialString.length() - 1; i >= 0; i--) {
} else if (state == RtpEndUserState.RINGING) { postDial.push("" + postDialString.charAt(i));
setDialing(); }
} else if (state == RtpEndUserState.CONNECTED) { }
xmppConnectionService.setDiallerIntegrationActive(true);
setActive();
postDial(); setCallerDisplayName(
} else if (state == RtpEndUserState.DECLINED_OR_BUSY) { account.getDisplayName(),
setDisconnected(new DisconnectCause(DisconnectCause.BUSY)); TelecomManager.PRESENTATION_ALLOWED
} else if (state == RtpEndUserState.ENDED) { );
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); setAudioModeIsVoip(true);
} else if (state == RtpEndUserState.RETRACTED) { setConnectionCapabilities(
setDisconnected(new DisconnectCause(DisconnectCause.CANCELED)); Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION
} else if (RtpSessionActivity.END_CARD.contains(state)) { );
setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); }
}
}
@Override public void setSessionId(final String sessionId) {
public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) { this.sessionId = sessionId;
switch(selectedAudioDevice) { }
case SPEAKER_PHONE:
setAudioRoute(CallAudioState.ROUTE_SPEAKER);
case WIRED_HEADSET:
setAudioRoute(CallAudioState.ROUTE_WIRED_HEADSET);
case EARPIECE:
setAudioRoute(CallAudioState.ROUTE_EARPIECE);
case BLUETOOTH:
setAudioRoute(CallAudioState.ROUTE_BLUETOOTH);
default:
setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
}
}
@Override @Override
public void onDisconnect() { public void onJingleRtpConnectionUpdate(final Account account, final Jid with, final String sessionId, final RtpEndUserState state) {
if (rtpConnection == null || rtpConnection.get() == null) { if (sessionId == null || !sessionId.equals(this.sessionId)) return;
xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid()); if (rtpConnection == null) {
} else { this.with = with; // Store full JID of connection
rtpConnection.get().endCall(); rtpConnection = xmppConnectionService.getJingleConnectionManager().findJingleRtpConnection(account, with, sessionId);
} }
destroy();
xmppConnectionService.setDiallerIntegrationActive(false);
xmppConnectionService.removeRtpConnectionUpdateListener(
(XmppConnectionService.OnJingleRtpConnectionUpdate) this
);
}
@Override setStatusHints(new StatusHints(null, gatewayIcon, null));
public void onAbort() {
onDisconnect();
}
@Override if (state == RtpEndUserState.FINDING_DEVICE) {
public void onPlayDtmfTone(char c) { setInitialized();
rtpConnection.get().applyDtmfTone("" + c); } else if (state == RtpEndUserState.RINGING) {
} setDialing();
} else if (state == RtpEndUserState.INCOMING_CALL) {
setRinging();
} else if (state == RtpEndUserState.CONNECTED) {
xmppConnectionService.setDiallerIntegrationActive(true);
setActive();
@Override postDial();
public void onPostDialContinue(boolean c) { } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
if (c) postDial(); setDisconnected(new DisconnectCause(DisconnectCause.BUSY));
} } else if (state == RtpEndUserState.ENDED) {
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
} else if (state == RtpEndUserState.RETRACTED) {
setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
} else if (RtpSessionActivity.END_CARD.contains(state)) {
setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
}
}
protected void sleep(int ms) { @Override
try { public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
Thread.sleep(ms); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) return;
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
protected void postDial() { switch (selectedAudioDevice) {
while (!postDial.empty()) { case SPEAKER_PHONE:
String next = postDial.pop(); setAudioRoute(CallAudioState.ROUTE_SPEAKER);
if (next.equals(";")) { case WIRED_HEADSET:
Stack v = (Stack) postDial.clone(); setAudioRoute(CallAudioState.ROUTE_WIRED_HEADSET);
Collections.reverse(v); case EARPIECE:
setPostDialWait(String.join("", v)); setAudioRoute(CallAudioState.ROUTE_EARPIECE);
return; case BLUETOOTH:
} else if (next.equals(",")) { setAudioRoute(CallAudioState.ROUTE_BLUETOOTH);
sleep(2000); default:
} else { setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
rtpConnection.get().applyDtmfTone(next); }
sleep(100); }
}
} @Override
} public void onAnswer() {
} // For incoming calls, a connection update may not have been triggered before answering
// so we have to acquire the rtp connection object here
this.rtpConnection =
xmppConnectionService.getJingleConnectionManager()
.findJingleRtpConnection(account, with, sessionId);
rtpConnection.get().acceptCall();
}
@Override
public void onDisconnect() {
if (rtpConnection == null || rtpConnection.get() == null) {
xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
} else {
rtpConnection.get().endCall();
}
destroy();
xmppConnectionService.setDiallerIntegrationActive(false);
xmppConnectionService.removeRtpConnectionUpdateListener(
(XmppConnectionService.OnJingleRtpConnectionUpdate) this
);
}
@Override
public void onAbort() {
onDisconnect();
}
@Override
public void onPlayDtmfTone(char c) {
rtpConnection.get().applyDtmfTone("" + c);
}
@Override
public void onPostDialContinue(boolean c) {
if (c) postDial();
}
protected void sleep(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
protected void postDial() {
while (!postDial.empty()) {
String next = postDial.pop();
if (next.equals(";")) {
Vector<String> v = new Vector<>(postDial);
Collections.reverse(v);
setPostDialWait(Joiner.on("").skipNulls().join(v));
return;
} else if (next.equals(",")) {
sleep(2000);
} else {
rtpConnection.get().applyDtmfTone(next);
sleep(100);
}
}
}
}
} }

View file

@ -599,7 +599,7 @@ public class Contact implements ListItem, Blockable {
"/" + getJid().asBareJid().toString(); "/" + getJid().asBareJid().toString();
} }
protected PhoneAccountHandle phoneAccountHandle() { public PhoneAccountHandle phoneAccountHandle() {
ComponentName componentName = new ComponentName( ComponentName componentName = new ComponentName(
"com.cheogram.android", "com.cheogram.android",
"com.cheogram.android.ConnectionService" "com.cheogram.android.ConnectionService"

View file

@ -1,5 +1,6 @@
package eu.siacs.conversations.services; package eu.siacs.conversations.services;
import android.Manifest;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationChannelGroup; import android.app.NotificationChannelGroup;
@ -8,6 +9,7 @@ import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Typeface; import android.graphics.Typeface;
@ -16,9 +18,12 @@ import android.media.Ringtone;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.Vibrator; import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@ -423,7 +428,58 @@ public class NotificationService {
notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification); notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification);
} }
private synchronized boolean tryRingingWithDialerUI(final AbstractJingleConnection.Id id, final Set<Media> media) {
if (mXmppConnectionService.checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// We cannot always request audio permission in Dialer UI
// e.g. when Dialer is shown over keyguard
return false;
}
if (media.size() != 1 || !media.contains(Media.AUDIO)) {
// Currently our ConnectionService only handles single audio calls
Log.w(Config.LOGTAG, "only audio calls can be handled by cheogram connection service");
return false;
}
PhoneAccountHandle handle = null;
for (Contact contact : id.account.getRoster().getContacts()) {
if (!contact.getJid().getDomain().equals(id.with.getDomain()))
continue;
if (!contact.getPresences().anyIdentity("gateway", "pstn"))
continue;
handle = contact.phoneAccountHandle();
break;
}
if (handle == null) {
Log.w(Config.LOGTAG, "Could not find phone account handle for " + id.account.getJid().toString());
return false;
}
Bundle callInfo = new Bundle();
callInfo.putString("account", id.account.getJid().toString());
callInfo.putString("with", id.with.toString());
callInfo.putString("sessionId", id.sessionId);
TelecomManager telecomManager = mXmppConnectionService.getSystemService(TelecomManager.class);
try {
telecomManager.addNewIncomingCall(handle, callInfo);
} catch (SecurityException e) {
// There *could* be race conditions where the account is not registered yet
// when an incoming call is already received
Log.w(Config.LOGTAG, e);
return false;
}
return true;
}
public synchronized void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) { public synchronized void startRinging(final AbstractJingleConnection.Id id, final Set<Media> media) {
if (tryRingingWithDialerUI(id, media)) {
return;
}
showIncomingCallNotification(id, media); showIncomingCallNotification(id, media);
final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE); final NotificationManager notificationManager = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
final int currentInterruptionFilter; final int currentInterruptionFilter;