java - 在android中通过服务实现sip
问题描述
我正在开发一个 android 应用程序,它应该接收 sip 呼叫。应该始终建立 sip 连接,所以我为此使用服务。
我遇到了一些问题:
- sipAudioCall 仅在我直接从广播接收器启动时才有效。我需要将调用发送到服务,然后打开一个带有接受\拒绝功能的活动,最后打开一个活动,应该开始调用(并且可以完成) - 但它不起作用。
- 为注册设置 sip 后,注册调用多次
PS我已经通过主要活动+接收器测试了一个sip连接,它工作得很好。问题始于服务+多活动实施
清单.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();
}
解决方案
1.你需要在启动服务时使用一个ForegroundService,而不是调用startService(),调用startForegroundService(),并在服务的onCreate中调用startForeground()
https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services
推荐阅读
- unity3d - 无法使用 IL2CPP 构建
- php - 如何使用 OCR 读取自定义表单的图像
- python - 如何在 django 的管理页面中添加新用户?
- windows - 无法在自定义操作 ExeCommand 中展开属性 SourceDir
- php - 创建新的空白斜杠 (/) 路由时,Symfony 3.4 应用程序内的无限 HTTP 301 重定向循环
- azure-devops - 是否可以使用部署连接来移动文件或其他操作?
- node.js - Mern 堆栈应用程序在本地 heroku 上工作,但在部署时无法在线工作
- javascript - 使用 Javascript 或 Jquery 动态生成表单是否安全?
- javascript - 我可以将变量传递给ajax查询吗?
- php - 如何将php字符串转换为jquery数组?