在android中通过服务实现sip
Posted
技术标签:
【中文标题】在android中通过服务实现sip【英文标题】:Implementing sip through service in android 【发布时间】:2020-04-08 22:07:04 【问题描述】:我正在开发一个 android 应用程序,它应该接收 sip 呼叫。应该始终建立 sip 连接,所以我为此使用服务。
我遇到了一些问题:
-
sipAudioCall 仅在我直接从广播接收器启动时才有效。我需要的是
将调用发送到服务,然后使用accept\decline 函数打开一个活动,最后打开一个应该开始调用(并且可以完成)的活动-但它不起作用。
-
设置sip注册后,注册调用多次
附:我已经通过主要活动+接收器测试了一个 sip 连接,它工作得很好。问题始于服务+多活动实施
manifest.xml
<receiver android:name=".IncomingCallReceiver" android:enabled="true" />
<service android:name=".SipService" android:enabled="true" />
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature android:name="android.hardware.sip.voip" android:required="true" />
<uses-feature android:name="android.software.sip" android:required="true" />
<uses-feature android:name="android.hardware.wifi" android:required="true" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />
MainActivity - 我在这里检查服务是否没有运行并启动它 + sip 权限。
protected override void OnCreate(Bundle savedInstanceState)
///stuff
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.UseSip) != Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.RecordAudio) != Permission.Granted ||
ContextCompat.CheckSelfPermission(this, Manifest.Permission.ProcessOutgoingCalls) != Permission.Granted)
ActivityCompat.RequestPermissions(this, new[] Manifest.Permission.UseSip, Manifest.Permission.RecordAudio, Manifest.Permission.ProcessOutgoingCalls , 0);
StartSipService();
private void StartSipService()
if (!IsServiceRunning(typeof(SipService)))
StartService(new Intent(this, typeof(SipService)));
//its deprecated ,but works for own services
private bool IsServiceRunning(Type cls)
var manager = (ActivityManager)GetSystemService(Context.ActivityService);
return manager.GetRunningServices(int.MaxValue).Any(service =>
service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName));
SipService - 它应该注册 sip + 绑定调用 accept\decline
public class SipService : Service, ISipRegistrationListener
public IBinder Binder get; private set;
public SipManager SipManager;
public SipProfile SipProfile;
public IncomingCallReceiver CallReceiver;
public SipAudioCall AudioCall;
public override void OnCreate()
RegisterReceiver();
CreateSipProfile("login", "pass", "domain");
base.OnCreate();
public void RegisterReceiver()
var filter = new IntentFilter();
filter.AddAction("com.myapp.INCOMING_CALL");
CallReceiver = new IncomingCallReceiver();
RegisterReceiver(CallReceiver, filter);
public override IBinder OnBind(Intent intent)
Binder = new SipBinder(this);
return new SipBinder(this);
public void StartCall()
if (AudioCall == null) return;
AudioCall.AnswerCall(30);
AudioCall.StartAudio();
if (AudioCall.IsMuted) AudioCall.ToggleMute();
public void StopCall()
AudioCall?.Close();
public override void OnDestroy()
Binder = null;
CloseLocalSipProfile();
base.OnDestroy();
public void OnRegistering(string localProfileUri)
public void OnRegistrationDone(string localProfileUri, long expiryTime)
public void OnRegistrationFailed(string localProfileUri, SipErrorCodes errorCode, string errorMessage)
CloseLocalSipProfile();
private void CreateSipProfile(string username, string password, string domain)
if (SipManager == null)
SipManager = SipManager.NewInstance(this);
var builder = new SipProfile.Builder(username, domain);
builder.SetPassword(password);
SipProfile = builder.Build();
RegisterSipIncomСall();
private void RegisterSipIncomСall()
var intent = new Intent();
intent.SetAction("tattelecom.nateks.INCOMING_CALL");
var pendingIntent = PendingIntent.GetBroadcast(this, 0, intent,
(PendingIntentFlags)FillInFlags.Data);
SipManager?.Open(SipProfile, pendingIntent, null);
SipManager?.SetRegistrationListener(SipProfile.UriString, this);
public void CloseLocalSipProfile()
if (SipManager == null) return;
if (SipProfile != null)
SipManager.Close(SipProfile.UriString);
if (CallReceiver != null)
UnregisterReceiver(CallReceiver);
SipBinder
public class SipBinder : Binder
public SipService Service get; private set;
public SipBinder(SipService service)
Service = service;
public void StartCall()
Service.StartCall();
public void StopCall()
Service.StopCall();
SipServiceConnection
public class SipServiceConnection : Java.Lang.Object, IServiceConnection
private CallActivity _activityCall;
private AcceptanceActivity _activityAcceptance;
public bool IsConnected get; private set;
public SipBinder Binder get; private set;
public SipServiceConnection(CallActivity activity)
_activityCall= activity;
IsConnected = false;
Binder = null;
public SipServiceConnection(AcceptanceActivity activity)
_activityAcceptance= activity;
IsConnected = false;
Binder = null;
public void OnServiceConnected(ComponentName name, IBinder service)
Binder = service as SipBinder;
IsConnected = Binder != null;
public void OnServiceDisconnected(ComponentName name)
IsConnected = false;
Binder = null;
public void StartCall()
if (IsConnected) Binder?.StartCall();
public void StopCall()
if (IsConnected) Binder?.StopCall();
IncomingCallReceiver - 应该向服务发送呼叫并打开接受活动
public class IncomingCallReceiver : BroadcastReceiver
public override void OnReceive(Context context, Intent intent)
SipAudioCall incomingCall = null;
var listener = new SipAudioCall.Listener();
SipService service= (SipService)context;
incomingCall = service.SipManager.TakeAudioCall(intent, listener);
if (incomingCall.IsMuted) incomingCall.ToggleMute();
service.AudioCall = incomingCall;
var newIntent = new Intent(activity.ApplicationContext,typeof(AcceptanceActivity));
newIntent.AddFlags(ActivityFlags.NewTask);
service.StartActivity(newIntent );
AcceptanceActivity - 绑定到服务,如果用户接受呼叫 - 打开呼叫活动。如果拒绝 - service.closeCall
protected override void OnCreate(Bundle savedInstanceState)
base.OnCreate(savedInstanceState);
///stuff
if (_serviceConnection == null)
_serviceConnection = new SipServiceConnection(this);
Intent serviceToStart = new Intent(this, typeof(SipService));
BindService(serviceToStart, _serviceConnection, Bind.AutoCreate);
private void CloseOnClick(object sender, EventArgs e)
_serviceConnection?.StopCall();
Finish();
private void AnswerOnClick(object sender, EventArgs e)
var root = new Intent(this, typeof(CallActivity));
StartActivity(root);
Finish();
CallActivity - 绑定到服务,启动音频呼叫并在需要时关闭呼叫
protected override void OnCreate(Bundle savedInstanceState)
base.OnCreate(savedInstanceState);
if (_serviceConnection == null)
_serviceConnection = new SipServiceConnection(this);
Intent serviceToStart = new Intent(this, typeof(SipService));
BindService(serviceToStart, _serviceConnection, Bind.AutoCreate);
StartCall();
private void CloseOnClick(object sender, EventArgs e)
_serviceConnection?.StopCall();
private void StartCall()
_serviceConnection?.StartCall();
【问题讨论】:
我认为问题出在 CloseLocalSipProfile() 方法上。服务不应停止。要查看服务是否从 cmd.exe 运行,您可以使用 >Netstat -a 这将给出 TCP 端口的状态。在客户端连接之前,您应该看到正在侦听。然后当客户端连接时,您应该看到正在建立的连接和客户端的 IP 地址。当客户端断开连接时,服务应该仍然在监听。一个服务可以支持多个客户端,因此在客户端连接后,您应该仍然可以看到监听和连接。 你也可以使用像wireshark或fiddler这样的嗅探器。 SIP 使用类似于 html 的格式并用作传输层 TCP。当 TCP 中的连接关闭时,您应该会看到 [FIN]。 好的。我发现我应该使用推送通知在来电时唤醒应用程序。仍然无法获得活动来接听电话 我认为您正在关闭代码中的服务。正如我所说,您需要使用 Netstat 检查服务器中侦听器的状态。它会告诉 Listener 是否仍在运行。 【参考方案1】:对于1.你需要在启动服务的时候使用一个ForegroundService,而不是调用startService(),调用startForegroundService(),并在服务的onCreate中调用startForeground()
https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services
【讨论】:
【参考方案2】:解决方案是在绑定服务连接和开始调用之间增加延迟。不知道为什么,好像没有立即绑定
【讨论】:
感谢分享。不要忘记接受答案。以上是关于在android中通过服务实现sip的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 的 OKhttp 中通过 POST 请求发送 JSON 正文
android 测试 动态触发intent,Android中通过耳机按键控制音乐播放的实现
Android中通过实现线程更新ProgressDialog(对话进度条)