在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 连接,它工作得很好。问题始于服务+多活动实施

ma​​nifest.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(对话进度条)

在 Android 中通过改造获取对象数组

在Android浏览器中通过WebView调用相机拍照/选择文件 上传到服务器

如何在android中通过手势平滑滑动? [复制]